diff --git a/Cargo.lock b/Cargo.lock index 46ac2a263e..54f1830cde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1493,7 +1493,7 @@ dependencies = [ [[package]] name = "rust-python-linter" -version = "0.0.7" +version = "0.0.8" dependencies = [ "anyhow", "bincode", diff --git a/Cargo.toml b/Cargo.toml index 83a9eab2d3..adfcbd76bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-python-linter" -version = "0.0.7" +version = "0.0.8" edition = "2021" [lib] diff --git a/resources/test/src/foo.py b/resources/test/src/foo.py index a8caefaa8c..2c739920af 100644 --- a/resources/test/src/foo.py +++ b/resources/test/src/foo.py @@ -1 +1,5 @@ from bar import * + + +def baz(x: int, x: int) -> None: + pass diff --git a/src/check.rs b/src/check.rs index 30fde191bb..43fd027b9c 100644 --- a/src/check.rs +++ b/src/check.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub enum CheckKind { + DuplicateArgumentName, ImportStarUsage, IfTuple, } @@ -11,16 +12,18 @@ impl CheckKind { /// A four-letter shorthand code for the check. pub fn code(&self) -> &'static str { match self { - CheckKind::ImportStarUsage => "F403", + CheckKind::DuplicateArgumentName => "F831", CheckKind::IfTuple => "F634", + CheckKind::ImportStarUsage => "F403", } } /// The body text for the check. pub fn body(&self) -> &'static str { match self { - CheckKind::ImportStarUsage => "Unable to detect undefined names", + CheckKind::DuplicateArgumentName => "Duplicate argument name in function definition", CheckKind::IfTuple => "If test is a tuple, which is always `True`", + CheckKind::ImportStarUsage => "Unable to detect undefined names", } } } diff --git a/src/checker.rs b/src/checker.rs index 791f78576a..80047a3d76 100644 --- a/src/checker.rs +++ b/src/checker.rs @@ -1,7 +1,9 @@ -use rustpython_parser::ast::{ExprKind, Stmt, StmtKind, Suite}; +use std::collections::HashSet; + +use rustpython_parser::ast::{Arg, Arguments, ExprKind, Stmt, StmtKind, Suite}; use crate::check::{Check, CheckKind}; -use crate::visitor::{walk_stmt, Visitor}; +use crate::visitor::{walk_arguments, walk_stmt, Visitor}; struct Checker { checks: Vec, @@ -32,6 +34,38 @@ impl Visitor for Checker { } walk_stmt(self, stmt); } + + fn visit_arguments(&mut self, arguments: &Arguments) { + // Collect all the arguments into a single vector. + let mut all_arguments: Vec<&Arg> = arguments + .posonlyargs + .iter() + .chain(arguments.kwonlyargs.iter()) + .chain(arguments.args.iter()) + .collect(); + if let Some(arg) = &arguments.vararg { + all_arguments.push(arg); + } + if let Some(arg) = &arguments.kwarg { + all_arguments.push(arg); + } + + // Search for duplicates. + let mut idents: HashSet = HashSet::new(); + for arg in all_arguments { + let ident = &arg.node.arg; + if idents.contains(ident) { + self.checks.push(Check { + kind: CheckKind::DuplicateArgumentName, + location: arg.location, + }); + break; + } + idents.insert(ident.clone()); + } + + walk_arguments(self, arguments); + } } pub fn check_ast(python_ast: &Suite) -> Vec {