echo-python 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {echo_python-0.1.0 → echo_python-0.1.1}/Cargo.lock +1 -1
- {echo_python-0.1.0 → echo_python-0.1.1}/Cargo.toml +1 -1
- {echo_python-0.1.0 → echo_python-0.1.1}/PKG-INFO +10 -6
- echo_python-0.1.1/docs/rules/echo-001.txt +25 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/readme.txt +9 -5
- echo_python-0.1.1/src/bindings.rs +310 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/check/source.rs +14 -2
- {echo_python-0.1.0 → echo_python-0.1.1}/src/checker/mod.rs +3 -1
- {echo_python-0.1.0 → echo_python-0.1.1}/src/checker/visit.rs +5 -5
- {echo_python-0.1.0 → echo_python-0.1.1}/src/lib.rs +2 -1
- echo_python-0.1.1/src/rules/echo001.rs +423 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/mod.rs +1 -1
- {echo_python-0.1.0 → echo_python-0.1.1}/src/settings/load.rs +40 -37
- {echo_python-0.1.0 → echo_python-0.1.1}/src/settings/mod.rs +3 -3
- {echo_python-0.1.0 → echo_python-0.1.1}/src/settings/rules.rs +13 -4
- echo_python-0.1.1/tests/echo001.rs +282 -0
- echo_python-0.1.0/src/rules/calls_use_kwargs.rs +0 -277
- echo_python-0.1.0/tests/calls_use_kwargs.rs +0 -135
- {echo_python-0.1.0 → echo_python-0.1.1}/.github/workflows/pypi.yml +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/.gitignore +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/AGENTS.md +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/LICENSE +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/Makefile +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/mise.toml +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/pyproject.toml +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/rust-toolchain.toml +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/rustfmt.toml +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/check/mod.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/check/paths.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/check/walk.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/mod.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/report.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/cmp.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/from_expr.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/mod.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/order.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/predicates.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/diagnostic/ctors.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/diagnostic/display.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/diagnostic/mod.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/locator/ctors.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/locator/methods.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/locator/mod.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/main.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/ctors.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/methods.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/mod.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/parse.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/mixed_list_sorted.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/numbers_list_sorted.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/params_one_per_line.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/words_list_sorted.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/tests/locator.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/tests/mixed_list_sorted.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/tests/numbers_list_sorted.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/tests/params_one_per_line.rs +0 -0
- {echo_python-0.1.0 → echo_python-0.1.1}/tests/words_list_sorted.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: echo-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Classifier: Environment :: Console
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -35,11 +35,12 @@ cli:
|
|
|
35
35
|
|
|
36
36
|
rules:
|
|
37
37
|
|
|
38
|
-
ECHO001 -
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
ECHO001 - use keyword arguments for calls with multiple positional args
|
|
39
|
+
https://github.com/anyaagarenko/echo-python/blob/main/docs/rules/echo-001.txt
|
|
40
|
+
ECHO002 - mixed list is not sorted (numbers then words)
|
|
41
|
+
ECHO003 - numbers list is not sorted
|
|
42
|
+
ECHO004 - words list is not sorted
|
|
43
|
+
ECHO005 - put each parameter on its own line when a function has more than one parameter
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
configure in pyproject.toml:
|
|
@@ -59,6 +60,9 @@ development:
|
|
|
59
60
|
see Makefile
|
|
60
61
|
|
|
61
62
|
|
|
63
|
+
contributions welcome
|
|
64
|
+
|
|
65
|
+
|
|
62
66
|
publishing:
|
|
63
67
|
|
|
64
68
|
bump version in Cargo.toml on main
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
ECHO001 - use keyword arguments for calls with multiple positional args
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
why:
|
|
5
|
+
|
|
6
|
+
multi-arg positional calls are hard to read and reorder safely.
|
|
7
|
+
keyword args make call sites explicit and ready to sort later.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
bad:
|
|
11
|
+
|
|
12
|
+
create_user("ada", "lovelace", True)
|
|
13
|
+
client.request("GET", url)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
good:
|
|
17
|
+
|
|
18
|
+
create_user(first="ada", last="lovelace", active=True)
|
|
19
|
+
client.request(method="GET", url=url)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
configure in pyproject.toml:
|
|
23
|
+
|
|
24
|
+
[tool.echo-python.echo001]
|
|
25
|
+
ignore = ["pytest.mark.parametrize", "pytest.param"]
|
|
@@ -17,11 +17,12 @@ cli:
|
|
|
17
17
|
|
|
18
18
|
rules:
|
|
19
19
|
|
|
20
|
-
ECHO001 -
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
ECHO001 - use keyword arguments for calls with multiple positional args
|
|
21
|
+
https://github.com/anyaagarenko/echo-python/blob/main/docs/rules/echo-001.txt
|
|
22
|
+
ECHO002 - mixed list is not sorted (numbers then words)
|
|
23
|
+
ECHO003 - numbers list is not sorted
|
|
24
|
+
ECHO004 - words list is not sorted
|
|
25
|
+
ECHO005 - put each parameter on its own line when a function has more than one parameter
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
configure in pyproject.toml:
|
|
@@ -41,6 +42,9 @@ development:
|
|
|
41
42
|
see Makefile
|
|
42
43
|
|
|
43
44
|
|
|
45
|
+
contributions welcome
|
|
46
|
+
|
|
47
|
+
|
|
44
48
|
publishing:
|
|
45
49
|
|
|
46
50
|
bump version in Cargo.toml on main
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
use std::collections::{HashMap, HashSet};
|
|
2
|
+
|
|
3
|
+
use rustpython_parser::ast::{self, Visitor};
|
|
4
|
+
use rustpython_parser::text_size::TextSize;
|
|
5
|
+
|
|
6
|
+
#[derive(Debug)]
|
|
7
|
+
struct Scope {
|
|
8
|
+
names: HashSet<String>,
|
|
9
|
+
parent: Option<usize>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[derive(Debug)]
|
|
13
|
+
pub(crate) struct Bindings {
|
|
14
|
+
call_scopes: HashMap<TextSize, usize>,
|
|
15
|
+
scopes: Vec<Scope>,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl Bindings {
|
|
19
|
+
pub(crate) fn from_module(module: &[ast::Stmt]) -> Self {
|
|
20
|
+
let mut collector = Collector::new();
|
|
21
|
+
for statement in module {
|
|
22
|
+
collector.visit_stmt(statement.clone());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
collector.bindings
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub(crate) fn match_builtin_expr(&self, call: &ast::ExprCall, builtin: &str) -> bool {
|
|
29
|
+
let Some(scope) = self.call_scopes.get(&call.range.start()) else {
|
|
30
|
+
return bare_or_builtins_attr(call.func.as_ref(), builtin);
|
|
31
|
+
};
|
|
32
|
+
match_builtin_expr(call.func.as_ref(), builtin, *scope, self)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn is_unbound(&self, name: &str, mut scope: usize) -> bool {
|
|
36
|
+
loop {
|
|
37
|
+
if self.scopes[scope].names.contains(name) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
scope = match self.scopes[scope].parent {
|
|
41
|
+
Some(parent) => parent,
|
|
42
|
+
None => return true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
struct Collector {
|
|
49
|
+
bindings: Bindings,
|
|
50
|
+
current_scope: usize,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
impl Collector {
|
|
54
|
+
fn new() -> Self {
|
|
55
|
+
Self {
|
|
56
|
+
bindings: Bindings {
|
|
57
|
+
call_scopes: HashMap::new(),
|
|
58
|
+
scopes: vec![Scope {
|
|
59
|
+
names: HashSet::new(),
|
|
60
|
+
parent: None,
|
|
61
|
+
}],
|
|
62
|
+
},
|
|
63
|
+
current_scope: 0,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fn enter_scope(&mut self) -> usize {
|
|
68
|
+
let parent = self.current_scope;
|
|
69
|
+
self.bindings.scopes.push(Scope {
|
|
70
|
+
names: HashSet::new(),
|
|
71
|
+
parent: Some(parent),
|
|
72
|
+
});
|
|
73
|
+
self.current_scope = self.bindings.scopes.len() - 1;
|
|
74
|
+
|
|
75
|
+
parent
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const fn leave_scope(&mut self, parent: usize) {
|
|
79
|
+
self.current_scope = parent;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fn bind(&mut self, name: &str) {
|
|
83
|
+
self.bindings.scopes[self.current_scope]
|
|
84
|
+
.names
|
|
85
|
+
.insert(name.to_string());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn bind_arguments(&mut self, arguments: &ast::Arguments) {
|
|
89
|
+
for argument in arguments
|
|
90
|
+
.posonlyargs
|
|
91
|
+
.iter()
|
|
92
|
+
.chain(&arguments.args)
|
|
93
|
+
.chain(&arguments.kwonlyargs)
|
|
94
|
+
{
|
|
95
|
+
self.bind(argument.def.arg.as_str());
|
|
96
|
+
}
|
|
97
|
+
if let Some(argument) = &arguments.vararg {
|
|
98
|
+
self.bind(argument.arg.as_str());
|
|
99
|
+
}
|
|
100
|
+
if let Some(argument) = &arguments.kwarg {
|
|
101
|
+
self.bind(argument.arg.as_str());
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn visit_function_outer(
|
|
106
|
+
&mut self,
|
|
107
|
+
arguments: &ast::Arguments,
|
|
108
|
+
decorators: &[ast::Expr],
|
|
109
|
+
returns: Option<&ast::Expr>,
|
|
110
|
+
type_params: &[ast::TypeParam],
|
|
111
|
+
) {
|
|
112
|
+
self.visit_arguments(arguments.clone());
|
|
113
|
+
for decorator in decorators {
|
|
114
|
+
self.visit_expr(decorator.clone());
|
|
115
|
+
}
|
|
116
|
+
if let Some(returns) = returns {
|
|
117
|
+
self.visit_expr(returns.clone());
|
|
118
|
+
}
|
|
119
|
+
for type_param in type_params {
|
|
120
|
+
self.visit_type_param(type_param.clone());
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
impl Visitor for Collector {
|
|
126
|
+
fn visit_expr_call(&mut self, node: ast::ExprCall) {
|
|
127
|
+
self.bindings
|
|
128
|
+
.call_scopes
|
|
129
|
+
.insert(node.range.start(), self.current_scope);
|
|
130
|
+
self.generic_visit_expr_call(node);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fn visit_stmt_ann_assign(&mut self, node: ast::StmtAnnAssign) {
|
|
134
|
+
if let ast::Expr::Name(target) = node.target.as_ref() {
|
|
135
|
+
self.bind(target.id.as_str());
|
|
136
|
+
}
|
|
137
|
+
self.generic_visit_stmt_ann_assign(node);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fn visit_stmt_assign(&mut self, node: ast::StmtAssign) {
|
|
141
|
+
for target in &node.targets {
|
|
142
|
+
if let ast::Expr::Name(name) = target {
|
|
143
|
+
self.bind(name.id.as_str());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
self.generic_visit_stmt_assign(node);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fn visit_stmt_async_function_def(&mut self, node: ast::StmtAsyncFunctionDef) {
|
|
150
|
+
self.bind(node.name.as_str());
|
|
151
|
+
self.visit_function_outer(
|
|
152
|
+
&node.args,
|
|
153
|
+
&node.decorator_list,
|
|
154
|
+
node.returns.as_deref(),
|
|
155
|
+
&node.type_params,
|
|
156
|
+
);
|
|
157
|
+
let parent = self.enter_scope();
|
|
158
|
+
self.bind_arguments(&node.args);
|
|
159
|
+
for statement in node.body {
|
|
160
|
+
self.visit_stmt(statement);
|
|
161
|
+
}
|
|
162
|
+
self.leave_scope(parent);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fn visit_stmt_class_def(&mut self, node: ast::StmtClassDef) {
|
|
166
|
+
self.bind(node.name.as_str());
|
|
167
|
+
for base in &node.bases {
|
|
168
|
+
self.visit_expr(base.clone());
|
|
169
|
+
}
|
|
170
|
+
for keyword in &node.keywords {
|
|
171
|
+
self.visit_keyword(keyword.clone());
|
|
172
|
+
}
|
|
173
|
+
for decorator in &node.decorator_list {
|
|
174
|
+
self.visit_expr(decorator.clone());
|
|
175
|
+
}
|
|
176
|
+
for type_param in &node.type_params {
|
|
177
|
+
self.visit_type_param(type_param.clone());
|
|
178
|
+
}
|
|
179
|
+
let parent = self.enter_scope();
|
|
180
|
+
for statement in node.body {
|
|
181
|
+
self.visit_stmt(statement);
|
|
182
|
+
}
|
|
183
|
+
self.leave_scope(parent);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fn visit_stmt_function_def(&mut self, node: ast::StmtFunctionDef) {
|
|
187
|
+
self.bind(node.name.as_str());
|
|
188
|
+
self.visit_function_outer(
|
|
189
|
+
&node.args,
|
|
190
|
+
&node.decorator_list,
|
|
191
|
+
node.returns.as_deref(),
|
|
192
|
+
&node.type_params,
|
|
193
|
+
);
|
|
194
|
+
let parent = self.enter_scope();
|
|
195
|
+
self.bind_arguments(&node.args);
|
|
196
|
+
for statement in node.body {
|
|
197
|
+
self.visit_stmt(statement);
|
|
198
|
+
}
|
|
199
|
+
self.leave_scope(parent);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn visit_stmt_import(&mut self, node: ast::StmtImport) {
|
|
203
|
+
for alias in &node.names {
|
|
204
|
+
self.bind(import_bound_name(alias));
|
|
205
|
+
}
|
|
206
|
+
self.generic_visit_stmt_import(node);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
fn visit_stmt_import_from(&mut self, node: ast::StmtImportFrom) {
|
|
210
|
+
for alias in &node.names {
|
|
211
|
+
if alias.name.as_str() == "*" {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
self.bind(import_bound_name(alias));
|
|
215
|
+
}
|
|
216
|
+
self.generic_visit_stmt_import_from(node);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fn match_builtin_expr(
|
|
221
|
+
expression: &ast::Expr,
|
|
222
|
+
builtin: &str,
|
|
223
|
+
scope: usize,
|
|
224
|
+
bindings: &Bindings,
|
|
225
|
+
) -> bool {
|
|
226
|
+
match expression {
|
|
227
|
+
ast::Expr::Name(name) => {
|
|
228
|
+
name.id.as_str() == builtin && bindings.is_unbound(name.id.as_str(), scope)
|
|
229
|
+
}
|
|
230
|
+
ast::Expr::Attribute(_) => bare_or_builtins_attr(expression, builtin),
|
|
231
|
+
_ => false,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
fn bare_or_builtins_attr(expression: &ast::Expr, builtin: &str) -> bool {
|
|
236
|
+
match expression {
|
|
237
|
+
ast::Expr::Name(name) => name.id.as_str() == builtin,
|
|
238
|
+
ast::Expr::Attribute(attribute) => {
|
|
239
|
+
attribute.attr.as_str() == builtin
|
|
240
|
+
&& matches!(
|
|
241
|
+
attribute.value.as_ref(),
|
|
242
|
+
ast::Expr::Name(name) if name.id.as_str() == "builtins"
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
_ => false,
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fn import_bound_name(alias: &ast::Alias) -> &str {
|
|
250
|
+
if let Some(name) = &alias.asname {
|
|
251
|
+
return name.as_str();
|
|
252
|
+
}
|
|
253
|
+
alias.name.split('.').next().unwrap_or(alias.name.as_str())
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#[cfg(test)]
|
|
257
|
+
mod tests {
|
|
258
|
+
use rustpython_parser::Parse;
|
|
259
|
+
use rustpython_parser::ast::{self, Visitor};
|
|
260
|
+
|
|
261
|
+
use super::Bindings;
|
|
262
|
+
|
|
263
|
+
fn match_builtin(source: &str, builtin: &str) -> bool {
|
|
264
|
+
let module = ast::Suite::parse(source, "<test>").expect("parse");
|
|
265
|
+
let bindings = Bindings::from_module(&module);
|
|
266
|
+
let mut calls = Calls::default();
|
|
267
|
+
for statement in module {
|
|
268
|
+
calls.visit_stmt(statement);
|
|
269
|
+
}
|
|
270
|
+
bindings.match_builtin_expr(calls.items.last().expect("call"), builtin)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#[derive(Default)]
|
|
274
|
+
struct Calls {
|
|
275
|
+
items: Vec<ast::ExprCall>,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
impl Visitor for Calls {
|
|
279
|
+
fn visit_expr_call(&mut self, node: ast::ExprCall) {
|
|
280
|
+
self.items.push(node.clone());
|
|
281
|
+
self.generic_visit_expr_call(node);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[test]
|
|
286
|
+
fn unbound_isinstance_matches_builtin() {
|
|
287
|
+
assert!(match_builtin("isinstance(1, int)\n", "isinstance"));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
#[test]
|
|
291
|
+
fn builtins_attr_matches_builtin() {
|
|
292
|
+
assert!(match_builtin("builtins.isinstance(1, int)\n", "isinstance"));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#[test]
|
|
296
|
+
fn shadowed_isinstance_does_not_match_builtin() {
|
|
297
|
+
assert!(!match_builtin(
|
|
298
|
+
"isinstance = check\nisinstance(1, int)\n",
|
|
299
|
+
"isinstance"
|
|
300
|
+
));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
#[test]
|
|
304
|
+
fn imported_isinstance_does_not_match_builtin() {
|
|
305
|
+
assert!(!match_builtin(
|
|
306
|
+
"from helpers import isinstance\nisinstance(1, int)\n",
|
|
307
|
+
"isinstance"
|
|
308
|
+
));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -4,6 +4,7 @@ use anyhow::{Context, Result};
|
|
|
4
4
|
use rustpython_parser::Parse;
|
|
5
5
|
use rustpython_parser::ast::{self, Visitor};
|
|
6
6
|
|
|
7
|
+
use crate::bindings::Bindings;
|
|
7
8
|
use crate::checker::Checker;
|
|
8
9
|
use crate::diagnostic::Diagnostic;
|
|
9
10
|
use crate::locator::Locator;
|
|
@@ -21,8 +22,17 @@ pub fn check_source(path: &Path, source: &str, options: &CheckOptions) -> Result
|
|
|
21
22
|
let locator = Locator::new(source);
|
|
22
23
|
let noqa = NoqaIndex::from_source(source);
|
|
23
24
|
let settings = settings::load_for_path(path, options);
|
|
25
|
+
let bindings = Bindings::from_module(&module);
|
|
24
26
|
let mut diagnostics = Vec::new();
|
|
25
|
-
visit_module(
|
|
27
|
+
visit_module(
|
|
28
|
+
path,
|
|
29
|
+
&bindings,
|
|
30
|
+
&locator,
|
|
31
|
+
&noqa,
|
|
32
|
+
&settings,
|
|
33
|
+
module,
|
|
34
|
+
&mut diagnostics,
|
|
35
|
+
);
|
|
26
36
|
Ok(diagnostics)
|
|
27
37
|
}
|
|
28
38
|
|
|
@@ -33,6 +43,7 @@ fn parse_module(path: &Path, source: &str) -> Result<ast::Suite> {
|
|
|
33
43
|
|
|
34
44
|
fn visit_module(
|
|
35
45
|
path: &Path,
|
|
46
|
+
bindings: &Bindings,
|
|
36
47
|
locator: &Locator,
|
|
37
48
|
noqa: &NoqaIndex,
|
|
38
49
|
settings: &Settings,
|
|
@@ -40,11 +51,12 @@ fn visit_module(
|
|
|
40
51
|
diagnostics: &mut Vec<Diagnostic>,
|
|
41
52
|
) {
|
|
42
53
|
let mut checker = Checker {
|
|
54
|
+
bindings,
|
|
55
|
+
diagnostics,
|
|
43
56
|
locator,
|
|
44
57
|
noqa,
|
|
45
58
|
path,
|
|
46
59
|
settings,
|
|
47
|
-
diagnostics,
|
|
48
60
|
};
|
|
49
61
|
for stmt in module {
|
|
50
62
|
checker.visit_stmt(stmt);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
mod visit;
|
|
2
2
|
|
|
3
|
+
use crate::bindings::Bindings;
|
|
3
4
|
use crate::diagnostic::Diagnostic;
|
|
4
5
|
use crate::locator::Locator;
|
|
5
6
|
use crate::noqa::NoqaIndex;
|
|
6
7
|
use crate::settings::Settings;
|
|
7
8
|
|
|
8
9
|
pub(crate) struct Checker<'a> {
|
|
10
|
+
pub(crate) bindings: &'a Bindings,
|
|
11
|
+
pub(crate) diagnostics: &'a mut Vec<Diagnostic>,
|
|
9
12
|
pub(crate) locator: &'a Locator,
|
|
10
13
|
pub(crate) noqa: &'a NoqaIndex,
|
|
11
14
|
pub(crate) path: &'a std::path::Path,
|
|
12
15
|
pub(crate) settings: &'a Settings,
|
|
13
|
-
pub(crate) diagnostics: &'a mut Vec<Diagnostic>,
|
|
14
16
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
use rustpython_parser::ast::{self, Visitor};
|
|
2
2
|
|
|
3
3
|
use super::Checker;
|
|
4
|
-
use crate::
|
|
4
|
+
use crate::RULE_ECHO001;
|
|
5
5
|
use crate::RULE_MIXED_LIST_SORTED;
|
|
6
6
|
use crate::RULE_NUMBERS_LIST_SORTED;
|
|
7
7
|
use crate::RULE_PARAMS_ONE_PER_LINE;
|
|
8
8
|
use crate::RULE_WORDS_LIST_SORTED;
|
|
9
9
|
use crate::rules::{
|
|
10
|
-
|
|
11
|
-
words_list_sorted,
|
|
10
|
+
echo001, mixed_list_sorted, numbers_list_sorted, params_one_per_line, words_list_sorted,
|
|
12
11
|
};
|
|
13
12
|
|
|
14
13
|
impl Visitor for Checker<'_> {
|
|
@@ -26,8 +25,9 @@ impl Visitor for Checker<'_> {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
fn visit_expr_call(&mut self, node: ast::ExprCall) {
|
|
29
|
-
if self.settings.is_enabled(
|
|
30
|
-
|
|
28
|
+
if self.settings.is_enabled(RULE_ECHO001) {
|
|
29
|
+
echo001::check(
|
|
30
|
+
self.bindings,
|
|
31
31
|
self.locator,
|
|
32
32
|
self.noqa,
|
|
33
33
|
self.path,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
mod bindings;
|
|
1
2
|
mod check;
|
|
2
3
|
mod checker;
|
|
3
4
|
mod common;
|
|
@@ -11,7 +12,7 @@ pub use check::{CheckResult, check_path, check_paths, check_source};
|
|
|
11
12
|
pub use diagnostic::Diagnostic;
|
|
12
13
|
pub use settings::CheckOptions;
|
|
13
14
|
|
|
14
|
-
pub const
|
|
15
|
+
pub const RULE_ECHO001: &str = "ECHO001";
|
|
15
16
|
pub const RULE_MIXED_LIST_SORTED: &str = "ECHO002";
|
|
16
17
|
pub const RULE_NUMBERS_LIST_SORTED: &str = "ECHO003";
|
|
17
18
|
pub const RULE_WORDS_LIST_SORTED: &str = "ECHO004";
|