diff options
4 files changed, 93 insertions, 32 deletions
diff --git a/src/pkgcheck/checks/eclass.py b/src/pkgcheck/checks/eclass.py index 5c4f205f..862dbb91 100644 --- a/src/pkgcheck/checks/eclass.py +++ b/src/pkgcheck/checks/eclass.py @@ -98,18 +98,36 @@ class MisplacedEclassVar(results.LineResult, results.Error): return f"invalid pre-inherit placement, line {self.lineno}: {self.line!r}" +class ProvidedEclassInherit(results.LineResult, results.Style): + """Ebuild inherits an eclass which is already provided by another eclass. + + When inheriting an eclass which declares ``@PROVIDES``, those referenced + eclasses are guaranteed to be provided by the eclass. Therefore, inheriting + them in ebuilds is redundant and should be removed. + """ + + def __init__(self, provider, **kwargs): + super().__init__(**kwargs) + self.provider = provider + + @property + def desc(self): + return f"line {self.lineno}: redundant eclass inherit {self.line!r}, provided by {self.provider!r}" + + class EclassUsageCheck(Check): """Scan packages for various eclass-related issues.""" _source = sources.EbuildParseRepoSource known_results = frozenset( - [ + { DeprecatedEclass, DeprecatedEclassVariable, DeprecatedEclassFunction, DuplicateEclassInherit, MisplacedEclassVar, - ] + ProvidedEclassInherit, + } ) required_addons = (addons.eclass.EclassAddon,) @@ -118,15 +136,16 @@ class EclassUsageCheck(Check): self.deprecated_eclasses = eclass_addon.deprecated self.eclass_cache = eclass_addon.eclasses - def check_pre_inherits(self, pkg, inherits): + def check_pre_inherits(self, pkg, inherits: list[tuple[list[str], int]]): """Check for invalid @PRE_INHERIT variable placement.""" - pre_inherits = {} # determine if any inherited eclasses have @PRE_INHERIT variables - for eclasses, lineno in inherits: - for eclass in eclasses: - for var in self.eclass_cache[eclass].variables: - if var.pre_inherit: - pre_inherits[var.name] = lineno + pre_inherits = { + var.name: lineno + for eclasses, lineno in inherits + for eclass in eclasses + for var in self.eclass_cache[eclass].variables + if var.pre_inherit + } # scan for any misplaced @PRE_INHERIT variables if pre_inherits: @@ -137,22 +156,23 @@ class EclassUsageCheck(Check): line = pkg.node_str(node) yield MisplacedEclassVar(var_name, line=line, lineno=lineno + 1, pkg=pkg) - def check_deprecated_variables(self, pkg, inherits): - """Check for usage of @DEPRECATED variables or functions.""" - deprecated = {} + def check_deprecated_variables(self, pkg, inherits: list[tuple[list[str], int]]): + """Check for usage of @DEPRECATED variables.""" # determine if any inherited eclasses have @DEPRECATED variables - for eclasses, _ in inherits: - for eclass in eclasses: - for var in self.eclass_cache[eclass].variables: - if var.deprecated: - deprecated[var.name] = var.deprecated + deprecated = { + var.name: var.deprecated + for eclasses, _ in inherits + for eclass in eclasses + for var in self.eclass_cache[eclass].variables + if var.deprecated + } # scan for usage of @DEPRECATED variables if deprecated: for node, _ in bash.var_query.captures(pkg.tree.root_node): var_name = pkg.node_str(node) - lineno, _colno = node.start_point if var_name in deprecated: + lineno, _colno = node.start_point line = pkg.node_str(node) replacement = deprecated[var_name] if not isinstance(replacement, str): @@ -161,22 +181,23 @@ class EclassUsageCheck(Check): var_name, replacement, line=line, lineno=lineno + 1, pkg=pkg ) - def check_deprecated_functions(self, pkg, inherits): - """Check for usage of @DEPRECATED variables or functions.""" - deprecated = {} - # determine if any inherited eclasses have @DEPRECATED variables or functions - for eclasses, _ in inherits: - for eclass in eclasses: - for func in self.eclass_cache[eclass].functions: - if func.deprecated: - deprecated[func.name] = func.deprecated + def check_deprecated_functions(self, pkg, inherits: list[tuple[list[str], int]]): + """Check for usage of @DEPRECATED functions.""" + # determine if any inherited eclasses have @DEPRECATED functions + deprecated = { + func.name: func.deprecated + for eclasses, _ in inherits + for eclass in eclasses + for func in self.eclass_cache[eclass].functions + if func.deprecated + } # scan for usage of @DEPRECATED functions if deprecated: for node, _ in bash.cmd_query.captures(pkg.tree.root_node): func_name = pkg.node_str(node.child_by_field_name("name")) - lineno, _colno = node.start_point if func_name in deprecated: + lineno, _colno = node.start_point line = pkg.node_str(node) replacement = deprecated[func_name] if not isinstance(replacement, str): @@ -185,10 +206,22 @@ class EclassUsageCheck(Check): func_name, replacement, line=line, lineno=lineno + 1, pkg=pkg ) + def check_provided_eclasses(self, pkg, inherits: list[tuple[list[str], int]]): + """Check for usage of eclasses (i.e. redundant inherits) that are + provided by another inherited eclass.""" + provided_eclasses = { + provided: (eclass, lineno + 1) + for eclasses, lineno in inherits + for eclass in eclasses + for provided in pkg.inherit.intersection(self.eclass_cache[eclass].provides) + } + for provided, (eclass, lineno) in provided_eclasses.items(): + yield ProvidedEclassInherit(eclass, pkg=pkg, line=provided, lineno=lineno) + def feed(self, pkg): if pkg.inherit: inherited = set() - inherits = [] + inherits: list[tuple[list[str], int]] = [] for node, _ in bash.cmd_query.captures(pkg.tree.root_node): name = pkg.node_str(node.child_by_field_name("name")) if name == "inherit": @@ -207,6 +240,7 @@ class EclassUsageCheck(Check): eclass, line=call, lineno=lineno + 1, pkg=pkg ) + yield from self.check_provided_eclasses(pkg, inherits) # verify @PRE_INHERIT variable placement yield from self.check_pre_inherits(pkg, inherits) # verify @DEPRECATED variables or functions @@ -281,7 +315,7 @@ class EclassParseCheck(Check): for var_node, _ in bash.var_query.captures(func_node): var_name = eclass.node_str(var_node) if var_name in variables: - lineno, colno = var_node.start_point + lineno, _colno = var_node.start_point usage[var_name].add(lineno + 1) for var, lines in sorted(usage.items()): yield EclassVariableScope( @@ -369,7 +403,12 @@ class EclassCheck(Check): _source = sources.EclassRepoSource known_results = frozenset( - [EclassBashSyntaxError, EclassDocError, EclassDocMissingFunc, EclassDocMissingVar] + [ + EclassBashSyntaxError, + EclassDocError, + EclassDocMissingFunc, + EclassDocMissingVar, + ] ) def __init__(self, *args): @@ -393,7 +432,7 @@ class EclassCheck(Check): lineno = 0 error = [] for line in p.stderr.splitlines(): - path, line, msg = line.split(": ", 2) + _path, line, msg = line.split(": ", 2) lineno = line[5:] error.append(msg.strip("\n")) error = ": ".join(error) diff --git a/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/expected.json b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/expected.json new file mode 100644 index 00000000..397c0644 --- /dev/null +++ b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/expected.json @@ -0,0 +1 @@ +{"__class__": "ProvidedEclassInherit", "category": "EclassUsageCheck", "package": "ProvidedEclassInherit", "version": "0", "line": "inherit", "lineno": 2, "provider": "deep-provided-inherit"} diff --git a/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/fix.patch b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/fix.patch new file mode 100644 index 00000000..607e8caf --- /dev/null +++ b/testdata/data/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/fix.patch @@ -0,0 +1,10 @@ +diff -Naur eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild fixed/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild +--- eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild 2021-05-23 20:23:16.423009026 -0600 ++++ fixed/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild 2021-05-23 20:23:43.734588313 -0600 +@@ -1,5 +1,5 @@ + EAPI=7 +-inherit inherit deep-provided-inherit ++inherit deep-provided-inherit + DESCRIPTION="Ebuild inheriting provided eclass" + HOMEPAGE="https://github.com/pkgcore/pkgcheck" + SLOT="0" diff --git a/testdata/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild b/testdata/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild new file mode 100644 index 00000000..cd8585ea --- /dev/null +++ b/testdata/repos/eclass/EclassUsageCheck/ProvidedEclassInherit/ProvidedEclassInherit-0.ebuild @@ -0,0 +1,11 @@ +EAPI=7 +inherit inherit deep-provided-inherit +DESCRIPTION="Ebuild inheriting provided eclass" +HOMEPAGE="https://github.com/pkgcore/pkgcheck" +SLOT="0" +LICENSE="BSD" + +src_prepare() { + inherit_public_func + deep-provided-inherit_public_func +} |