Implement E721 (#193)

This commit is contained in:
Charlie Marsh
2022-09-14 21:10:29 -04:00
committed by GitHub
parent 2e1eb84cbf
commit c0cb73ab16
10 changed files with 217 additions and 8 deletions

View File

@@ -476,6 +476,46 @@ pub fn check_is_literal(
checks
}
/// Check TypeComparison compliance.
pub fn check_type_comparison(
ops: &Vec<Cmpop>,
comparators: &Vec<Expr>,
location: Location,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
for (op, right) in izip!(ops, comparators) {
if matches!(op, Cmpop::Is | Cmpop::IsNot | Cmpop::Eq | Cmpop::NotEq) {
match &right.node {
ExprKind::Call { func, args, .. } => {
if let ExprKind::Name { id, .. } = &func.node {
// Ex) type(False)
if id == "type" {
if let Some(arg) = args.first() {
// Allow comparison for types which are not obvious.
if !matches!(arg.node, ExprKind::Name { .. }) {
checks.push(Check::new(CheckKind::TypeComparison, location));
}
}
}
}
}
ExprKind::Attribute { value, .. } => {
if let ExprKind::Name { id, .. } = &value.node {
// Ex) types.IntType
if id == "types" {
checks.push(Check::new(CheckKind::TypeComparison, location));
}
}
}
_ => {}
}
}
}
checks
}
/// Check TwoStarredExpressions and TooManyExpressionsInStarredAssignment compliance.
pub fn check_starred_expressions(
elts: &[Expr],

View File

@@ -746,6 +746,14 @@ where
expr.location,
));
}
if self.settings.select.contains(&CheckCode::E721) {
self.checks.extend(checks::check_type_comparison(
ops,
comparators,
expr.location,
));
}
}
ExprKind::Constant {
value: Constant::Str(value),
@@ -813,7 +821,7 @@ where
} else if match_name_or_attr(func, "NamedTuple") {
self.visit_expr(func);
// NamedTuple("a", [("a", int)])
// Ex) NamedTuple("a", [("a", int)])
if args.len() > 1 {
match &args[1].node {
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
@@ -837,7 +845,7 @@ where
}
}
// NamedTuple("a", a=int)
// Ex) NamedTuple("a", a=int)
for keyword in keywords {
let KeywordData { value, .. } = &keyword.node;
self.visit_annotation(value);
@@ -845,7 +853,7 @@ where
} else if match_name_or_attr(func, "TypedDict") {
self.visit_expr(func);
// TypedDict("a", {"a": int})
// Ex) TypedDict("a", {"a": int})
if args.len() > 1 {
if let ExprKind::Dict { keys, values } = &args[1].node {
for key in keys {
@@ -859,7 +867,7 @@ where
}
}
// TypedDict("a", a=int)
// Ex) TypedDict("a", a=int)
for keyword in keywords {
let KeywordData { value, .. } = &keyword.node;
self.visit_annotation(value);

View File

@@ -14,6 +14,7 @@ pub enum CheckCode {
E712,
E713,
E714,
E721,
E722,
E731,
E741,
@@ -60,8 +61,9 @@ impl FromStr for CheckCode {
"E711" => Ok(CheckCode::E711),
"E712" => Ok(CheckCode::E712),
"E713" => Ok(CheckCode::E713),
"E722" => Ok(CheckCode::E722),
"E714" => Ok(CheckCode::E714),
"E721" => Ok(CheckCode::E721),
"E722" => Ok(CheckCode::E722),
"E731" => Ok(CheckCode::E731),
"E741" => Ok(CheckCode::E741),
"E742" => Ok(CheckCode::E742),
@@ -86,6 +88,7 @@ impl FromStr for CheckCode {
"F704" => Ok(CheckCode::F704),
"F706" => Ok(CheckCode::F706),
"F707" => Ok(CheckCode::F707),
"F722" => Ok(CheckCode::F722),
"F821" => Ok(CheckCode::F821),
"F822" => Ok(CheckCode::F822),
"F823" => Ok(CheckCode::F823),
@@ -108,6 +111,7 @@ impl CheckCode {
CheckCode::E712 => "E712",
CheckCode::E713 => "E713",
CheckCode::E714 => "E714",
CheckCode::E721 => "E721",
CheckCode::E722 => "E722",
CheckCode::E731 => "E731",
CheckCode::E741 => "E741",
@@ -203,6 +207,7 @@ pub enum CheckKind {
TooManyExpressionsInStarredAssignment,
TrueFalseComparison(bool, RejectedCmpop),
TwoStarredExpressions,
TypeComparison,
UndefinedExport(String),
UndefinedLocal(String),
UndefinedName(String),
@@ -251,6 +256,7 @@ impl CheckKind {
}
CheckKind::TrueFalseComparison(_, _) => "TrueFalseComparison",
CheckKind::TwoStarredExpressions => "TwoStarredExpressions",
CheckKind::TypeComparison => "TypeComparison",
CheckKind::UndefinedExport(_) => "UndefinedExport",
CheckKind::UndefinedLocal(_) => "UndefinedLocal",
CheckKind::UndefinedName(_) => "UndefinedName",
@@ -297,6 +303,7 @@ impl CheckKind {
CheckKind::TooManyExpressionsInStarredAssignment => &CheckCode::F621,
CheckKind::TrueFalseComparison(_, _) => &CheckCode::E712,
CheckKind::TwoStarredExpressions => &CheckCode::F622,
CheckKind::TypeComparison => &CheckCode::E721,
CheckKind::UndefinedExport(_) => &CheckCode::F822,
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
CheckKind::UndefinedName(_) => &CheckCode::F821,
@@ -409,6 +416,7 @@ impl CheckKind {
},
},
CheckKind::TwoStarredExpressions => "two starred expressions in assignment".to_string(),
CheckKind::TypeComparison => "do not compare types, use `isinstance()`".to_string(),
CheckKind::UndefinedExport(name) => {
format!("Undefined name `{name}` in `__all__`")
}

View File

@@ -240,6 +240,108 @@ mod tests {
Ok(())
}
#[test]
fn e721() -> Result<()> {
let mut actual = check_path(
Path::new("./resources/test/fixtures/E721.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::E721]),
},
&fixer::Mode::Generate,
)?;
actual.sort_by_key(|check| check.location);
let expected = vec![
Check {
kind: CheckKind::TypeComparison,
location: Location::new(2, 14),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(5, 14),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(10, 8),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(15, 14),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(18, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(18, 46),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(20, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(22, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(24, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(26, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(28, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(30, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(32, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(34, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(40, 18),
fix: None,
},
Check {
kind: CheckKind::TypeComparison,
location: Location::new(42, 18),
fix: None,
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn e722() -> Result<()> {
let mut actual = check_path(

View File

@@ -266,6 +266,7 @@ other-attribute = 1
CheckCode::E712,
CheckCode::E713,
CheckCode::E714,
CheckCode::E721,
CheckCode::E722,
CheckCode::E731,
CheckCode::E741,

View File

@@ -49,6 +49,7 @@ impl Settings {
CheckCode::E712,
CheckCode::E713,
CheckCode::E714,
CheckCode::E721,
CheckCode::E722,
CheckCode::E731,
CheckCode::E741,