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.
Files changed (57) hide show
  1. {echo_python-0.1.0 → echo_python-0.1.1}/Cargo.lock +1 -1
  2. {echo_python-0.1.0 → echo_python-0.1.1}/Cargo.toml +1 -1
  3. {echo_python-0.1.0 → echo_python-0.1.1}/PKG-INFO +10 -6
  4. echo_python-0.1.1/docs/rules/echo-001.txt +25 -0
  5. {echo_python-0.1.0 → echo_python-0.1.1}/readme.txt +9 -5
  6. echo_python-0.1.1/src/bindings.rs +310 -0
  7. {echo_python-0.1.0 → echo_python-0.1.1}/src/check/source.rs +14 -2
  8. {echo_python-0.1.0 → echo_python-0.1.1}/src/checker/mod.rs +3 -1
  9. {echo_python-0.1.0 → echo_python-0.1.1}/src/checker/visit.rs +5 -5
  10. {echo_python-0.1.0 → echo_python-0.1.1}/src/lib.rs +2 -1
  11. echo_python-0.1.1/src/rules/echo001.rs +423 -0
  12. {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/mod.rs +1 -1
  13. {echo_python-0.1.0 → echo_python-0.1.1}/src/settings/load.rs +40 -37
  14. {echo_python-0.1.0 → echo_python-0.1.1}/src/settings/mod.rs +3 -3
  15. {echo_python-0.1.0 → echo_python-0.1.1}/src/settings/rules.rs +13 -4
  16. echo_python-0.1.1/tests/echo001.rs +282 -0
  17. echo_python-0.1.0/src/rules/calls_use_kwargs.rs +0 -277
  18. echo_python-0.1.0/tests/calls_use_kwargs.rs +0 -135
  19. {echo_python-0.1.0 → echo_python-0.1.1}/.github/workflows/pypi.yml +0 -0
  20. {echo_python-0.1.0 → echo_python-0.1.1}/.gitignore +0 -0
  21. {echo_python-0.1.0 → echo_python-0.1.1}/AGENTS.md +0 -0
  22. {echo_python-0.1.0 → echo_python-0.1.1}/LICENSE +0 -0
  23. {echo_python-0.1.0 → echo_python-0.1.1}/Makefile +0 -0
  24. {echo_python-0.1.0 → echo_python-0.1.1}/mise.toml +0 -0
  25. {echo_python-0.1.0 → echo_python-0.1.1}/pyproject.toml +0 -0
  26. {echo_python-0.1.0 → echo_python-0.1.1}/rust-toolchain.toml +0 -0
  27. {echo_python-0.1.0 → echo_python-0.1.1}/rustfmt.toml +0 -0
  28. {echo_python-0.1.0 → echo_python-0.1.1}/src/check/mod.rs +0 -0
  29. {echo_python-0.1.0 → echo_python-0.1.1}/src/check/paths.rs +0 -0
  30. {echo_python-0.1.0 → echo_python-0.1.1}/src/check/walk.rs +0 -0
  31. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/mod.rs +0 -0
  32. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/report.rs +0 -0
  33. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/cmp.rs +0 -0
  34. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/from_expr.rs +0 -0
  35. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/mod.rs +0 -0
  36. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/order.rs +0 -0
  37. {echo_python-0.1.0 → echo_python-0.1.1}/src/common/sort_key/predicates.rs +0 -0
  38. {echo_python-0.1.0 → echo_python-0.1.1}/src/diagnostic/ctors.rs +0 -0
  39. {echo_python-0.1.0 → echo_python-0.1.1}/src/diagnostic/display.rs +0 -0
  40. {echo_python-0.1.0 → echo_python-0.1.1}/src/diagnostic/mod.rs +0 -0
  41. {echo_python-0.1.0 → echo_python-0.1.1}/src/locator/ctors.rs +0 -0
  42. {echo_python-0.1.0 → echo_python-0.1.1}/src/locator/methods.rs +0 -0
  43. {echo_python-0.1.0 → echo_python-0.1.1}/src/locator/mod.rs +0 -0
  44. {echo_python-0.1.0 → echo_python-0.1.1}/src/main.rs +0 -0
  45. {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/ctors.rs +0 -0
  46. {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/methods.rs +0 -0
  47. {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/mod.rs +0 -0
  48. {echo_python-0.1.0 → echo_python-0.1.1}/src/noqa/parse.rs +0 -0
  49. {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/mixed_list_sorted.rs +0 -0
  50. {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/numbers_list_sorted.rs +0 -0
  51. {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/params_one_per_line.rs +0 -0
  52. {echo_python-0.1.0 → echo_python-0.1.1}/src/rules/words_list_sorted.rs +0 -0
  53. {echo_python-0.1.0 → echo_python-0.1.1}/tests/locator.rs +0 -0
  54. {echo_python-0.1.0 → echo_python-0.1.1}/tests/mixed_list_sorted.rs +0 -0
  55. {echo_python-0.1.0 → echo_python-0.1.1}/tests/numbers_list_sorted.rs +0 -0
  56. {echo_python-0.1.0 → echo_python-0.1.1}/tests/params_one_per_line.rs +0 -0
  57. {echo_python-0.1.0 → echo_python-0.1.1}/tests/words_list_sorted.rs +0 -0
@@ -169,7 +169,7 @@ dependencies = [
169
169
 
170
170
  [[package]]
171
171
  name = "echo-python"
172
- version = "0.1.0"
172
+ version = "0.1.1"
173
173
  dependencies = [
174
174
  "anyhow",
175
175
  "clap",
@@ -7,7 +7,7 @@ license = "MIT"
7
7
  name = "echo-python"
8
8
  readme = "readme.txt"
9
9
  repository = "https://github.com/anyaagarenko/echo-python"
10
- version = "0.1.0"
10
+ version = "0.1.1"
11
11
 
12
12
  [[bin]]
13
13
  name = "echo-python"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echo-python
3
- Version: 0.1.0
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 - calls with multiple args must use keywords (self/cls exempt)
39
- ECHO002 - mixed lists: numbers by value, then words alphabetically
40
- ECHO003 - numeric lists sorted by value
41
- ECHO004 - word/name lists sorted alphabetically
42
- ECHO005 - params one per line when more than one (self/cls not counted)
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 - calls with multiple args must use keywords (self/cls exempt)
21
- ECHO002 - mixed lists: numbers by value, then words alphabetically
22
- ECHO003 - numeric lists sorted by value
23
- ECHO004 - word/name lists sorted alphabetically
24
- ECHO005 - params one per line when more than one (self/cls not counted)
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(path, &locator, &noqa, &settings, module, &mut diagnostics);
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::RULE_CALLS_USE_KWARGS;
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
- calls_use_kwargs, mixed_list_sorted, numbers_list_sorted, params_one_per_line,
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(RULE_CALLS_USE_KWARGS) {
30
- calls_use_kwargs::check(
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 RULE_CALLS_USE_KWARGS: &str = "ECHO001";
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";