1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
import re
from pkgcore.ebuild.eapi import EAPI
from .. import addons, bash, results, sources
from . import Check
class _ReservedNameCheck(Check):
reserved_prefixes = ("__", "abort", "dyn", "prep")
reserved_substrings = ("hook", "paludis", "portage") # 'ebuild' is special case
reserved_ebuild_regex = re.compile(r"(.*[^a-zA-Z])?ebuild.*")
"""Portage variables whose use is half-legitimate and harmless if the package manager doesn't support them."""
special_whitelist = (
"EBUILD_DEATH_HOOKS",
"EBUILD_SUCCESS_HOOKS",
"PORTAGE_QUIET",
"PORTAGE_ACTUAL_DISTDIR",
)
"""Approved good exceptions to using of variables."""
variables_usage_whitelist = {"EBUILD_PHASE", "EBUILD_PHASE_FUNC"}
def _check(self, used_type: str, used_names):
for used_name, (lineno, _) in used_names.items():
if used_name in self.special_whitelist:
continue
test_name = used_name.lower()
for reserved in self.reserved_prefixes:
if test_name.startswith(reserved):
yield used_name, used_type, reserved, "prefix", lineno + 1
for reserved in self.reserved_substrings:
if reserved in test_name:
yield used_name, used_type, reserved, "substring", lineno + 1
if self.reserved_ebuild_regex.match(test_name):
yield used_name, used_type, "ebuild", "substring", lineno + 1
def _feed(self, item):
yield from self._check(
"function",
{
item.node_str(node.child_by_field_name("name")): node.start_point
for node, _ in bash.func_query.captures(item.tree.root_node)
},
)
used_variables = {
item.node_str(node.child_by_field_name("name")): node.start_point
for node, _ in bash.var_assign_query.captures(item.tree.root_node)
}
for node, _ in bash.var_query.captures(item.tree.root_node):
if (name := item.node_str(node)) not in self.variables_usage_whitelist:
used_variables.setdefault(name, node.start_point)
yield from self._check("variable", used_variables)
class EclassReservedName(results.EclassResult, results.Warning):
"""Eclass uses reserved variable or function name for package manager."""
def __init__(
self, used_name: str, used_type: str, reserved_word: str, reserved_type: str, **kwargs
):
super().__init__(**kwargs)
self.used_name = used_name
self.used_type = used_type
self.reserved_word = reserved_word
self.reserved_type = reserved_type
@property
def desc(self):
return f'{self.eclass}: {self.used_type} name "{self.used_name}" is disallowed because "{self.reserved_word}" is a reserved {self.reserved_type}'
class EclassReservedCheck(_ReservedNameCheck):
"""Scan eclasses for reserved function or variable names."""
_source = sources.EclassParseRepoSource
known_results = frozenset([EclassReservedName])
required_addons = (addons.eclass.EclassAddon,)
def __init__(self, *args, eclass_addon):
super().__init__(*args)
self.eclass_cache = eclass_addon.eclasses
def feed(self, eclass):
for *args, _ in self._feed(eclass):
yield EclassReservedName(*args, eclass=eclass.name)
class EbuildReservedName(results.LineResult, results.Warning):
"""Ebuild uses reserved variable or function name for package manager."""
def __init__(self, used_type: str, reserved_word: str, reserved_type: str, **kwargs):
super().__init__(**kwargs)
self.used_type = used_type
self.reserved_word = reserved_word
self.reserved_type = reserved_type
@property
def desc(self):
return f'line {self.lineno}: {self.used_type} name "{self.line}" is disallowed because "{self.reserved_word}" is a reserved {self.reserved_type}'
class EbuildReservedCheck(_ReservedNameCheck):
"""Scan ebuilds for reserved function or variable names."""
_source = sources.EbuildParseRepoSource
known_results = frozenset([EbuildReservedName])
def __init__(self, options, **kwargs):
super().__init__(options, **kwargs)
self.phases_hooks = {
eapi_name: {
f"{prefix}_{phase}" for phase in eapi.phases.values() for prefix in ("pre", "post")
}
for eapi_name, eapi in EAPI.known_eapis.items()
}
def feed(self, pkg):
for used_name, *args, lineno in self._feed(pkg):
yield EbuildReservedName(*args, lineno=lineno, line=used_name, pkg=pkg)
for node, _ in bash.func_query.captures(pkg.tree.root_node):
used_name = pkg.node_str(node.child_by_field_name("name"))
if used_name in self.phases_hooks[str(pkg.eapi)]:
lineno, _ = node.start_point
yield EbuildReservedName(
"function", used_name, "phase hook", lineno=lineno + 1, line=used_name, pkg=pkg
)
|