1 |
commit: 4247e10d9c266ac1f6aac48b0c67f1092dde1d78 |
2 |
Author: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri Dec 30 19:27:23 2022 +0000 |
4 |
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org> |
5 |
CommitDate: Mon Jan 2 20:11:43 2023 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/pkgcore/pkgcheck.git/commit/?id=4247e10d |
7 |
|
8 |
ProvidedEclassInherit: new check for inheriting provided eclases |
9 |
|
10 |
Resolves: https://github.com/pkgcore/pkgcheck/issues/504 |
11 |
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org> |
12 |
|
13 |
src/pkgcheck/checks/eclass.py | 103 ++++++++++++++------- |
14 |
.../ProvidedEclassInherit/expected.json | 1 + |
15 |
.../ProvidedEclassInherit/fix.patch | 10 ++ |
16 |
.../ProvidedEclassInherit-0.ebuild | 11 +++ |
17 |
4 files changed, 93 insertions(+), 32 deletions(-) |
18 |
|
19 |
diff --git a/src/pkgcheck/checks/eclass.py b/src/pkgcheck/checks/eclass.py |
20 |
index 5c4f205f..862dbb91 100644 |
21 |
--- a/src/pkgcheck/checks/eclass.py |
22 |
+++ b/src/pkgcheck/checks/eclass.py |
23 |
@@ -98,18 +98,36 @@ class MisplacedEclassVar(results.LineResult, results.Error): |
24 |
return f"invalid pre-inherit placement, line {self.lineno}: {self.line!r}" |
25 |
|
26 |
|
27 |
+class ProvidedEclassInherit(results.LineResult, results.Style): |
28 |
+ """Ebuild inherits an eclass which is already provided by another eclass. |
29 |
+ |
30 |
+ When inheriting an eclass which declares ``@PROVIDES``, those referenced |
31 |
+ eclasses are guaranteed to be provided by the eclass. Therefore, inheriting |
32 |
+ them in ebuilds is redundant and should be removed. |
33 |
+ """ |
34 |
+ |
35 |
+ def __init__(self, provider, **kwargs): |
36 |
+ super().__init__(**kwargs) |
37 |
+ self.provider = provider |
38 |
+ |
39 |
+ @property |
40 |
+ def desc(self): |
41 |
+ return f"line {self.lineno}: redundant eclass inherit {self.line!r}, provided by {self.provider!r}" |
42 |
+ |
43 |
+ |
44 |
class EclassUsageCheck(Check): |
45 |
"""Scan packages for various eclass-related issues.""" |
46 |
|
47 |
_source = sources.EbuildParseRepoSource |
48 |
known_results = frozenset( |
49 |
- [ |
50 |
+ { |
51 |
DeprecatedEclass, |
52 |
DeprecatedEclassVariable, |
53 |
DeprecatedEclassFunction, |
54 |
DuplicateEclassInherit, |
55 |
MisplacedEclassVar, |
56 |
- ] |
57 |
+ ProvidedEclassInherit, |
58 |
+ } |
59 |
) |
60 |
required_addons = (addons.eclass.EclassAddon,) |
61 |
|
62 |
@@ -118,15 +136,16 @@ class EclassUsageCheck(Check): |
63 |
self.deprecated_eclasses = eclass_addon.deprecated |
64 |
self.eclass_cache = eclass_addon.eclasses |
65 |
|
66 |
- def check_pre_inherits(self, pkg, inherits): |
67 |
+ def check_pre_inherits(self, pkg, inherits: list[tuple[list[str], int]]): |
68 |
"""Check for invalid @PRE_INHERIT variable placement.""" |
69 |
- pre_inherits = {} |
70 |
# determine if any inherited eclasses have @PRE_INHERIT variables |
71 |
- for eclasses, lineno in inherits: |
72 |
- for eclass in eclasses: |
73 |
- for var in self.eclass_cache[eclass].variables: |
74 |
- if var.pre_inherit: |
75 |
- pre_inherits[var.name] = lineno |
76 |
+ pre_inherits = { |
77 |
+ var.name: lineno |
78 |
+ for eclasses, lineno in inherits |
79 |
+ for eclass in eclasses |
80 |
+ for var in self.eclass_cache[eclass].variables |
81 |
+ if var.pre_inherit |
82 |
+ } |
83 |
|
84 |
# scan for any misplaced @PRE_INHERIT variables |
85 |
if pre_inherits: |
86 |
@@ -137,22 +156,23 @@ class EclassUsageCheck(Check): |
87 |
line = pkg.node_str(node) |
88 |
yield MisplacedEclassVar(var_name, line=line, lineno=lineno + 1, pkg=pkg) |
89 |
|
90 |
- def check_deprecated_variables(self, pkg, inherits): |
91 |
- """Check for usage of @DEPRECATED variables or functions.""" |
92 |
- deprecated = {} |
93 |
+ def check_deprecated_variables(self, pkg, inherits: list[tuple[list[str], int]]): |
94 |
+ """Check for usage of @DEPRECATED variables.""" |
95 |
# determine if any inherited eclasses have @DEPRECATED variables |
96 |
- for eclasses, _ in inherits: |
97 |
- for eclass in eclasses: |
98 |
- for var in self.eclass_cache[eclass].variables: |
99 |
- if var.deprecated: |
100 |
- deprecated[var.name] = var.deprecated |
101 |
+ deprecated = { |
102 |
+ var.name: var.deprecated |
103 |
+ for eclasses, _ in inherits |
104 |
+ for eclass in eclasses |
105 |
+ for var in self.eclass_cache[eclass].variables |
106 |
+ if var.deprecated |
107 |
+ } |
108 |
|
109 |
# scan for usage of @DEPRECATED variables |
110 |
if deprecated: |
111 |
for node, _ in bash.var_query.captures(pkg.tree.root_node): |
112 |
var_name = pkg.node_str(node) |
113 |
- lineno, _colno = node.start_point |
114 |
if var_name in deprecated: |
115 |
+ lineno, _colno = node.start_point |
116 |
line = pkg.node_str(node) |
117 |
replacement = deprecated[var_name] |
118 |
if not isinstance(replacement, str): |
119 |
@@ -161,22 +181,23 @@ class EclassUsageCheck(Check): |
120 |
var_name, replacement, line=line, lineno=lineno + 1, pkg=pkg |
121 |
) |
122 |
|
123 |
- def check_deprecated_functions(self, pkg, inherits): |
124 |
- """Check for usage of @DEPRECATED variables or functions.""" |
125 |
- deprecated = {} |
126 |
- # determine if any inherited eclasses have @DEPRECATED variables or functions |
127 |
- for eclasses, _ in inherits: |
128 |
- for eclass in eclasses: |
129 |
- for func in self.eclass_cache[eclass].functions: |
130 |
- if func.deprecated: |
131 |
- deprecated[func.name] = func.deprecated |
132 |
+ def check_deprecated_functions(self, pkg, inherits: list[tuple[list[str], int]]): |
133 |
+ """Check for usage of @DEPRECATED functions.""" |
134 |
+ # determine if any inherited eclasses have @DEPRECATED functions |
135 |
+ deprecated = { |
136 |
+ func.name: func.deprecated |
137 |
+ for eclasses, _ in inherits |
138 |
+ for eclass in eclasses |
139 |
+ for func in self.eclass_cache[eclass].functions |
140 |
+ if func.deprecated |
141 |
+ } |
142 |
|
143 |
# scan for usage of @DEPRECATED functions |
144 |
if deprecated: |
145 |
for node, _ in bash.cmd_query.captures(pkg.tree.root_node): |
146 |
func_name = pkg.node_str(node.child_by_field_name("name")) |
147 |
- lineno, _colno = node.start_point |
148 |
if func_name in deprecated: |
149 |
+ lineno, _colno = node.start_point |
150 |
line = pkg.node_str(node) |
151 |
replacement = deprecated[func_name] |
152 |
if not isinstance(replacement, str): |
153 |
@@ -185,10 +206,22 @@ class EclassUsageCheck(Check): |
154 |
func_name, replacement, line=line, lineno=lineno + 1, pkg=pkg |
155 |
) |
156 |
|
157 |
+ def check_provided_eclasses(self, pkg, inherits: list[tuple[list[str], int]]): |
158 |
+ """Check for usage of eclasses (i.e. redundant inherits) that are |
159 |
+ provided by another inherited eclass.""" |
160 |
+ provided_eclasses = { |
161 |
+ provided: (eclass, lineno + 1) |
162 |
+ for eclasses, lineno in inherits |
163 |
+ for eclass in eclasses |
164 |
+ for provided in pkg.inherit.intersection(self.eclass_cache[eclass].provides) |
165 |
+ } |
166 |
+ for provided, (eclass, lineno) in provided_eclasses.items(): |
167 |
+ yield ProvidedEclassInherit(eclass, pkg=pkg, line=provided, lineno=lineno) |
168 |
+ |
169 |
def feed(self, pkg): |
170 |
if pkg.inherit: |
171 |
inherited = set() |
172 |
- inherits = [] |
173 |
+ inherits: list[tuple[list[str], int]] = [] |
174 |
for node, _ in bash.cmd_query.captures(pkg.tree.root_node): |
175 |
name = pkg.node_str(node.child_by_field_name("name")) |
176 |
if name == "inherit": |
177 |
@@ -207,6 +240,7 @@ class EclassUsageCheck(Check): |
178 |
eclass, line=call, lineno=lineno + 1, pkg=pkg |
179 |
) |
180 |
|
181 |
+ yield from self.check_provided_eclasses(pkg, inherits) |
182 |
# verify @PRE_INHERIT variable placement |
183 |
yield from self.check_pre_inherits(pkg, inherits) |
184 |
# verify @DEPRECATED variables or functions |
185 |
@@ -281,7 +315,7 @@ class EclassParseCheck(Check): |
186 |
for var_node, _ in bash.var_query.captures(func_node): |
187 |
var_name = eclass.node_str(var_node) |
188 |
if var_name in variables: |
189 |
- lineno, colno = var_node.start_point |
190 |
+ lineno, _colno = var_node.start_point |
191 |
usage[var_name].add(lineno + 1) |
192 |
for var, lines in sorted(usage.items()): |
193 |
yield EclassVariableScope( |
194 |
@@ -369,7 +403,12 @@ class EclassCheck(Check): |
195 |
|
196 |
_source = sources.EclassRepoSource |
197 |
known_results = frozenset( |
198 |
- [EclassBashSyntaxError, EclassDocError, EclassDocMissingFunc, EclassDocMissingVar] |
199 |
+ [ |
200 |
+ EclassBashSyntaxError, |
201 |
+ EclassDocError, |
202 |
+ EclassDocMissingFunc, |
203 |
+ EclassDocMissingVar, |
204 |
+ ] |
205 |
) |
206 |
|
207 |
def __init__(self, *args): |
208 |
@@ -393,7 +432,7 @@ class EclassCheck(Check): |
209 |
lineno = 0 |
210 |
error = [] |
211 |
for line in p.stderr.splitlines(): |
212 |
- path, line, msg = line.split(": ", 2) |
213 |
+ _path, line, msg = line.split(": ", 2) |
214 |
lineno = line[5:] |
215 |
error.append(msg.strip("\n")) |
216 |
error = ": ".join(error) |
217 |
|
218 |
diff --git a/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/expected.json b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/expected.json |
219 |
new file mode 100644 |
220 |
index 00000000..397c0644 |
221 |
--- /dev/null |
222 |
+++ b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/expected.json |
223 |
@@ -0,0 +1 @@ |
224 |
+{"__class__": "ProvidedEclassInherit", "category": "EclassUsageCheck", "package": "ProvidedEclassInherit", "version": "0", "line": "inherit", "lineno": 2, "provider": "deep-provided-inherit"} |
225 |
|
226 |
diff --git a/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/fix.patch b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/fix.patch |
227 |
new file mode 100644 |
228 |
index 00000000..607e8caf |
229 |
--- /dev/null |
230 |
+++ b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/fix.patch |
231 |
@@ -0,0 +1,10 @@ |
232 |
+diff -Naur eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild fixed/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild |
233 |
+--- eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild 2021-05-23 20:23:16.423009026 -0600 |
234 |
++++ fixed/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild 2021-05-23 20:23:43.734588313 -0600 |
235 |
+@@ -1,5 +1,5 @@ |
236 |
+ EAPI=7 |
237 |
+-inherit inherit deep-provided-inherit |
238 |
++inherit deep-provided-inherit |
239 |
+ DESCRIPTION="Ebuild inheriting provided eclass" |
240 |
+ HOMEPAGE="https://github.com/pkgcore/pkgcheck" |
241 |
+ SLOT="0" |
242 |
|
243 |
diff --git a/testdata/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild b/testdata/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild |
244 |
new file mode 100644 |
245 |
index 00000000..cd8585ea |
246 |
--- /dev/null |
247 |
+++ b/testdata/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild |
248 |
@@ -0,0 +1,11 @@ |
249 |
+EAPI=7 |
250 |
+inherit inherit deep-provided-inherit |
251 |
+DESCRIPTION="Ebuild inheriting provided eclass" |
252 |
+HOMEPAGE="https://github.com/pkgcore/pkgcheck" |
253 |
+SLOT="0" |
254 |
+LICENSE="BSD" |
255 |
+ |
256 |
+src_prepare() { |
257 |
+ inherit_public_func |
258 |
+ deep-provided-inherit_public_func |
259 |
+} |