Implement R002 (NoAssertEquals) (#98)

This commit is contained in:
Charlie Marsh
2022-09-05 12:27:47 -04:00
committed by GitHub
parent 79b6472c7c
commit 198e5cf27f
9 changed files with 126 additions and 17 deletions

View File

@@ -2,8 +2,8 @@ use std::collections::BTreeSet;
use std::path::Path;
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Stmt,
StmtKind, Suite,
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
Location, Stmt, StmtKind, Suite,
};
use rustpython_parser::parser;
@@ -11,7 +11,7 @@ use crate::ast_ops::{
extract_all_names, Binding, BindingKind, Scope, ScopeKind, SourceCodeLocator,
};
use crate::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::checks::{Check, CheckCode, CheckKind};
use crate::checks::{Check, CheckCode, CheckKind, Fix};
use crate::settings::Settings;
use crate::visitor::{walk_excepthandler, Visitor};
use crate::{autofix, fixer, visitor};
@@ -132,7 +132,7 @@ impl Visitor for Checker<'_> {
decorator_list,
..
} => {
if self.settings.select.contains(&CheckCode::R0205) {
if self.settings.select.contains(&CheckCode::R001) {
for expr in bases {
if let ExprKind::Name { id, .. } = &expr.node {
if id == "object" {
@@ -159,6 +159,7 @@ impl Visitor for Checker<'_> {
) {
check.amend(fix);
}
} else {
}
self.checks.push(check);
}
@@ -442,6 +443,37 @@ impl Visitor for Checker<'_> {
ExprContext::Store => self.handle_node_store(expr, parent),
ExprContext::Del => self.handle_node_delete(expr),
},
ExprKind::Call { func, .. } => {
if self.settings.select.contains(&CheckCode::R002) {
if let ExprKind::Attribute { value, attr, .. } = &func.node {
if attr == "assertEquals" {
if let ExprKind::Name { id, .. } = &value.node {
if id == "self" {
let mut check =
Check::new(CheckKind::NoAssertEquals, expr.location);
if matches!(self.autofix, autofix::Mode::Generate)
|| matches!(self.autofix, autofix::Mode::Apply)
{
check.amend(Fix {
content: "assertEqual".to_string(),
start: Location::new(
func.location.row(),
func.location.column() + 1,
),
end: Location::new(
func.location.row(),
func.location.column() + 1 + "assertEquals".len(),
),
applied: false,
});
}
self.checks.push(check);
}
}
}
}
}
}
ExprKind::GeneratorExp { .. }
| ExprKind::ListComp { .. }
| ExprKind::DictComp { .. }

View File

@@ -24,7 +24,8 @@ pub enum CheckCode {
F831,
F841,
F901,
R0205,
R001,
R002,
}
impl FromStr for CheckCode {
@@ -48,7 +49,8 @@ impl FromStr for CheckCode {
"F831" => Ok(CheckCode::F831),
"F841" => Ok(CheckCode::F841),
"F901" => Ok(CheckCode::F901),
"R0205" => Ok(CheckCode::R0205),
"R001" => Ok(CheckCode::R001),
"R002" => Ok(CheckCode::R002),
_ => Err(anyhow::anyhow!("Unknown check code: {s}")),
}
}
@@ -68,12 +70,13 @@ impl CheckCode {
CheckCode::F706 => "F706",
CheckCode::F707 => "F707",
CheckCode::F821 => "F821",
CheckCode::F823 => "F823",
CheckCode::F822 => "F822",
CheckCode::F823 => "F823",
CheckCode::F831 => "F831",
CheckCode::F841 => "F841",
CheckCode::F901 => "F901",
CheckCode::R0205 => "R0205",
CheckCode::R001 => "R001",
CheckCode::R002 => "R002",
}
}
@@ -96,7 +99,8 @@ impl CheckCode {
CheckCode::F831 => &LintSource::AST,
CheckCode::F841 => &LintSource::AST,
CheckCode::F901 => &LintSource::AST,
CheckCode::R0205 => &LintSource::AST,
CheckCode::R001 => &LintSource::AST,
CheckCode::R002 => &LintSource::AST,
}
}
}
@@ -117,6 +121,7 @@ pub enum CheckKind {
ImportStarUsage,
LineTooLong,
ModuleImportNotAtTopOfFile,
NoAssertEquals,
RaiseNotImplemented,
ReturnOutsideFunction,
UndefinedExport(String),
@@ -140,6 +145,7 @@ impl CheckKind {
CheckKind::ImportStarUsage => "ImportStarUsage",
CheckKind::LineTooLong => "LineTooLong",
CheckKind::ModuleImportNotAtTopOfFile => "ModuleImportNotAtTopOfFile",
CheckKind::NoAssertEquals => "NoAssertEquals",
CheckKind::RaiseNotImplemented => "RaiseNotImplemented",
CheckKind::ReturnOutsideFunction => "ReturnOutsideFunction",
CheckKind::UndefinedExport(_) => "UndefinedExport",
@@ -163,6 +169,7 @@ impl CheckKind {
CheckKind::ImportStarUsage => &CheckCode::F403,
CheckKind::LineTooLong => &CheckCode::E501,
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
CheckKind::NoAssertEquals => &CheckCode::R002,
CheckKind::RaiseNotImplemented => &CheckCode::F901,
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
CheckKind::UndefinedExport(_) => &CheckCode::F822,
@@ -170,7 +177,7 @@ impl CheckKind {
CheckKind::UndefinedName(_) => &CheckCode::F821,
CheckKind::UnusedImport(_) => &CheckCode::F401,
CheckKind::UnusedVariable(_) => &CheckCode::F841,
CheckKind::UselessObjectInheritance(_) => &CheckCode::R0205,
CheckKind::UselessObjectInheritance(_) => &CheckCode::R001,
CheckKind::YieldOutsideFunction => &CheckCode::F704,
}
}
@@ -196,6 +203,9 @@ impl CheckKind {
CheckKind::ModuleImportNotAtTopOfFile => {
"Module level import not at top of file".to_string()
}
CheckKind::NoAssertEquals => {
"`assertEquals` is deprecated, use `assertEqual` instead".to_string()
}
CheckKind::RaiseNotImplemented => {
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
}
@@ -226,7 +236,26 @@ impl CheckKind {
/// Whether the check kind is (potentially) fixable.
pub fn fixable(&self) -> bool {
matches!(self, CheckKind::UselessObjectInheritance(_))
match self {
CheckKind::AssertTuple => false,
CheckKind::DefaultExceptNotLast => false,
CheckKind::DuplicateArgumentName => false,
CheckKind::FStringMissingPlaceholders => false,
CheckKind::IfTuple => false,
CheckKind::ImportStarUsage => false,
CheckKind::LineTooLong => false,
CheckKind::ModuleImportNotAtTopOfFile => false,
CheckKind::NoAssertEquals => true,
CheckKind::RaiseNotImplemented => false,
CheckKind::ReturnOutsideFunction => false,
CheckKind::UndefinedExport(_) => false,
CheckKind::UndefinedLocal(_) => false,
CheckKind::UndefinedName(_) => false,
CheckKind::UnusedImport(_) => false,
CheckKind::UnusedVariable(_) => false,
CheckKind::UselessObjectInheritance(_) => true,
CheckKind::YieldOutsideFunction => false,
}
}
}

View File

@@ -591,13 +591,13 @@ mod tests {
}
#[test]
fn r0205() -> Result<()> {
fn r001() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/R0205.py"),
Path::new("./resources/test/fixtures/R001.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::R0205]),
select: BTreeSet::from([CheckCode::R001]),
},
&autofix::Mode::Generate,
)?;
@@ -810,4 +810,45 @@ mod tests {
Ok(())
}
#[test]
fn r002() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/R002.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::R002]),
},
&autofix::Mode::Generate,
)?;
let expected = vec![
Check {
kind: CheckKind::NoAssertEquals,
location: Location::new(1, 19),
fix: Some(Fix {
content: "assertEqual".to_string(),
start: Location::new(1, 6),
end: Location::new(1, 18),
applied: false,
}),
},
Check {
kind: CheckKind::NoAssertEquals,
location: Location::new(2, 18),
fix: Some(Fix {
content: "assertEqual".to_string(),
start: Location::new(2, 6),
end: Location::new(2, 18),
applied: false,
}),
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
}

View File

@@ -253,7 +253,8 @@ other-attribute = 1
CheckCode::F831,
CheckCode::F841,
CheckCode::F901,
CheckCode::R0205,
CheckCode::R001,
CheckCode::R002,
])),
}
);