From 7d670f02e193cef701de542ff0befdff6ce0e875 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 6 Oct 2022 22:48:03 -0400 Subject: [PATCH] Enable abspath(__file__) removal --- README.md | 1 + resources/test/fixtures/U002.py | 15 +++++++++++++++ src/ast/checks.rs | 21 +++++++++++++++++++++ src/check_ast.rs | 5 +++++ src/checks.rs | 25 ++++++++++++++++++++----- src/linter.rs | 12 ++++++++++++ src/plugins.rs | 2 ++ src/plugins/unnecessary_abspath.rs | 25 +++++++++++++++++++++++++ 8 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 resources/test/fixtures/U002.py create mode 100644 src/plugins/unnecessary_abspath.rs diff --git a/README.md b/README.md index 8a416b28e3837..4d0f681c06257 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com | T201 | PrintFound | `print` found | | 🛠 | | T203 | PPrintFound | `pprint` found | | 🛠 | | U001 | UselessMetaclassType | `__metaclass__ = type` is implied | | 🛠 | +| U002 | UnnecessaryAbspath | `abspath(__file__)` is unnecessary in Python 3.9 and later | | 🛠 | | R001 | UselessObjectInheritance | Class `...` inherits from object | | 🛠 | | R002 | NoAssertEquals | `assertEquals` is deprecated, use `assertEqual` instead | | 🛠 | | M001 | UnusedNOQA | Unused `noqa` directive | | 🛠 | diff --git a/resources/test/fixtures/U002.py b/resources/test/fixtures/U002.py new file mode 100644 index 0000000000000..0a85ac08287ed --- /dev/null +++ b/resources/test/fixtures/U002.py @@ -0,0 +1,15 @@ +from os.path import abspath + +x = abspath(__file__) + + +import os + + +y = os.path.abspath(__file__) + + +from os import path + + +z = path.abspath(__file__) diff --git a/src/ast/checks.rs b/src/ast/checks.rs index 11ccc371f4094..4286cdb834117 100644 --- a/src/ast/checks.rs +++ b/src/ast/checks.rs @@ -134,6 +134,27 @@ pub fn check_useless_metaclass_type( None } +/// Check UnnecessaryAbspath compliance. +pub fn check_unnecessary_abspath(func: &Expr, args: &Vec, location: Range) -> Option { + // Validate the arguments. + if args.len() == 1 { + if let ExprKind::Name { id, .. } = &args[0].node { + if id == "__file__" { + match &func.node { + ExprKind::Attribute { attr: id, .. } | ExprKind::Name { id, .. } => { + if id == "abspath" { + return Some(Check::new(CheckKind::UnnecessaryAbspath, location)); + } + } + _ => {} + } + } + } + } + + None +} + fn is_ambiguous_name(name: &str) -> bool { name == "l" || name == "I" || name == "O" } diff --git a/src/check_ast.rs b/src/check_ast.rs index 6ba1b669ac4dd..c6b6da2933f87 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -772,6 +772,11 @@ where }; } + // pyupgrade + if self.settings.enabled.contains(&CheckCode::U002) { + plugins::unnecessary_abspath(self, expr, func, args); + } + if let ExprKind::Name { id, ctx } = &func.node { if id == "locals" && matches!(ctx, ExprContext::Load) { let scope = &mut self.scopes[*(self diff --git a/src/checks.rs b/src/checks.rs index 409cb43ea5e38..14aae4f1adf11 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -53,7 +53,7 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [ CheckCode::F901, ]; -pub const ALL_CHECK_CODES: [CheckCode; 53] = [ +pub const ALL_CHECK_CODES: [CheckCode; 54] = [ // pycodestyle CheckCode::E402, CheckCode::E501, @@ -111,6 +111,7 @@ pub const ALL_CHECK_CODES: [CheckCode; 53] = [ CheckCode::T203, // pyupgrade CheckCode::U001, + CheckCode::U002, // Refactor CheckCode::R001, CheckCode::R002, @@ -177,6 +178,7 @@ pub enum CheckCode { T203, // pyupgrade U001, + U002, // Refactor R001, R002, @@ -246,6 +248,7 @@ impl FromStr for CheckCode { "T203" => Ok(CheckCode::T203), // pyupgrade "U001" => Ok(CheckCode::U001), + "U002" => Ok(CheckCode::U002), // Refactor "R001" => Ok(CheckCode::R001), "R002" => Ok(CheckCode::R002), @@ -316,6 +319,7 @@ impl CheckCode { CheckCode::T203 => "T203", // pyupgrade CheckCode::U001 => "U001", + CheckCode::U002 => "U002", // Refactor CheckCode::R001 => "R001", CheckCode::R002 => "R002", @@ -395,6 +399,7 @@ impl CheckCode { CheckCode::T203 => CheckKind::PPrintFound, // pyupgrade CheckCode::U001 => CheckKind::UselessMetaclassType, + CheckCode::U002 => CheckKind::UnnecessaryAbspath, // Refactor CheckCode::R001 => CheckKind::UselessObjectInheritance("...".to_string()), CheckCode::R002 => CheckKind::NoAssertEquals, @@ -446,7 +451,6 @@ pub enum CheckKind { ModuleImportNotAtTopOfFile, MultiValueRepeatedKeyLiteral, MultiValueRepeatedKeyVariable(String), - NoAssertEquals, NoneComparison(RejectedCmpop), NotInTest, NotIsTest, @@ -460,10 +464,7 @@ pub enum CheckKind { UndefinedLocal(String), UndefinedName(String), UnusedImport(String), - UnusedNOQA(Option), UnusedVariable(String), - UselessMetaclassType, - UselessObjectInheritance(String), YieldOutsideFunction, // flake8-builtin BuiltinVariableShadowing(String), @@ -476,6 +477,14 @@ pub enum CheckKind { // flake8-print PrintFound, PPrintFound, + // pyupgrade + UnnecessaryAbspath, + UselessMetaclassType, + // Refactor + NoAssertEquals, + UselessObjectInheritance(String), + // Meta + UnusedNOQA(Option), } impl CheckKind { @@ -536,6 +545,7 @@ impl CheckKind { CheckKind::PrintFound => "PrintFound", CheckKind::PPrintFound => "PPrintFound", // pyupgrade + CheckKind::UnnecessaryAbspath => "UnnecessaryAbspath", CheckKind::UselessMetaclassType => "UselessMetaclassType", // Refactor CheckKind::NoAssertEquals => "NoAssertEquals", @@ -602,6 +612,7 @@ impl CheckKind { CheckKind::PrintFound => &CheckCode::T201, CheckKind::PPrintFound => &CheckCode::T203, // pyupgrade + CheckKind::UnnecessaryAbspath => &CheckCode::U002, CheckKind::UselessMetaclassType => &CheckCode::U001, // Refactor CheckKind::NoAssertEquals => &CheckCode::R002, @@ -761,6 +772,9 @@ impl CheckKind { CheckKind::PrintFound => "`print` found".to_string(), CheckKind::PPrintFound => "`pprint` found".to_string(), // pyupgrade + CheckKind::UnnecessaryAbspath => { + "`abspath(__file__)` is unnecessary in Python 3.9 and later".to_string() + } CheckKind::UselessMetaclassType => "`__metaclass__ = type` is implied".to_string(), // Refactor CheckKind::NoAssertEquals => { @@ -785,6 +799,7 @@ impl CheckKind { | CheckKind::PPrintFound | CheckKind::PrintFound | CheckKind::SuperCallWithParameters + | CheckKind::UnnecessaryAbspath | CheckKind::UnusedImport(_) | CheckKind::UnusedNOQA(_) | CheckKind::UselessMetaclassType diff --git a/src/linter.rs b/src/linter.rs index cfaf3d4651490..81331c99c17c5 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -845,4 +845,16 @@ mod tests { insta::assert_yaml_snapshot!(checks); Ok(()) } + + #[test] + fn u002() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/U002.py"), + &settings::Settings::for_rule(CheckCode::U002), + &fixer::Mode::Generate, + )?; + checks.sort_by_key(|check| check.location); + insta::assert_yaml_snapshot!(checks); + Ok(()) + } } diff --git a/src/plugins.rs b/src/plugins.rs index 0d8cfe156ff5b..d4963b157c6a4 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -4,6 +4,7 @@ mod if_tuple; mod invalid_print_syntax; mod print_call; mod super_call_with_parameters; +mod unnecessary_abspath; mod useless_metaclass_type; mod useless_object_inheritance; @@ -13,5 +14,6 @@ pub use if_tuple::if_tuple; pub use invalid_print_syntax::invalid_print_syntax; pub use print_call::print_call; pub use super_call_with_parameters::super_call_with_parameters; +pub use unnecessary_abspath::unnecessary_abspath; pub use useless_metaclass_type::useless_metaclass_type; pub use useless_object_inheritance::useless_object_inheritance; diff --git a/src/plugins/unnecessary_abspath.rs b/src/plugins/unnecessary_abspath.rs new file mode 100644 index 0000000000000..8e3a8f56b31d7 --- /dev/null +++ b/src/plugins/unnecessary_abspath.rs @@ -0,0 +1,25 @@ +use rustpython_ast::Expr; + +use crate::ast::checks; +use crate::ast::types::{CheckLocator, Range}; +use crate::autofix::fixer; +use crate::check_ast::Checker; +use crate::checks::Fix; + +pub fn unnecessary_abspath(checker: &mut Checker, expr: &Expr, func: &Expr, args: &Vec) { + if let Some(mut check) = checks::check_unnecessary_abspath( + func, + args, + checker.locate_check(Range::from_located(expr)), + ) { + if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) { + check.amend(Fix { + content: "__file__".to_string(), + location: expr.location, + end_location: expr.end_location, + applied: false, + }); + } + checker.add_check(check); + } +}