Compare commits
1 Commits
v0.3.3
...
pythonplus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f97547b5f |
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,49 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-bandit`\]: Implement `S610` rule ([#10316](https://github.com/astral-sh/ruff/pull/10316))
|
||||
- \[`pycodestyle`\] Implement `blank-line-at-end-of-file` (`W391`) ([#10243](https://github.com/astral-sh/ruff/pull/10243))
|
||||
- \[`pycodestyle`\] Implement `redundant-backslash` (`E502`) ([#10292](https://github.com/astral-sh/ruff/pull/10292))
|
||||
- \[`pylint`\] - implement `redeclared-assigned-name` (`W0128`) ([#9268](https://github.com/astral-sh/ruff/pull/9268))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8_comprehensions`\] Handled special case for `C400` which also matches `C416` ([#10419](https://github.com/astral-sh/ruff/pull/10419))
|
||||
- \[`flake8-bandit`\] Implement upstream updates for `S311`, `S324` and `S605` ([#10313](https://github.com/astral-sh/ruff/pull/10313))
|
||||
- \[`pyflakes`\] Remove `F401` fix for `__init__` imports by default and allow opt-in to unsafe fix ([#10365](https://github.com/astral-sh/ruff/pull/10365))
|
||||
- \[`pylint`\] Implement `invalid-bool-return-type` (`E304`) ([#10377](https://github.com/astral-sh/ruff/pull/10377))
|
||||
- \[`pylint`\] Include builtin warnings in useless-exception-statement (`PLW0133`) ([#10394](https://github.com/astral-sh/ruff/pull/10394))
|
||||
|
||||
### CLI
|
||||
|
||||
- Add message on success to `ruff check` ([#8631](https://github.com/astral-sh/ruff/pull/8631))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`PIE970`\] Allow trailing ellipsis in `typing.TYPE_CHECKING` ([#10413](https://github.com/astral-sh/ruff/pull/10413))
|
||||
- Avoid `TRIO115` if the argument is a variable ([#10376](https://github.com/astral-sh/ruff/pull/10376))
|
||||
- \[`F811`\] Avoid removing shadowed imports that point to different symbols ([#10387](https://github.com/astral-sh/ruff/pull/10387))
|
||||
- Fix `F821` and `F822` false positives in `.pyi` files ([#10341](https://github.com/astral-sh/ruff/pull/10341))
|
||||
- Fix `F821` false negatives in `.py` files when `from __future__ import annotations` is active ([#10362](https://github.com/astral-sh/ruff/pull/10362))
|
||||
- Fix case where `Indexer` fails to identify continuation preceded by newline #10351 ([#10354](https://github.com/astral-sh/ruff/pull/10354))
|
||||
- Sort hash maps in `Settings` display ([#10370](https://github.com/astral-sh/ruff/pull/10370))
|
||||
- Track conditional deletions in the semantic model ([#10415](https://github.com/astral-sh/ruff/pull/10415))
|
||||
- \[`C413`\] Wrap expressions in parentheses when negating ([#10346](https://github.com/astral-sh/ruff/pull/10346))
|
||||
- \[`pycodestyle`\] Do not ignore lines before the first logical line in blank lines rules. ([#10382](https://github.com/astral-sh/ruff/pull/10382))
|
||||
- \[`pycodestyle`\] Do not trigger `E225` and `E275` when the next token is a ')' ([#10315](https://github.com/astral-sh/ruff/pull/10315))
|
||||
- \[`pylint`\] Avoid false-positive slot non-assignment for `__dict__` (`PLE0237`) ([#10348](https://github.com/astral-sh/ruff/pull/10348))
|
||||
- Gate f-string struct size test for Rustc \< 1.76 ([#10371](https://github.com/astral-sh/ruff/pull/10371))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Use `ruff.toml` format in README ([#10393](https://github.com/astral-sh/ruff/pull/10393))
|
||||
- \[`RUF008`\] Make it clearer that a mutable default in a dataclass is only valid if it is typed as a ClassVar ([#10395](https://github.com/astral-sh/ruff/pull/10395))
|
||||
- \[`pylint`\] Extend docs and test in `invalid-str-return-type` (`E307`) ([#10400](https://github.com/astral-sh/ruff/pull/10400))
|
||||
- Remove `.` from `check` and `format` commands ([#10217](https://github.com/astral-sh/ruff/pull/10217))
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Preview features
|
||||
@@ -1243,7 +1199,7 @@ Read Ruff's new [versioning policy](https://docs.astral.sh/ruff/versioning/).
|
||||
- \[`refurb`\] Add `single-item-membership-test` (`FURB171`) ([#7815](https://github.com/astral-sh/ruff/pull/7815))
|
||||
- \[`pylint`\] Add `and-or-ternary` (`R1706`) ([#7811](https://github.com/astral-sh/ruff/pull/7811))
|
||||
|
||||
*New rules are added in [preview](https://docs.astral.sh/ruff/preview/).*
|
||||
_New rules are added in [preview](https://docs.astral.sh/ruff/preview/)._
|
||||
|
||||
### Configuration
|
||||
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2003,7 +2003,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2167,7 +2167,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2448,7 +2448,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
||||
@@ -151,7 +151,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.3
|
||||
rev: v0.3.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
# Cannot combine with C416. Should use list comprehension here.
|
||||
even_nums = list(2 * x for x in range(3))
|
||||
odd_nums = list(
|
||||
2 * x + 1 for x in range(3)
|
||||
)
|
||||
|
||||
|
||||
# Short-circuit case, combine with C416 and should produce x = list(range(3))
|
||||
x = list(x for x in range(3))
|
||||
x = list(
|
||||
x for x in range(3)
|
||||
)
|
||||
|
||||
# Not built-in list.
|
||||
|
||||
def list(*args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
list(2 * x for x in range(3))
|
||||
list(x for x in range(3))
|
||||
|
||||
@@ -227,11 +227,3 @@ class Repro[int](Protocol):
|
||||
def impl(self) -> str:
|
||||
"""Docstring"""
|
||||
return self.func()
|
||||
|
||||
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
def contains_meaningful_ellipsis() -> list[int]:
|
||||
"""Allow this in a TYPE_CHECKING block."""
|
||||
...
|
||||
|
||||
@@ -11,13 +11,6 @@ def f():
|
||||
print(X)
|
||||
|
||||
|
||||
def f():
|
||||
global X
|
||||
|
||||
if X > 0:
|
||||
del X
|
||||
|
||||
|
||||
###
|
||||
# Non-errors.
|
||||
###
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
FIRST, FIRST = (1, 2) # PLW0128
|
||||
FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
|
||||
|
||||
FIRST, SECOND, _, _, _ignored = (1, 2, 3, 4, 5) # OK
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Binding, BindingKind, Imported, ResolvedReference, ScopeKind};
|
||||
use ruff_python_semantic::{Binding, BindingKind, Imported, ScopeKind};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -91,29 +91,13 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
if checker.enabled(Rule::GlobalVariableNotAssigned) {
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
let binding = checker.semantic.binding(binding_id);
|
||||
// If the binding is a `global`, then it's a top-level `global` that was never
|
||||
// assigned in the current scope. If it were assigned, the `global` would be
|
||||
// shadowed by the assignment.
|
||||
if binding.kind.is_global() {
|
||||
// If the binding was conditionally deleted, it will include a reference within
|
||||
// a `Del` context, but won't be shadowed by a `BindingKind::Deletion`, as in:
|
||||
// ```python
|
||||
// if condition:
|
||||
// del var
|
||||
// ```
|
||||
if binding
|
||||
.references
|
||||
.iter()
|
||||
.map(|id| checker.semantic.reference(*id))
|
||||
.all(ResolvedReference::is_load)
|
||||
{
|
||||
diagnostics.push(Diagnostic::new(
|
||||
pylint::rules::GlobalVariableNotAssigned {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
));
|
||||
}
|
||||
diagnostics.push(Diagnostic::new(
|
||||
pylint::rules::GlobalVariableNotAssigned {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1389,9 +1389,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
Stmt::Assign(assign @ ast::StmtAssign { targets, value, .. }) => {
|
||||
if checker.enabled(Rule::RedeclaredAssignedName) {
|
||||
pylint::rules::redeclared_assigned_name(checker, targets);
|
||||
}
|
||||
if checker.enabled(Rule::LambdaAssignment) {
|
||||
if let [target] = &targets[..] {
|
||||
pycodestyle::rules::lambda_assignment(checker, target, value, None, stmt);
|
||||
|
||||
@@ -540,11 +540,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
for name in names {
|
||||
if let Some((scope_id, binding_id)) = self.semantic.nonlocal(name) {
|
||||
// Mark the binding as "used".
|
||||
self.semantic.add_local_reference(
|
||||
binding_id,
|
||||
ExprContext::Load,
|
||||
name.range(),
|
||||
);
|
||||
self.semantic.add_local_reference(binding_id, name.range());
|
||||
|
||||
// Mark the binding in the enclosing scope as "rebound" in the current
|
||||
// scope.
|
||||
@@ -2117,8 +2113,7 @@ impl<'a> Checker<'a> {
|
||||
// Mark anything referenced in `__all__` as used.
|
||||
// TODO(charlie): `range` here should be the range of the name in `__all__`, not
|
||||
// the range of `__all__` itself.
|
||||
self.semantic
|
||||
.add_global_reference(binding_id, ExprContext::Load, range);
|
||||
self.semantic.add_global_reference(binding_id, range);
|
||||
} else {
|
||||
if self.semantic.global_scope().uses_star_imports() {
|
||||
if self.enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
|
||||
@@ -294,7 +294,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "W0108") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryLambda),
|
||||
(Pylint, "W0120") => (RuleGroup::Stable, rules::pylint::rules::UselessElseOnLoop),
|
||||
(Pylint, "W0127") => (RuleGroup::Stable, rules::pylint::rules::SelfAssigningVariable),
|
||||
(Pylint, "W0128") => (RuleGroup::Preview, rules::pylint::rules::RedeclaredAssignedName),
|
||||
(Pylint, "W0129") => (RuleGroup::Stable, rules::pylint::rules::AssertOnStringLiteral),
|
||||
(Pylint, "W0131") => (RuleGroup::Stable, rules::pylint::rules::NamedExprWithoutContext),
|
||||
(Pylint, "W0133") => (RuleGroup::Preview, rules::pylint::rules::UselessExceptionStatement),
|
||||
|
||||
@@ -255,7 +255,6 @@ impl Renamer {
|
||||
| BindingKind::ClassDefinition(_)
|
||||
| BindingKind::FunctionDefinition(_)
|
||||
| BindingKind::Deletion
|
||||
| BindingKind::ConditionalDeletion(_)
|
||||
| BindingKind::UnboundException(_) => {
|
||||
Some(Edit::range_replacement(target.to_string(), binding.range()))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::ExprGenerator;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -11,53 +9,37 @@ use super::helpers;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary generators that can be rewritten as `list`
|
||||
/// comprehensions (or with `list` directly).
|
||||
/// comprehensions.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// It is unnecessary to use `list` around a generator expression, since
|
||||
/// there are equivalent comprehensions for these types. Using a
|
||||
/// comprehension is clearer and more idiomatic.
|
||||
///
|
||||
/// Further, if the comprehension can be removed entirely, as in the case of
|
||||
/// `list(x for x in foo)`, it's better to use `list(foo)` directly, since it's
|
||||
/// even more direct.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```python
|
||||
/// list(f(x) for x in foo)
|
||||
/// list(x for x in foo)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// [f(x) for x in foo]
|
||||
/// list(foo)
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe, as it may occasionally drop comments
|
||||
/// when rewriting the call. In most cases, though, comments will be preserved.
|
||||
#[violation]
|
||||
pub struct UnnecessaryGeneratorList {
|
||||
short_circuit: bool,
|
||||
}
|
||||
pub struct UnnecessaryGeneratorList;
|
||||
|
||||
impl AlwaysFixableViolation for UnnecessaryGeneratorList {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
if self.short_circuit {
|
||||
format!("Unnecessary generator (rewrite using `list()`")
|
||||
} else {
|
||||
format!("Unnecessary generator (rewrite as a `list` comprehension)")
|
||||
}
|
||||
format!("Unnecessary generator (rewrite as a `list` comprehension)")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
if self.short_circuit {
|
||||
"Rewrite using `list()`".to_string()
|
||||
} else {
|
||||
"Rewrite as a `list` comprehension".to_string()
|
||||
}
|
||||
"Rewrite as a `list` comprehension".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,59 +56,28 @@ pub(crate) fn unnecessary_generator_list(checker: &mut Checker, call: &ast::Expr
|
||||
if !checker.semantic().is_builtin("list") {
|
||||
return;
|
||||
}
|
||||
if argument.is_generator_expr() {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, call.range());
|
||||
|
||||
let Some(ExprGenerator {
|
||||
elt, generators, ..
|
||||
}) = argument.as_generator_expr()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// Convert `list(x for x in y)` to `[x for x in y]`.
|
||||
diagnostic.set_fix({
|
||||
// Replace `list(` with `[`.
|
||||
let call_start = Edit::replacement(
|
||||
"[".to_string(),
|
||||
call.start(),
|
||||
call.arguments.start() + TextSize::from(1),
|
||||
);
|
||||
|
||||
// Short-circuit: given `list(x for x in y)`, generate `list(y)` (in lieu of `[x for x in y]`).
|
||||
if let [generator] = generators.as_slice() {
|
||||
if generator.ifs.is_empty() && !generator.is_async {
|
||||
if ComparableExpr::from(elt) == ComparableExpr::from(&generator.target) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnnecessaryGeneratorList {
|
||||
short_circuit: true,
|
||||
},
|
||||
call.range(),
|
||||
);
|
||||
let iterator = format!("list({})", checker.locator().slice(generator.iter.range()));
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
iterator,
|
||||
call.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Replace `)` with `]`.
|
||||
let call_end = Edit::replacement(
|
||||
"]".to_string(),
|
||||
call.arguments.end() - TextSize::from(1),
|
||||
call.end(),
|
||||
);
|
||||
|
||||
Fix::unsafe_edits(call_start, [call_end])
|
||||
});
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
// Convert `list(f(x) for x in y)` to `[f(x) for x in y]`.
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnnecessaryGeneratorList {
|
||||
short_circuit: false,
|
||||
},
|
||||
call.range(),
|
||||
);
|
||||
diagnostic.set_fix({
|
||||
// Replace `list(` with `[`.
|
||||
let call_start = Edit::replacement(
|
||||
"[".to_string(),
|
||||
call.start(),
|
||||
call.arguments.start() + TextSize::from(1),
|
||||
);
|
||||
|
||||
// Replace `)` with `]`.
|
||||
let call_end = Edit::replacement(
|
||||
"]".to_string(),
|
||||
call.arguments.end() - TextSize::from(1),
|
||||
call.end(),
|
||||
);
|
||||
|
||||
Fix::unsafe_edits(call_start, [call_end])
|
||||
});
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -1,90 +1,42 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_comprehensions/mod.rs
|
||||
---
|
||||
C400.py:2:13: C400 [*] Unnecessary generator (rewrite as a `list` comprehension)
|
||||
C400.py:1:5: C400 [*] Unnecessary generator (rewrite as a `list` comprehension)
|
||||
|
|
||||
1 | # Cannot combine with C416. Should use list comprehension here.
|
||||
2 | even_nums = list(2 * x for x in range(3))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C400
|
||||
3 | odd_nums = list(
|
||||
4 | 2 * x + 1 for x in range(3)
|
||||
1 | x = list(x for x in range(3))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ C400
|
||||
2 | x = list(
|
||||
3 | x for x in range(3)
|
||||
|
|
||||
= help: Rewrite as a `list` comprehension
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 1 | # Cannot combine with C416. Should use list comprehension here.
|
||||
2 |-even_nums = list(2 * x for x in range(3))
|
||||
2 |+even_nums = [2 * x for x in range(3)]
|
||||
3 3 | odd_nums = list(
|
||||
4 4 | 2 * x + 1 for x in range(3)
|
||||
5 5 | )
|
||||
1 |-x = list(x for x in range(3))
|
||||
1 |+x = [x for x in range(3)]
|
||||
2 2 | x = list(
|
||||
3 3 | x for x in range(3)
|
||||
4 4 | )
|
||||
|
||||
C400.py:3:12: C400 [*] Unnecessary generator (rewrite as a `list` comprehension)
|
||||
C400.py:2:5: C400 [*] Unnecessary generator (rewrite as a `list` comprehension)
|
||||
|
|
||||
1 | # Cannot combine with C416. Should use list comprehension here.
|
||||
2 | even_nums = list(2 * x for x in range(3))
|
||||
3 | odd_nums = list(
|
||||
| ____________^
|
||||
4 | | 2 * x + 1 for x in range(3)
|
||||
5 | | )
|
||||
1 | x = list(x for x in range(3))
|
||||
2 | x = list(
|
||||
| _____^
|
||||
3 | | x for x in range(3)
|
||||
4 | | )
|
||||
| |_^ C400
|
||||
|
|
||||
= help: Rewrite as a `list` comprehension
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 1 | # Cannot combine with C416. Should use list comprehension here.
|
||||
2 2 | even_nums = list(2 * x for x in range(3))
|
||||
3 |-odd_nums = list(
|
||||
3 |+odd_nums = [
|
||||
4 4 | 2 * x + 1 for x in range(3)
|
||||
5 |-)
|
||||
5 |+]
|
||||
1 1 | x = list(x for x in range(3))
|
||||
2 |-x = list(
|
||||
2 |+x = [
|
||||
3 3 | x for x in range(3)
|
||||
4 |-)
|
||||
4 |+]
|
||||
5 5 |
|
||||
6 6 |
|
||||
7 7 |
|
||||
8 8 | # Short-circuit case, combine with C416 and should produce x = list(range(3))
|
||||
7 7 | def list(*args, **kwargs):
|
||||
|
||||
C400.py:9:5: C400 [*] Unnecessary generator (rewrite using `list()`
|
||||
|
|
||||
8 | # Short-circuit case, combine with C416 and should produce x = list(range(3))
|
||||
9 | x = list(x for x in range(3))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ C400
|
||||
10 | x = list(
|
||||
11 | x for x in range(3)
|
||||
|
|
||||
= help: Rewrite using `list()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
6 6 |
|
||||
7 7 |
|
||||
8 8 | # Short-circuit case, combine with C416 and should produce x = list(range(3))
|
||||
9 |-x = list(x for x in range(3))
|
||||
9 |+x = list(range(3))
|
||||
10 10 | x = list(
|
||||
11 11 | x for x in range(3)
|
||||
12 12 | )
|
||||
|
||||
C400.py:10:5: C400 [*] Unnecessary generator (rewrite using `list()`
|
||||
|
|
||||
8 | # Short-circuit case, combine with C416 and should produce x = list(range(3))
|
||||
9 | x = list(x for x in range(3))
|
||||
10 | x = list(
|
||||
| _____^
|
||||
11 | | x for x in range(3)
|
||||
12 | | )
|
||||
| |_^ C400
|
||||
13 |
|
||||
14 | # Not built-in list.
|
||||
|
|
||||
= help: Rewrite using `list()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 |
|
||||
8 8 | # Short-circuit case, combine with C416 and should produce x = list(range(3))
|
||||
9 9 | x = list(x for x in range(3))
|
||||
10 |-x = list(
|
||||
11 |- x for x in range(3)
|
||||
12 |-)
|
||||
10 |+x = list(range(3))
|
||||
13 11 |
|
||||
14 12 | # Not built-in list.
|
||||
15 13 | def list(*args, **kwargs):
|
||||
|
||||
@@ -87,12 +87,6 @@ pub(crate) fn unnecessary_placeholder(checker: &mut Checker, body: &[Stmt]) {
|
||||
let kind = match stmt {
|
||||
Stmt::Pass(_) => Placeholder::Pass,
|
||||
Stmt::Expr(expr) if expr.value.is_ellipsis_literal_expr() => {
|
||||
// In a type-checking block, a trailing ellipsis might be meaningful. A
|
||||
// user might be using the type-checking context to declare a stub.
|
||||
if checker.semantic().in_type_checking_block() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ellipses are significant in protocol methods and abstract methods. Specifically,
|
||||
// Pyright uses the presence of an ellipsis to indicate that a method is a stub,
|
||||
// rather than a default implementation.
|
||||
|
||||
@@ -121,7 +121,8 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
checker
|
||||
.semantic()
|
||||
.reference(reference_id)
|
||||
.in_runtime_context()
|
||||
.context()
|
||||
.is_runtime()
|
||||
})
|
||||
{
|
||||
let Some(node_id) = binding.source else {
|
||||
@@ -154,7 +155,8 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
if checker.settings.flake8_type_checking.quote_annotations
|
||||
&& binding.references().all(|reference_id| {
|
||||
let reference = checker.semantic().reference(reference_id);
|
||||
reference.in_typing_context() || reference.in_runtime_evaluated_annotation()
|
||||
reference.context().is_typing()
|
||||
|| reference.in_runtime_evaluated_annotation()
|
||||
})
|
||||
{
|
||||
actions
|
||||
@@ -266,7 +268,7 @@ fn quote_imports(checker: &Checker, node_id: NodeId, imports: &[ImportBinding])
|
||||
.flat_map(|ImportBinding { binding, .. }| {
|
||||
binding.references.iter().filter_map(|reference_id| {
|
||||
let reference = checker.semantic().reference(*reference_id);
|
||||
if reference.in_runtime_context() {
|
||||
if reference.context().is_runtime() {
|
||||
Some(quote_annotation(
|
||||
reference.expression_id()?,
|
||||
checker.semantic(),
|
||||
|
||||
@@ -499,7 +499,7 @@ fn fix_imports(checker: &Checker, node_id: NodeId, imports: &[ImportBinding]) ->
|
||||
.flat_map(|ImportBinding { binding, .. }| {
|
||||
binding.references.iter().filter_map(|reference_id| {
|
||||
let reference = checker.semantic().reference(*reference_id);
|
||||
if reference.in_runtime_context() {
|
||||
if reference.context().is_runtime() {
|
||||
Some(quote_annotation(
|
||||
reference.expression_id()?,
|
||||
checker.semantic(),
|
||||
|
||||
@@ -95,7 +95,6 @@ mod tests {
|
||||
#[test_case(Rule::NonlocalWithoutBinding, Path::new("nonlocal_without_binding.py"))]
|
||||
#[test_case(Rule::NonSlotAssignment, Path::new("non_slot_assignment.py"))]
|
||||
#[test_case(Rule::PropertyWithParameters, Path::new("property_with_parameters.py"))]
|
||||
#[test_case(Rule::RedeclaredAssignedName, Path::new("redeclared_assigned_name.py"))]
|
||||
#[test_case(
|
||||
Rule::RedefinedArgumentFromLocal,
|
||||
Path::new("redefined_argument_from_local.py")
|
||||
|
||||
@@ -47,7 +47,6 @@ pub(crate) use non_slot_assignment::*;
|
||||
pub(crate) use nonlocal_without_binding::*;
|
||||
pub(crate) use potential_index_error::*;
|
||||
pub(crate) use property_with_parameters::*;
|
||||
pub(crate) use redeclared_assigned_name::*;
|
||||
pub(crate) use redefined_argument_from_local::*;
|
||||
pub(crate) use redefined_loop_name::*;
|
||||
pub(crate) use repeated_equality_comparison::*;
|
||||
@@ -137,7 +136,6 @@ mod non_slot_assignment;
|
||||
mod nonlocal_without_binding;
|
||||
mod potential_index_error;
|
||||
mod property_with_parameters;
|
||||
mod redeclared_assigned_name;
|
||||
mod redefined_argument_from_local;
|
||||
mod redefined_loop_name;
|
||||
mod repeated_equality_comparison;
|
||||
|
||||
@@ -67,7 +67,6 @@ pub(crate) fn non_ascii_name(binding: &Binding, locator: &Locator) -> Option<Dia
|
||||
| BindingKind::FromImport(_)
|
||||
| BindingKind::SubmoduleImport(_)
|
||||
| BindingKind::Deletion
|
||||
| BindingKind::ConditionalDeletion(_)
|
||||
| BindingKind::UnboundException(_) => {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for declared assignments to the same variable multiple times
|
||||
/// in the same assignment.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Assigning a variable multiple times in the same assignment is redundant,
|
||||
/// as the final assignment to the variable is what the value will be.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a, b, a = (1, 2, 3)
|
||||
/// print(a) # 3
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// # this is assuming you want to assign 3 to `a`
|
||||
/// _, b, a = (1, 2, 3)
|
||||
/// print(a) # 3
|
||||
/// ```
|
||||
///
|
||||
#[violation]
|
||||
pub struct RedeclaredAssignedName {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Violation for RedeclaredAssignedName {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let RedeclaredAssignedName { name } = self;
|
||||
format!("Redeclared variable `{name}` in assignment")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLW0128
|
||||
pub(crate) fn redeclared_assigned_name(checker: &mut Checker, targets: &Vec<Expr>) {
|
||||
let mut names: Vec<String> = Vec::new();
|
||||
|
||||
for target in targets {
|
||||
check_expr(checker, target, &mut names);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(checker: &mut Checker, expr: &Expr, names: &mut Vec<String>) {
|
||||
match expr {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
for target in elts {
|
||||
check_expr(checker, target, names);
|
||||
}
|
||||
}
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
if checker.settings.dummy_variable_rgx.is_match(id) {
|
||||
// Ignore dummy variable assignments
|
||||
return;
|
||||
}
|
||||
if names.contains(id) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
RedeclaredAssignedName {
|
||||
name: id.to_string(),
|
||||
},
|
||||
expr.range(),
|
||||
));
|
||||
}
|
||||
names.push(id.to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
redeclared_assigned_name.py:1:8: PLW0128 Redeclared variable `FIRST` in assignment
|
||||
|
|
||||
1 | FIRST, FIRST = (1, 2) # PLW0128
|
||||
| ^^^^^ PLW0128
|
||||
2 | FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
3 | FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
|
|
||||
|
||||
redeclared_assigned_name.py:2:9: PLW0128 Redeclared variable `FIRST` in assignment
|
||||
|
|
||||
1 | FIRST, FIRST = (1, 2) # PLW0128
|
||||
2 | FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
| ^^^^^ PLW0128
|
||||
3 | FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
4 | FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
|
||||
|
|
||||
|
||||
redeclared_assigned_name.py:3:9: PLW0128 Redeclared variable `FIRST` in assignment
|
||||
|
|
||||
1 | FIRST, FIRST = (1, 2) # PLW0128
|
||||
2 | FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
3 | FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
| ^^^^^ PLW0128
|
||||
4 | FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
|
||||
|
|
||||
|
||||
redeclared_assigned_name.py:3:32: PLW0128 Redeclared variable `FIRST` in assignment
|
||||
|
|
||||
1 | FIRST, FIRST = (1, 2) # PLW0128
|
||||
2 | FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
3 | FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
| ^^^^^ PLW0128
|
||||
4 | FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
|
||||
|
|
||||
|
||||
redeclared_assigned_name.py:4:23: PLW0128 Redeclared variable `FIRST` in assignment
|
||||
|
|
||||
2 | FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
3 | FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
4 | FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
|
||||
| ^^^^^ PLW0128
|
||||
5 |
|
||||
6 | FIRST, SECOND, _, _, _ignored = (1, 2, 3, 4, 5) # OK
|
||||
|
|
||||
|
||||
redeclared_assigned_name.py:4:30: PLW0128 Redeclared variable `SECOND` in assignment
|
||||
|
|
||||
2 | FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
|
||||
3 | FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
|
||||
4 | FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
|
||||
| ^^^^^^ PLW0128
|
||||
5 |
|
||||
6 | FIRST, SECOND, _, _, _ignored = (1, 2, 3, 4, 5) # OK
|
||||
|
|
||||
|
||||
|
||||
@@ -141,15 +141,15 @@ impl<'src, 'loc> UselessSuppressionComments<'src, 'loc> {
|
||||
if comment.kind == SuppressionKind::Off || comment.kind == SuppressionKind::On {
|
||||
if let Some(
|
||||
AnyNodeRef::StmtClassDef(StmtClassDef {
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
})
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
})
|
||||
| AnyNodeRef::StmtFunctionDef(StmtFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
}),
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
}),
|
||||
) = comment.enclosing
|
||||
{
|
||||
if comment.line_position.is_own_line() && comment.range.start() < name.start() {
|
||||
@@ -196,7 +196,7 @@ impl<'src, 'loc> UselessSuppressionComments<'src, 'loc> {
|
||||
self.captured.sort_by_key(|(t, _)| t.start());
|
||||
}
|
||||
|
||||
fn ignored_comments(&self) -> impl Iterator<Item = (TextRange, IgnoredReason)> + '_ {
|
||||
fn ignored_comments(&self) -> impl Iterator<Item=(TextRange, IgnoredReason)> + '_ {
|
||||
self.captured.iter().map(|(r, i)| (*r, *i))
|
||||
}
|
||||
}
|
||||
@@ -276,7 +276,8 @@ const fn is_valid_enclosing_node(node: AnyNodeRef) -> bool {
|
||||
| AnyNodeRef::StmtIpyEscapeCommand(_)
|
||||
| AnyNodeRef::ExceptHandlerExceptHandler(_)
|
||||
| AnyNodeRef::MatchCase(_)
|
||||
| AnyNodeRef::ElifElseClause(_) => true,
|
||||
| AnyNodeRef::ElifElseClause(_)
|
||||
| AnyNodeRef::StmtCrement(_) => true,
|
||||
|
||||
AnyNodeRef::ExprBoolOp(_)
|
||||
| AnyNodeRef::ExprNamed(_)
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::rules::ruff::rules::helpers::{is_class_var_annotation, is_dataclass};
|
||||
/// Instead of sharing mutable defaults, use the `field(default_factory=...)`
|
||||
/// pattern.
|
||||
///
|
||||
/// If the default value is intended to be mutable, it must be annotated with
|
||||
/// `typing.ClassVar`; otherwise, a `ValueError` will be raised.
|
||||
/// If the default value is intended to be mutable, it should be annotated with
|
||||
/// `typing.ClassVar`.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```python
|
||||
@@ -29,8 +29,6 @@ use crate::rules::ruff::rules::helpers::{is_class_var_annotation, is_dataclass};
|
||||
///
|
||||
/// @dataclass
|
||||
/// class A:
|
||||
/// # A list without a `default_factory` or `ClassVar` annotation
|
||||
/// # will raise a `ValueError`.
|
||||
/// mutable_default: list[int] = []
|
||||
/// ```
|
||||
///
|
||||
@@ -46,7 +44,7 @@ use crate::rules::ruff::rules::helpers::{is_class_var_annotation, is_dataclass};
|
||||
///
|
||||
/// Or:
|
||||
/// ```python
|
||||
/// from dataclasses import dataclass
|
||||
/// from dataclasses import dataclass, field
|
||||
/// from typing import ClassVar
|
||||
///
|
||||
///
|
||||
|
||||
@@ -1543,6 +1543,9 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
|
||||
ast::Stmt::Pass(_) => Self::Pass,
|
||||
ast::Stmt::Break(_) => Self::Break,
|
||||
ast::Stmt::Continue(_) => Self::Continue,
|
||||
ast::Stmt::Crement(_) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,6 +432,7 @@ pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool {
|
||||
Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => {
|
||||
any_over_expr(target, func) || any_over_expr(value, func)
|
||||
}
|
||||
Stmt::Crement(ast::StmtCrement { target, .. }) => any_over_expr(target, func),
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
target,
|
||||
annotation,
|
||||
|
||||
@@ -36,6 +36,7 @@ pub enum AnyNode {
|
||||
StmtTypeAlias(ast::StmtTypeAlias),
|
||||
StmtAssign(ast::StmtAssign),
|
||||
StmtAugAssign(ast::StmtAugAssign),
|
||||
StmtCrement(ast::StmtCrement),
|
||||
StmtAnnAssign(ast::StmtAnnAssign),
|
||||
StmtFor(ast::StmtFor),
|
||||
StmtWhile(ast::StmtWhile),
|
||||
@@ -130,6 +131,7 @@ impl AnyNode {
|
||||
AnyNode::StmtTypeAlias(node) => Some(Stmt::TypeAlias(node)),
|
||||
AnyNode::StmtAssign(node) => Some(Stmt::Assign(node)),
|
||||
AnyNode::StmtAugAssign(node) => Some(Stmt::AugAssign(node)),
|
||||
AnyNode::StmtCrement(node) => Some(Stmt::Crement(node)),
|
||||
AnyNode::StmtAnnAssign(node) => Some(Stmt::AnnAssign(node)),
|
||||
AnyNode::StmtFor(node) => Some(Stmt::For(node)),
|
||||
AnyNode::StmtWhile(node) => Some(Stmt::While(node)),
|
||||
@@ -262,6 +264,7 @@ impl AnyNode {
|
||||
| AnyNode::StmtTypeAlias(_)
|
||||
| AnyNode::StmtAssign(_)
|
||||
| AnyNode::StmtAugAssign(_)
|
||||
| AnyNode::StmtCrement(_)
|
||||
| AnyNode::StmtAnnAssign(_)
|
||||
| AnyNode::StmtFor(_)
|
||||
| AnyNode::StmtWhile(_)
|
||||
@@ -327,6 +330,7 @@ impl AnyNode {
|
||||
| AnyNode::StmtTypeAlias(_)
|
||||
| AnyNode::StmtAssign(_)
|
||||
| AnyNode::StmtAugAssign(_)
|
||||
| AnyNode::StmtCrement(_)
|
||||
| AnyNode::StmtAnnAssign(_)
|
||||
| AnyNode::StmtFor(_)
|
||||
| AnyNode::StmtWhile(_)
|
||||
@@ -432,6 +436,7 @@ impl AnyNode {
|
||||
| AnyNode::StmtTypeAlias(_)
|
||||
| AnyNode::StmtAssign(_)
|
||||
| AnyNode::StmtAugAssign(_)
|
||||
| AnyNode::StmtCrement(_)
|
||||
| AnyNode::StmtAnnAssign(_)
|
||||
| AnyNode::StmtFor(_)
|
||||
| AnyNode::StmtWhile(_)
|
||||
@@ -522,6 +527,7 @@ impl AnyNode {
|
||||
| AnyNode::StmtTypeAlias(_)
|
||||
| AnyNode::StmtAssign(_)
|
||||
| AnyNode::StmtAugAssign(_)
|
||||
| AnyNode::StmtCrement(_)
|
||||
| AnyNode::StmtAnnAssign(_)
|
||||
| AnyNode::StmtFor(_)
|
||||
| AnyNode::StmtWhile(_)
|
||||
@@ -637,6 +643,7 @@ impl AnyNode {
|
||||
Self::StmtTypeAlias(node) => AnyNodeRef::StmtTypeAlias(node),
|
||||
Self::StmtAssign(node) => AnyNodeRef::StmtAssign(node),
|
||||
Self::StmtAugAssign(node) => AnyNodeRef::StmtAugAssign(node),
|
||||
Self::StmtCrement(node) => AnyNodeRef::StmtCrement(node),
|
||||
Self::StmtAnnAssign(node) => AnyNodeRef::StmtAnnAssign(node),
|
||||
Self::StmtFor(node) => AnyNodeRef::StmtFor(node),
|
||||
Self::StmtWhile(node) => AnyNodeRef::StmtWhile(node),
|
||||
@@ -1125,6 +1132,48 @@ impl AstNode for ast::StmtAugAssign {
|
||||
visitor.visit_expr(value);
|
||||
}
|
||||
}
|
||||
impl AstNode for ast::StmtCrement {
|
||||
fn cast(kind: AnyNode) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if let AnyNode::StmtCrement(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
|
||||
if let AnyNodeRef::StmtCrement(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any_node_ref(&self) -> AnyNodeRef {
|
||||
AnyNodeRef::from(self)
|
||||
}
|
||||
|
||||
fn into_any_node(self) -> AnyNode {
|
||||
AnyNode::from(self)
|
||||
}
|
||||
|
||||
fn visit_preorder<'a, V>(&'a self, visitor: &mut V)
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let ast::StmtCrement {
|
||||
target,
|
||||
op: _,
|
||||
range: _,
|
||||
} = self;
|
||||
|
||||
visitor.visit_expr(target);
|
||||
// TODO(konstin): visitor.visit_operator(op);
|
||||
}
|
||||
}
|
||||
impl AstNode for ast::StmtAnnAssign {
|
||||
fn cast(kind: AnyNode) -> Option<Self>
|
||||
where
|
||||
@@ -4538,6 +4587,7 @@ impl From<Stmt> for AnyNode {
|
||||
Stmt::Break(node) => AnyNode::StmtBreak(node),
|
||||
Stmt::Continue(node) => AnyNode::StmtContinue(node),
|
||||
Stmt::IpyEscapeCommand(node) => AnyNode::StmtIpyEscapeCommand(node),
|
||||
Stmt::Crement(node) => AnyNode::StmtCrement(node),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4676,6 +4726,12 @@ impl From<ast::StmtAugAssign> for AnyNode {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ast::StmtCrement> for AnyNode {
|
||||
fn from(node: ast::StmtCrement) -> Self {
|
||||
AnyNode::StmtCrement(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ast::StmtAnnAssign> for AnyNode {
|
||||
fn from(node: ast::StmtAnnAssign) -> Self {
|
||||
AnyNode::StmtAnnAssign(node)
|
||||
@@ -5251,6 +5307,7 @@ impl Ranged for AnyNode {
|
||||
AnyNode::StringLiteral(node) => node.range(),
|
||||
AnyNode::BytesLiteral(node) => node.range(),
|
||||
AnyNode::ElifElseClause(node) => node.range(),
|
||||
AnyNode::StmtCrement(node) => node.range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5266,6 +5323,7 @@ pub enum AnyNodeRef<'a> {
|
||||
StmtTypeAlias(&'a ast::StmtTypeAlias),
|
||||
StmtAssign(&'a ast::StmtAssign),
|
||||
StmtAugAssign(&'a ast::StmtAugAssign),
|
||||
StmtCrement(&'a ast::StmtCrement),
|
||||
StmtAnnAssign(&'a ast::StmtAnnAssign),
|
||||
StmtFor(&'a ast::StmtFor),
|
||||
StmtWhile(&'a ast::StmtWhile),
|
||||
@@ -5444,6 +5502,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
AnyNodeRef::StringLiteral(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::BytesLiteral(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::ElifElseClause(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::StmtCrement(node) => NonNull::from(*node).cast(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5546,6 +5605,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
AnyNodeRef::StringLiteral(_) => NodeKind::StringLiteral,
|
||||
AnyNodeRef::BytesLiteral(_) => NodeKind::BytesLiteral,
|
||||
AnyNodeRef::ElifElseClause(_) => NodeKind::ElifElseClause,
|
||||
AnyNodeRef::StmtCrement(_) => NodeKind::StmtCrement,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5558,6 +5618,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::StmtTypeAlias(_)
|
||||
| AnyNodeRef::StmtAssign(_)
|
||||
| AnyNodeRef::StmtAugAssign(_)
|
||||
| AnyNodeRef::StmtCrement(_)
|
||||
| AnyNodeRef::StmtAnnAssign(_)
|
||||
| AnyNodeRef::StmtFor(_)
|
||||
| AnyNodeRef::StmtWhile(_)
|
||||
@@ -5690,6 +5751,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::StmtTypeAlias(_)
|
||||
| AnyNodeRef::StmtAssign(_)
|
||||
| AnyNodeRef::StmtAugAssign(_)
|
||||
| AnyNodeRef::StmtCrement(_)
|
||||
| AnyNodeRef::StmtAnnAssign(_)
|
||||
| AnyNodeRef::StmtFor(_)
|
||||
| AnyNodeRef::StmtWhile(_)
|
||||
@@ -5754,6 +5816,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::StmtTypeAlias(_)
|
||||
| AnyNodeRef::StmtAssign(_)
|
||||
| AnyNodeRef::StmtAugAssign(_)
|
||||
| AnyNodeRef::StmtCrement(_)
|
||||
| AnyNodeRef::StmtAnnAssign(_)
|
||||
| AnyNodeRef::StmtFor(_)
|
||||
| AnyNodeRef::StmtWhile(_)
|
||||
@@ -5859,6 +5922,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::StmtTypeAlias(_)
|
||||
| AnyNodeRef::StmtAssign(_)
|
||||
| AnyNodeRef::StmtAugAssign(_)
|
||||
| AnyNodeRef::StmtCrement(_)
|
||||
| AnyNodeRef::StmtAnnAssign(_)
|
||||
| AnyNodeRef::StmtFor(_)
|
||||
| AnyNodeRef::StmtWhile(_)
|
||||
@@ -5949,6 +6013,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::StmtTypeAlias(_)
|
||||
| AnyNodeRef::StmtAssign(_)
|
||||
| AnyNodeRef::StmtAugAssign(_)
|
||||
| AnyNodeRef::StmtCrement(_)
|
||||
| AnyNodeRef::StmtAnnAssign(_)
|
||||
| AnyNodeRef::StmtFor(_)
|
||||
| AnyNodeRef::StmtWhile(_)
|
||||
@@ -6058,6 +6123,7 @@ impl<'a> AnyNodeRef<'a> {
|
||||
AnyNodeRef::StmtTypeAlias(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::StmtAssign(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::StmtAugAssign(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::StmtCrement(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::StmtAnnAssign(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::StmtFor(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::StmtWhile(node) => node.visit_preorder(visitor),
|
||||
@@ -6372,6 +6438,12 @@ impl<'a> From<&'a ast::StmtAugAssign> for AnyNodeRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::StmtCrement> for AnyNodeRef<'a> {
|
||||
fn from(node: &'a ast::StmtCrement) -> Self {
|
||||
AnyNodeRef::StmtCrement(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::StmtAnnAssign> for AnyNodeRef<'a> {
|
||||
fn from(node: &'a ast::StmtAnnAssign) -> Self {
|
||||
AnyNodeRef::StmtAnnAssign(node)
|
||||
@@ -6837,6 +6909,7 @@ impl<'a> From<&'a Stmt> for AnyNodeRef<'a> {
|
||||
Stmt::Break(node) => AnyNodeRef::StmtBreak(node),
|
||||
Stmt::Continue(node) => AnyNodeRef::StmtContinue(node),
|
||||
Stmt::IpyEscapeCommand(node) => AnyNodeRef::StmtIpyEscapeCommand(node),
|
||||
Stmt::Crement(node) => AnyNodeRef::StmtCrement(node),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7073,6 +7146,7 @@ impl Ranged for AnyNodeRef<'_> {
|
||||
AnyNodeRef::FString(node) => node.range(),
|
||||
AnyNodeRef::StringLiteral(node) => node.range(),
|
||||
AnyNodeRef::BytesLiteral(node) => node.range(),
|
||||
AnyNodeRef::StmtCrement(node) => node.range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7173,4 +7247,5 @@ pub enum NodeKind {
|
||||
FString,
|
||||
StringLiteral,
|
||||
BytesLiteral,
|
||||
StmtCrement,
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ pub enum Stmt {
|
||||
// Jupyter notebook specific
|
||||
#[is(name = "ipy_escape_command_stmt")]
|
||||
IpyEscapeCommand(StmtIpyEscapeCommand),
|
||||
#[is(name = "crement_stmt")]
|
||||
Crement(StmtCrement),
|
||||
}
|
||||
|
||||
/// An AST node used to represent a IPython escape command at the statement level.
|
||||
@@ -297,6 +299,26 @@ impl From<StmtAugAssign> for Stmt {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum CrementKind {
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
|
||||
/// `++` or `--`
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct StmtCrement {
|
||||
pub range: TextRange,
|
||||
pub target: Box<Expr>,
|
||||
pub op: CrementKind,
|
||||
}
|
||||
|
||||
impl From<StmtCrement> for Stmt {
|
||||
fn from(payload: StmtCrement) -> Self {
|
||||
Stmt::Crement(payload)
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [AnnAssign](https://docs.python.org/3/library/ast.html#ast.AnnAssign)
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct StmtAnnAssign {
|
||||
@@ -3693,6 +3715,11 @@ impl Ranged for crate::nodes::StmtAugAssign {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
impl Ranged for crate::nodes::StmtCrement {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
impl Ranged for crate::nodes::StmtAnnAssign {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
@@ -3816,6 +3843,7 @@ impl Ranged for crate::Stmt {
|
||||
Self::Break(node) => node.range(),
|
||||
Self::Continue(node) => node.range(),
|
||||
Stmt::IpyEscapeCommand(node) => node.range(),
|
||||
Stmt::Crement(node) => node.range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +203,14 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
visitor.visit_operator(op);
|
||||
visitor.visit_expr(target);
|
||||
}
|
||||
Stmt::Crement(ast::StmtCrement {
|
||||
target,
|
||||
op: _,
|
||||
range: _,
|
||||
}) => {
|
||||
// TODO(konstin): visitor.visit_operator(op);
|
||||
visitor.visit_expr(target);
|
||||
}
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
target,
|
||||
annotation,
|
||||
|
||||
@@ -209,6 +209,7 @@ where
|
||||
Stmt::TypeAlias(stmt) => stmt.visit_preorder(visitor),
|
||||
Stmt::Assign(stmt) => stmt.visit_preorder(visitor),
|
||||
Stmt::AugAssign(stmt) => stmt.visit_preorder(visitor),
|
||||
Stmt::Crement(stmt) => stmt.visit_preorder(visitor),
|
||||
Stmt::AnnAssign(stmt) => stmt.visit_preorder(visitor),
|
||||
Stmt::For(stmt) => stmt.visit_preorder(visitor),
|
||||
Stmt::While(stmt) => stmt.visit_preorder(visitor),
|
||||
|
||||
@@ -306,6 +306,7 @@ pub fn walk_stmt<V: Transformer + ?Sized>(visitor: &V, stmt: &mut Stmt) {
|
||||
Stmt::Nonlocal(_) => {}
|
||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => visitor.visit_expr(value),
|
||||
Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) | Stmt::IpyEscapeCommand(_) => {}
|
||||
Stmt::Crement(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ use std::ops::Deref;
|
||||
|
||||
use ruff_python_ast::str::Quote;
|
||||
use ruff_python_ast::{
|
||||
self as ast, Alias, ArgOrKeyword, BoolOp, CmpOp, Comprehension, ConversionFlag, DebugText,
|
||||
ExceptHandler, Expr, Identifier, MatchCase, Operator, Parameter, Parameters, Pattern,
|
||||
Singleton, Stmt, Suite, TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple,
|
||||
WithItem,
|
||||
self as ast, Alias, ArgOrKeyword, BoolOp, CmpOp, Comprehension, ConversionFlag, CrementKind,
|
||||
DebugText, ExceptHandler, Expr, Identifier, MatchCase, Operator, Parameter, Parameters,
|
||||
Pattern, Singleton, Stmt, Suite, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
|
||||
TypeParamTypeVarTuple, WithItem,
|
||||
};
|
||||
use ruff_python_ast::{ParameterWithDefault, TypeParams};
|
||||
use ruff_python_literal::escape::{AsciiEscape, Escape, UnicodeEscape};
|
||||
@@ -345,6 +345,20 @@ impl<'a> Generator<'a> {
|
||||
self.unparse_expr(value, precedence::AUG_ASSIGN);
|
||||
});
|
||||
}
|
||||
Stmt::Crement(ast::StmtCrement {
|
||||
target,
|
||||
op,
|
||||
range: _,
|
||||
}) => {
|
||||
statement!({
|
||||
self.unparse_expr(target, precedence::AUG_ASSIGN);
|
||||
self.p(" ");
|
||||
self.p(match op {
|
||||
CrementKind::Increment => "+= 1",
|
||||
CrementKind::Decrement => "-= 1",
|
||||
});
|
||||
});
|
||||
}
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
target,
|
||||
annotation,
|
||||
|
||||
@@ -706,7 +706,8 @@ impl Format<PyFormatContext<'_>> for FormatEnclosingNode<'_> {
|
||||
| AnyNodeRef::TypeParamTypeVar(_)
|
||||
| AnyNodeRef::TypeParamTypeVarTuple(_)
|
||||
| AnyNodeRef::TypeParamParamSpec(_)
|
||||
| AnyNodeRef::BytesLiteral(_) => {
|
||||
| AnyNodeRef::BytesLiteral(_)
|
||||
| AnyNodeRef::StmtCrement(_) => {
|
||||
panic!("Range formatting only supports formatting logical lines")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,19 @@ impl FormatRule<Stmt, PyFormatContext<'_>> for FormatStmt {
|
||||
Stmt::Delete(x) => x.format().fmt(f),
|
||||
Stmt::Assign(x) => x.format().fmt(f),
|
||||
Stmt::AugAssign(x) => x.format().fmt(f),
|
||||
Stmt::Crement(x) => {
|
||||
x.target.format().fmt(f)?;
|
||||
use ruff_formatter::prelude::*;
|
||||
match x.op {
|
||||
ruff_python_ast::CrementKind::Increment => {
|
||||
ruff_formatter::write!(f, [space(), text("+="), space(), text("1")])?;
|
||||
}
|
||||
ruff_python_ast::CrementKind::Decrement => {
|
||||
ruff_formatter::write!(f, [space(), text("-="), space(), text("1")])?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Stmt::AnnAssign(x) => x.format().fmt(f),
|
||||
Stmt::For(x) => x.format().fmt(f),
|
||||
Stmt::While(x) => x.format().fmt(f),
|
||||
|
||||
@@ -1079,7 +1079,9 @@ impl<'source> Lexer<'source> {
|
||||
}
|
||||
}
|
||||
'+' => {
|
||||
if self.cursor.eat_char('=') {
|
||||
if self.cursor.eat_char('+') {
|
||||
Tok::Increment
|
||||
} else if self.cursor.eat_char('=') {
|
||||
Tok::PlusEqual
|
||||
} else {
|
||||
Tok::Plus
|
||||
@@ -1166,7 +1168,9 @@ impl<'source> Lexer<'source> {
|
||||
}
|
||||
}
|
||||
'-' => {
|
||||
if self.cursor.eat_char('=') {
|
||||
if self.cursor.eat_char('-') {
|
||||
Tok::Increment
|
||||
} else if self.cursor.eat_char('=') {
|
||||
Tok::MinusEqual
|
||||
} else if self.cursor.eat_char('>') {
|
||||
Tok::Rarrow
|
||||
|
||||
@@ -110,6 +110,16 @@ DelStatement: ast::Stmt = {
|
||||
};
|
||||
|
||||
ExpressionStatement: ast::Stmt = {
|
||||
<location:@L> <target:TestOrStarExprList> <op:Crement> <end_location:@R> =>? {
|
||||
invalid::assignment_target(&target.expr)?;
|
||||
Ok(ast::Stmt::Crement(
|
||||
ast::StmtCrement {
|
||||
target: Box::new(set_context(target.into(), ast::ExprContext::Store)),
|
||||
op,
|
||||
range: (location..end_location).into()
|
||||
},
|
||||
))
|
||||
},
|
||||
<location:@L> <expression:TestOrStarExprList> <suffix:AssignSuffix*> <end_location:@R> =>? {
|
||||
// Just an expression, no assignment:
|
||||
if suffix.is_empty() {
|
||||
@@ -204,6 +214,11 @@ AugAssign: ast::Operator = {
|
||||
"//=" => ast::Operator::FloorDiv,
|
||||
};
|
||||
|
||||
Crement: ast::CrementKind = {
|
||||
"++" => ast::CrementKind::Increment,
|
||||
"--" => ast::CrementKind::Decrement,
|
||||
};
|
||||
|
||||
FlowStatement: ast::Stmt = {
|
||||
<location:@L> "break" <end_location:@R> => {
|
||||
|
||||
@@ -2033,6 +2048,8 @@ extern {
|
||||
">" => token::Tok::Greater,
|
||||
">=" => token::Tok::GreaterEqual,
|
||||
"->" => token::Tok::Rarrow,
|
||||
"++" => token::Tok::Increment,
|
||||
"--" => token::Tok::Decrement,
|
||||
"and" => token::Tok::And,
|
||||
"as" => token::Tok::As,
|
||||
"assert" => token::Tok::Assert,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -157,6 +157,10 @@ pub enum Tok {
|
||||
VbarEqual,
|
||||
/// Token value for caret equal `^=`.
|
||||
CircumflexEqual,
|
||||
/// Token value for `++`.
|
||||
Increment,
|
||||
/// Token value for `--`.
|
||||
Decrement,
|
||||
/// Token value for left shift equal `<<=`.
|
||||
LeftShiftEqual,
|
||||
/// Token value for right shift equal `>>=`.
|
||||
@@ -343,6 +347,8 @@ impl fmt::Display for Tok {
|
||||
With => f.write_str("'with'"),
|
||||
Yield => f.write_str("'yield'"),
|
||||
ColonEqual => f.write_str("':='"),
|
||||
Increment => f.write_str("++"),
|
||||
Decrement => f.write_str("--"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -526,6 +532,8 @@ pub enum TokenKind {
|
||||
StartModule,
|
||||
StartInteractive,
|
||||
StartExpression,
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
|
||||
impl TokenKind {
|
||||
@@ -620,6 +628,8 @@ impl TokenKind {
|
||||
| TokenKind::AmperEqual
|
||||
| TokenKind::VbarEqual
|
||||
| TokenKind::CircumflexEqual
|
||||
| TokenKind::Increment
|
||||
| TokenKind::Decrement
|
||||
| TokenKind::LeftShiftEqual
|
||||
| TokenKind::RightShiftEqual
|
||||
| TokenKind::DoubleStarEqual
|
||||
@@ -799,6 +809,8 @@ impl TokenKind {
|
||||
Tok::Yield => TokenKind::Yield,
|
||||
Tok::StartModule => TokenKind::StartModule,
|
||||
Tok::StartExpression => TokenKind::StartExpression,
|
||||
Tok::Increment => TokenKind::Increment,
|
||||
Tok::Decrement => TokenKind::Decrement,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +75,6 @@ impl<'a> Binding<'a> {
|
||||
self.flags.intersects(BindingFlags::GLOBAL)
|
||||
}
|
||||
|
||||
/// Return `true` if this [`Binding`] was deleted.
|
||||
pub const fn is_deleted(&self) -> bool {
|
||||
self.flags.intersects(BindingFlags::DELETED)
|
||||
}
|
||||
|
||||
/// Return `true` if this [`Binding`] represents an assignment to `__all__` with an invalid
|
||||
/// value (e.g., `__all__ = "Foo"`).
|
||||
pub const fn is_invalid_all_format(&self) -> bool {
|
||||
@@ -170,7 +165,6 @@ impl<'a> Binding<'a> {
|
||||
// Deletions, annotations, `__future__` imports, and builtins are never considered
|
||||
// redefinitions.
|
||||
BindingKind::Deletion
|
||||
| BindingKind::ConditionalDeletion(_)
|
||||
| BindingKind::Annotation
|
||||
| BindingKind::FutureImport
|
||||
| BindingKind::Builtin => {
|
||||
@@ -271,19 +265,6 @@ bitflags! {
|
||||
/// ```
|
||||
const GLOBAL = 1 << 4;
|
||||
|
||||
/// The binding was deleted (i.e., the target of a `del` statement).
|
||||
///
|
||||
/// For example, the binding could be `x` in:
|
||||
/// ```python
|
||||
/// del x
|
||||
/// ```
|
||||
///
|
||||
/// The semantic model will typically shadow a deleted binding via an additional binding
|
||||
/// with [`BindingKind::Deletion`]; however, conditional deletions (e.g.,
|
||||
/// `if condition: del x`) do _not_ generate a shadow binding. This flag is thus used to
|
||||
/// detect whether a binding was _ever_ deleted, even conditionally.
|
||||
const DELETED = 1 << 5;
|
||||
|
||||
/// The binding represents an export via `__all__`, but the assigned value uses an invalid
|
||||
/// expression (i.e., a non-container type).
|
||||
///
|
||||
@@ -291,7 +272,7 @@ bitflags! {
|
||||
/// ```python
|
||||
/// __all__ = 1
|
||||
/// ```
|
||||
const INVALID_ALL_FORMAT = 1 << 6;
|
||||
const INVALID_ALL_FORMAT = 1 << 5;
|
||||
|
||||
/// The binding represents an export via `__all__`, but the assigned value contains an
|
||||
/// invalid member (i.e., a non-string).
|
||||
@@ -300,7 +281,7 @@ bitflags! {
|
||||
/// ```python
|
||||
/// __all__ = [1]
|
||||
/// ```
|
||||
const INVALID_ALL_OBJECT = 1 << 7;
|
||||
const INVALID_ALL_OBJECT = 1 << 6;
|
||||
|
||||
/// The binding represents a private declaration.
|
||||
///
|
||||
@@ -308,7 +289,7 @@ bitflags! {
|
||||
/// ```python
|
||||
/// _T = "This is a private variable"
|
||||
/// ```
|
||||
const PRIVATE_DECLARATION = 1 << 8;
|
||||
const PRIVATE_DECLARATION = 1 << 7;
|
||||
|
||||
/// The binding represents an unpacked assignment.
|
||||
///
|
||||
@@ -316,7 +297,7 @@ bitflags! {
|
||||
/// ```python
|
||||
/// (x, y) = 1, 2
|
||||
/// ```
|
||||
const UNPACKED_ASSIGNMENT = 1 << 9;
|
||||
const UNPACKED_ASSIGNMENT = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,13 +512,6 @@ pub enum BindingKind<'a> {
|
||||
/// ```
|
||||
Deletion,
|
||||
|
||||
/// A binding for a deletion, like `x` in:
|
||||
/// ```python
|
||||
/// if x > 0:
|
||||
/// del x
|
||||
/// ```
|
||||
ConditionalDeletion(BindingId),
|
||||
|
||||
/// A binding to bind an exception to a local variable, like `x` in:
|
||||
/// ```python
|
||||
/// try:
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_python_ast::helpers::from_relative_import;
|
||||
use ruff_python_ast::name::{QualifiedName, UnqualifiedName};
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext, Operator, Stmt};
|
||||
use ruff_python_ast::{self as ast, Expr, Operator, Stmt};
|
||||
use ruff_python_stdlib::path::is_python_stub_file;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
@@ -271,7 +271,7 @@ impl<'a> SemanticModel<'a> {
|
||||
.get(symbol)
|
||||
.map_or(true, |binding_id| {
|
||||
// Treat the deletion of a name as a reference to that name.
|
||||
self.add_local_reference(binding_id, ExprContext::Del, range);
|
||||
self.add_local_reference(binding_id, range);
|
||||
self.bindings[binding_id].is_unbound()
|
||||
});
|
||||
|
||||
@@ -296,9 +296,8 @@ impl<'a> SemanticModel<'a> {
|
||||
let reference_id = self.resolved_references.push(
|
||||
ScopeId::global(),
|
||||
self.node_id,
|
||||
ExprContext::Load,
|
||||
self.flags,
|
||||
name.range,
|
||||
self.flags,
|
||||
);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
|
||||
@@ -309,9 +308,8 @@ impl<'a> SemanticModel<'a> {
|
||||
let reference_id = self.resolved_references.push(
|
||||
ScopeId::global(),
|
||||
self.node_id,
|
||||
ExprContext::Load,
|
||||
self.flags,
|
||||
name.range,
|
||||
self.flags,
|
||||
);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
}
|
||||
@@ -367,9 +365,8 @@ impl<'a> SemanticModel<'a> {
|
||||
let reference_id = self.resolved_references.push(
|
||||
self.scope_id,
|
||||
self.node_id,
|
||||
ExprContext::Load,
|
||||
self.flags,
|
||||
name.range,
|
||||
self.flags,
|
||||
);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
|
||||
@@ -380,9 +377,8 @@ impl<'a> SemanticModel<'a> {
|
||||
let reference_id = self.resolved_references.push(
|
||||
self.scope_id,
|
||||
self.node_id,
|
||||
ExprContext::Load,
|
||||
self.flags,
|
||||
name.range,
|
||||
self.flags,
|
||||
);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
}
|
||||
@@ -430,15 +426,6 @@ impl<'a> SemanticModel<'a> {
|
||||
return ReadResult::UnboundLocal(binding_id);
|
||||
}
|
||||
|
||||
BindingKind::ConditionalDeletion(binding_id) => {
|
||||
self.unresolved_references.push(
|
||||
name.range,
|
||||
self.exceptions(),
|
||||
UnresolvedReferenceFlags::empty(),
|
||||
);
|
||||
return ReadResult::UnboundLocal(binding_id);
|
||||
}
|
||||
|
||||
// If we hit an unbound exception that shadowed a bound name, resole to the
|
||||
// bound name. For example, given:
|
||||
//
|
||||
@@ -459,9 +446,8 @@ impl<'a> SemanticModel<'a> {
|
||||
let reference_id = self.resolved_references.push(
|
||||
self.scope_id,
|
||||
self.node_id,
|
||||
ExprContext::Load,
|
||||
self.flags,
|
||||
name.range,
|
||||
self.flags,
|
||||
);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
|
||||
@@ -472,9 +458,8 @@ impl<'a> SemanticModel<'a> {
|
||||
let reference_id = self.resolved_references.push(
|
||||
self.scope_id,
|
||||
self.node_id,
|
||||
ExprContext::Load,
|
||||
self.flags,
|
||||
name.range,
|
||||
self.flags,
|
||||
);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
}
|
||||
@@ -563,7 +548,6 @@ impl<'a> SemanticModel<'a> {
|
||||
match self.bindings[binding_id].kind {
|
||||
BindingKind::Annotation => continue,
|
||||
BindingKind::Deletion | BindingKind::UnboundException(None) => return None,
|
||||
BindingKind::ConditionalDeletion(binding_id) => return Some(binding_id),
|
||||
BindingKind::UnboundException(Some(binding_id)) => return Some(binding_id),
|
||||
_ => return Some(binding_id),
|
||||
}
|
||||
@@ -1331,28 +1315,18 @@ impl<'a> SemanticModel<'a> {
|
||||
}
|
||||
|
||||
/// Add a reference to the given [`BindingId`] in the local scope.
|
||||
pub fn add_local_reference(
|
||||
&mut self,
|
||||
binding_id: BindingId,
|
||||
ctx: ExprContext,
|
||||
range: TextRange,
|
||||
) {
|
||||
pub fn add_local_reference(&mut self, binding_id: BindingId, range: TextRange) {
|
||||
let reference_id =
|
||||
self.resolved_references
|
||||
.push(self.scope_id, self.node_id, ctx, self.flags, range);
|
||||
.push(self.scope_id, self.node_id, range, self.flags);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
}
|
||||
|
||||
/// Add a reference to the given [`BindingId`] in the global scope.
|
||||
pub fn add_global_reference(
|
||||
&mut self,
|
||||
binding_id: BindingId,
|
||||
ctx: ExprContext,
|
||||
range: TextRange,
|
||||
) {
|
||||
pub fn add_global_reference(&mut self, binding_id: BindingId, range: TextRange) {
|
||||
let reference_id =
|
||||
self.resolved_references
|
||||
.push(ScopeId::global(), self.node_id, ctx, self.flags, range);
|
||||
.push(ScopeId::global(), self.node_id, range, self.flags);
|
||||
self.bindings[binding_id].references.push(reference_id);
|
||||
}
|
||||
|
||||
@@ -1726,6 +1700,7 @@ bitflags! {
|
||||
/// only required by the Python interpreter, but by runtime type checkers too.
|
||||
const RUNTIME_REQUIRED_ANNOTATION = 1 << 2;
|
||||
|
||||
|
||||
/// The model is in a type definition.
|
||||
///
|
||||
/// For example, the model could be visiting `int` in:
|
||||
@@ -1911,6 +1886,7 @@ bitflags! {
|
||||
/// ```
|
||||
const COMPREHENSION_ASSIGNMENT = 1 << 19;
|
||||
|
||||
|
||||
/// The model is in a module / class / function docstring.
|
||||
///
|
||||
/// For example, the model could be visiting either the module, class,
|
||||
|
||||
@@ -3,10 +3,10 @@ use std::ops::Deref;
|
||||
use bitflags::bitflags;
|
||||
|
||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||
use ruff_python_ast::ExprContext;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::context::ExecutionContext;
|
||||
use crate::scope::ScopeId;
|
||||
use crate::{Exceptions, NodeId, SemanticModelFlags};
|
||||
|
||||
@@ -18,12 +18,10 @@ pub struct ResolvedReference {
|
||||
node_id: Option<NodeId>,
|
||||
/// The scope in which the reference is defined.
|
||||
scope_id: ScopeId,
|
||||
/// The expression context in which the reference occurs (e.g., `Load`, `Store`, `Del`).
|
||||
ctx: ExprContext,
|
||||
/// The model state in which the reference occurs.
|
||||
flags: SemanticModelFlags,
|
||||
/// The range of the reference in the source code.
|
||||
range: TextRange,
|
||||
/// The model state in which the reference occurs.
|
||||
flags: SemanticModelFlags,
|
||||
}
|
||||
|
||||
impl ResolvedReference {
|
||||
@@ -37,19 +35,13 @@ impl ResolvedReference {
|
||||
self.scope_id
|
||||
}
|
||||
|
||||
/// Return `true` if the reference occurred in a `Load` operation.
|
||||
pub const fn is_load(&self) -> bool {
|
||||
self.ctx.is_load()
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a typing context.
|
||||
pub const fn in_typing_context(&self) -> bool {
|
||||
self.flags.intersects(SemanticModelFlags::TYPING_CONTEXT)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a runtime context.
|
||||
pub const fn in_runtime_context(&self) -> bool {
|
||||
!self.flags.intersects(SemanticModelFlags::TYPING_CONTEXT)
|
||||
/// The [`ExecutionContext`] of the reference.
|
||||
pub const fn context(&self) -> ExecutionContext {
|
||||
if self.flags.intersects(SemanticModelFlags::TYPING_CONTEXT) {
|
||||
ExecutionContext::Typing
|
||||
} else {
|
||||
ExecutionContext::Runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a typing-only type annotation.
|
||||
@@ -116,16 +108,14 @@ impl ResolvedReferences {
|
||||
&mut self,
|
||||
scope_id: ScopeId,
|
||||
node_id: Option<NodeId>,
|
||||
ctx: ExprContext,
|
||||
flags: SemanticModelFlags,
|
||||
range: TextRange,
|
||||
flags: SemanticModelFlags,
|
||||
) -> ResolvedReferenceId {
|
||||
self.0.push(ResolvedReference {
|
||||
node_id,
|
||||
scope_id,
|
||||
ctx,
|
||||
flags,
|
||||
range,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -14,7 +14,7 @@ Ruff can be used as a [pre-commit](https://pre-commit.com) hook via [`ruff-pre-c
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.3
|
||||
rev: v0.3.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -27,7 +27,7 @@ To enable lint fixes, add the `--fix` argument to the lint hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.3
|
||||
rev: v0.3.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -41,7 +41,7 @@ To run the hooks over Jupyter Notebooks too, add `jupyter` to the list of allowe
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.3
|
||||
rev: v0.3.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
|
||||
readme = "README.md"
|
||||
|
||||
1
ruff.schema.json
generated
1
ruff.schema.json
generated
@@ -3330,7 +3330,6 @@
|
||||
"PLW012",
|
||||
"PLW0120",
|
||||
"PLW0127",
|
||||
"PLW0128",
|
||||
"PLW0129",
|
||||
"PLW013",
|
||||
"PLW0131",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "scripts"
|
||||
version = "0.3.3"
|
||||
version = "0.3.2"
|
||||
description = ""
|
||||
authors = ["Charles Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user