From 6d3abdf1d67bfae472c81388205d680927f27710 Mon Sep 17 00:00:00 2001 From: Dmitry Dygalo Date: Wed, 14 Sep 2022 01:04:13 +0200 Subject: [PATCH] feat: Implement `InvalidPrintSyntax` (`F633`) --- resources/test/fixtures/F633.py | 4 ++++ src/check_ast.rs | 24 +++++++++++++++++++++++- src/checks.rs | 7 +++++++ src/linter.rs | 25 +++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 resources/test/fixtures/F633.py diff --git a/resources/test/fixtures/F633.py b/resources/test/fixtures/F633.py new file mode 100644 index 0000000000..3c0db17342 --- /dev/null +++ b/resources/test/fixtures/F633.py @@ -0,0 +1,4 @@ +from __future__ import print_function +import sys + +print >> sys.stderr, "Hello" diff --git a/src/check_ast.rs b/src/check_ast.rs index d905e2cc18..9de7c807fd 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -3,7 +3,7 @@ use std::path::Path; use rustpython_parser::ast::{ Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, - KeywordData, Location, Stmt, StmtKind, Suite, + KeywordData, Location, Operator, Stmt, StmtKind, Suite, }; use rustpython_parser::parser; @@ -668,6 +668,28 @@ where } self.in_f_string = true; } + ExprKind::BinOp { + left, + op: Operator::RShift, + .. + } => { + if self.settings.select.contains(&CheckCode::F633) { + if let ExprKind::Name { id, .. } = &left.node { + if id == "print" { + let scope = &self.scopes + [*(self.scope_stack.last().expect("No current scope found."))]; + if let Some(Binding { + kind: BindingKind::Builtin, + .. + }) = scope.values.get("print") + { + self.checks + .push(Check::new(CheckKind::InvalidPrintSyntax, left.location)); + } + } + } + } + } ExprKind::UnaryOp { op, operand } => { let check_not_in = self.settings.select.contains(&CheckCode::E713); let check_not_is = self.settings.select.contains(&CheckCode::E714); diff --git a/src/checks.rs b/src/checks.rs index 9dc7f51cde..4566708efe 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -31,6 +31,7 @@ pub enum CheckCode { F621, F622, F631, + F633, F634, F701, F702, @@ -76,6 +77,7 @@ impl FromStr for CheckCode { "F621" => Ok(CheckCode::F621), "F622" => Ok(CheckCode::F622), "F631" => Ok(CheckCode::F631), + "F633" => Ok(CheckCode::F633), "F634" => Ok(CheckCode::F634), "F701" => Ok(CheckCode::F701), "F702" => Ok(CheckCode::F702), @@ -121,6 +123,7 @@ impl CheckCode { CheckCode::F621 => "F621", CheckCode::F622 => "F622", CheckCode::F631 => "F631", + CheckCode::F633 => "F633", CheckCode::F634 => "F634", CheckCode::F701 => "F701", CheckCode::F702 => "F702", @@ -179,6 +182,7 @@ pub enum CheckKind { FutureFeatureNotDefined(String), IOError(String), IfTuple, + InvalidPrintSyntax, ImportStarNotPermitted(String), ImportStarUsage(String), LateFutureImport, @@ -223,6 +227,7 @@ impl CheckKind { CheckKind::FutureFeatureNotDefined(_) => "FutureFeatureNotDefined", CheckKind::IOError(_) => "IOError", CheckKind::IfTuple => "IfTuple", + CheckKind::InvalidPrintSyntax => "InvalidPrintSyntax", CheckKind::ImportStarNotPermitted(_) => "ImportStarNotPermitted", CheckKind::ImportStarUsage(_) => "ImportStarUsage", CheckKind::LateFutureImport => "LateFutureImport", @@ -269,6 +274,7 @@ impl CheckKind { CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407, CheckKind::IOError(_) => &CheckCode::E902, CheckKind::IfTuple => &CheckCode::F634, + CheckKind::InvalidPrintSyntax => &CheckCode::F633, CheckKind::ImportStarNotPermitted(_) => &CheckCode::F406, CheckKind::ImportStarUsage(_) => &CheckCode::F403, CheckKind::LateFutureImport => &CheckCode::F404, @@ -335,6 +341,7 @@ impl CheckKind { format!("No such file or directory: `{name}`") } CheckKind::IfTuple => "If test is a tuple, which is always `True`".to_string(), + CheckKind::InvalidPrintSyntax => "use of >> is invalid with print function".to_string(), CheckKind::ImportStarNotPermitted(name) => { format!("`from {name} import *` only allowed at module level") } diff --git a/src/linter.rs b/src/linter.rs index d8e994ce21..ce15561e76 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -851,6 +851,31 @@ mod tests { Ok(()) } + #[test] + fn f633() -> Result<()> { + let mut actual = check_path( + Path::new("./resources/test/fixtures/F633.py"), + &settings::Settings { + line_length: 88, + exclude: vec![], + select: BTreeSet::from([CheckCode::F633]), + }, + &fixer::Mode::Generate, + )?; + actual.sort_by_key(|check| check.location); + let expected = vec![Check { + kind: CheckKind::InvalidPrintSyntax, + location: Location::new(4, 1), + fix: None, + }]; + assert_eq!(actual.len(), expected.len()); + for i in 0..actual.len() { + assert_eq!(actual[i], expected[i]); + } + + Ok(()) + } + #[test] fn f634() -> Result<()> { let mut actual = check_path(