feat(B032): add b032 flake8_bugbear (#3085)
This commit is contained in:
29
crates/ruff/resources/test/fixtures/flake8_bugbear/B032.py
vendored
Normal file
29
crates/ruff/resources/test/fixtures/flake8_bugbear/B032.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Should emit:
|
||||
B032 - on lines 9, 10, 12, 13, 16-19
|
||||
"""
|
||||
|
||||
# Flag these
|
||||
dct = {"a": 1}
|
||||
|
||||
dct["b"]: 2
|
||||
dct.b: 2
|
||||
|
||||
dct["b"]: "test"
|
||||
dct.b: "test"
|
||||
|
||||
test = "test"
|
||||
dct["b"]: test
|
||||
dct["b"]: test.lower()
|
||||
dct.b: test
|
||||
dct.b: test.lower()
|
||||
|
||||
# Do not flag below
|
||||
typed_dct: dict[str, int] = {"a": 1}
|
||||
typed_dct["b"] = 2
|
||||
typed_dct.b = 2
|
||||
|
||||
|
||||
class TestClass:
|
||||
def test_self(self):
|
||||
self.test: int
|
||||
@@ -1842,6 +1842,18 @@ where
|
||||
pycodestyle::rules::lambda_assignment(self, target, value, stmt);
|
||||
}
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UnintentionalTypeAnnotation)
|
||||
{
|
||||
flake8_bugbear::rules::unintentional_type_annotation(
|
||||
self,
|
||||
target,
|
||||
value.as_deref(),
|
||||
stmt,
|
||||
);
|
||||
}
|
||||
}
|
||||
StmtKind::Delete { .. } => {}
|
||||
StmtKind::Expr { value, .. } => {
|
||||
|
||||
@@ -180,6 +180,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
|
||||
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
|
||||
(Flake8Bugbear, "029") => Rule::ExceptWithEmptyTuple,
|
||||
(Flake8Bugbear, "032") => Rule::UnintentionalTypeAnnotation,
|
||||
(Flake8Bugbear, "904") => Rule::RaiseWithoutFromInsideExcept,
|
||||
(Flake8Bugbear, "905") => Rule::ZipWithoutExplicitStrict,
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ ruff_macros::register_rules!(
|
||||
rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept,
|
||||
rules::flake8_bugbear::rules::ZipWithoutExplicitStrict,
|
||||
rules::flake8_bugbear::rules::ExceptWithEmptyTuple,
|
||||
rules::flake8_bugbear::rules::UnintentionalTypeAnnotation,
|
||||
// flake8-blind-except
|
||||
rules::flake8_blind_except::rules::BlindExcept,
|
||||
// flake8-comprehensions
|
||||
|
||||
@@ -42,6 +42,7 @@ mod tests {
|
||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.py"); "B027")]
|
||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.pyi"); "B027_pyi")]
|
||||
#[test_case(Rule::ExceptWithEmptyTuple, Path::new("B029.py"); "B029")]
|
||||
#[test_case(Rule::UnintentionalTypeAnnotation, Path::new("B032.py"); "B032")]
|
||||
#[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"); "B904")]
|
||||
#[test_case(Rule::ZipWithoutExplicitStrict, Path::new("B905.py"); "B905")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
|
||||
@@ -41,6 +41,10 @@ pub use useless_contextlib_suppress::{useless_contextlib_suppress, UselessContex
|
||||
pub use useless_expression::{useless_expression, UselessExpression};
|
||||
pub use zip_without_explicit_strict::{zip_without_explicit_strict, ZipWithoutExplicitStrict};
|
||||
|
||||
pub use unintentional_type_annotation::{
|
||||
unintentional_type_annotation, UnintentionalTypeAnnotation,
|
||||
};
|
||||
|
||||
mod abstract_base_class;
|
||||
mod assert_false;
|
||||
mod assert_raises_exception;
|
||||
@@ -62,6 +66,7 @@ mod setattr_with_constant;
|
||||
mod star_arg_unpacking_after_keyword_arg;
|
||||
mod strip_with_multi_characters;
|
||||
mod unary_prefix_increment;
|
||||
mod unintentional_type_annotation;
|
||||
mod unreliable_callable_check;
|
||||
mod unused_loop_control_variable;
|
||||
mod useless_comparison;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
use rustpython_parser::ast::{Expr, ExprKind, Stmt};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
/// ## What it does
|
||||
/// Checks for the unintentional use of type annotations.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The use of a colon (`:`) in lieu of an assignment (`=`) can be syntactically valid, but
|
||||
/// is almost certainly a mistake when used in a subscript or attribute assignment.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a["b"]: 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a["b"] = 1
|
||||
/// ```
|
||||
pub struct UnintentionalTypeAnnotation;
|
||||
);
|
||||
impl Violation for UnintentionalTypeAnnotation {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"Possible unintentional type annotation (using `:`). Did you mean to assign (using `=`)?"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// B032
|
||||
pub fn unintentional_type_annotation(
|
||||
checker: &mut Checker,
|
||||
target: &Expr,
|
||||
value: Option<&Expr>,
|
||||
stmt: &Stmt,
|
||||
) {
|
||||
if value.is_some() {
|
||||
return;
|
||||
}
|
||||
match &target.node {
|
||||
ExprKind::Subscript { value, .. } => {
|
||||
if matches!(&value.node, ExprKind::Name { .. }) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
UnintentionalTypeAnnotation,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
ExprKind::Attribute { value, .. } => {
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
if id != "self" {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
UnintentionalTypeAnnotation,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 11
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 13
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 16
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 14
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 17
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
column: 22
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 18
|
||||
column: 0
|
||||
end_location:
|
||||
row: 18
|
||||
column: 11
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
UnintentionalTypeAnnotation: ~
|
||||
location:
|
||||
row: 19
|
||||
column: 0
|
||||
end_location:
|
||||
row: 19
|
||||
column: 19
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
2
ruff.schema.json
generated
2
ruff.schema.json
generated
@@ -1430,6 +1430,8 @@
|
||||
"B026",
|
||||
"B027",
|
||||
"B029",
|
||||
"B03",
|
||||
"B032",
|
||||
"B9",
|
||||
"B90",
|
||||
"B904",
|
||||
|
||||
Reference in New Issue
Block a user