Add another check

This commit is contained in:
Charles Marsh 2022-08-09 22:16:50 -04:00
parent 6257dd00c7
commit 211849901c
7 changed files with 120 additions and 54 deletions

View file

@ -1,7 +1,16 @@
# rust-python-linter # rust-python-linter
A [Pyflakes](https://github.com/PyCQA/pyflakes)-inspired Python linter, written in Rust.
Supports:
- Python 3.10
- True parallelism
- [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired
caching
``` ```
cargo fmt cargo fmt
cargo clippy cargo clippy
cargo run test_sources cargo run resources/test/src
``` ```

View file

@ -0,0 +1,8 @@
if (1, 2):
pass
for _ in range(5):
if True:
pass
elif (1, 2):
pass

View file

@ -1,57 +1,94 @@
use std::path::Path; use std::path::Path;
use rustpython_parser::ast::{Located, StmtKind, Suite}; use rustpython_parser::ast::{ExprKind, Stmt, StmtKind, Suite};
use crate::message::Message; use crate::message::Message;
pub fn check_ast(path: &Path, python_ast: &Suite) -> Vec<Message> { fn check_statement(path: &Path, stmt: &Stmt) -> Vec<Message> {
let mut messages: Vec<Message> = vec![]; let mut messages: Vec<Message> = vec![];
for statement in python_ast { match &stmt.node {
let Located { StmtKind::FunctionDef { body, .. } => {
location, messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
custom: _, }
node, StmtKind::AsyncFunctionDef { body, .. } => {
} = statement; messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
match node { }
StmtKind::FunctionDef { .. } => {} StmtKind::ClassDef { body, .. } => {
StmtKind::AsyncFunctionDef { .. } => {} messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
StmtKind::ClassDef { .. } => {} }
StmtKind::Return { .. } => {} StmtKind::Return { .. } => {}
StmtKind::Delete { .. } => {} StmtKind::Delete { .. } => {}
StmtKind::Assign { .. } => {} StmtKind::Assign { .. } => {}
StmtKind::AugAssign { .. } => {} StmtKind::AugAssign { .. } => {}
StmtKind::AnnAssign { .. } => {} StmtKind::AnnAssign { .. } => {}
StmtKind::For { .. } => {} StmtKind::For { body, orelse, .. } => {
StmtKind::AsyncFor { .. } => {} messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
StmtKind::While { .. } => {} messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
StmtKind::If { .. } => {} }
StmtKind::With { .. } => {} StmtKind::AsyncFor { body, orelse, .. } => {
StmtKind::AsyncWith { .. } => {} messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
StmtKind::Raise { .. } => {} messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
StmtKind::Try { .. } => {} }
StmtKind::Assert { .. } => {} StmtKind::While { body, orelse, .. } => {
StmtKind::Import { .. } => {} messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
StmtKind::ImportFrom { messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
level: _, }
module: _, StmtKind::If { test, body, orelse } => {
names, if let ExprKind::Tuple { .. } = test.node {
} => { messages.push(Message::IfTuple {
for alias in names { filename: path.to_path_buf(),
if alias.name == "*" { location: stmt.location,
messages.push(Message::ImportStarUsage { });
filename: path.to_path_buf(), }
location: *location, messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
}); messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
} }
StmtKind::With { body, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::AsyncWith { body, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::Raise { .. } => {}
StmtKind::Try {
body,
orelse,
finalbody,
..
} => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
messages.extend(
finalbody
.iter()
.flat_map(|stmt| check_statement(path, stmt)),
);
}
StmtKind::Assert { .. } => {}
StmtKind::Import { .. } => {}
StmtKind::ImportFrom { names, .. } => {
for alias in names {
if alias.name == "*" {
messages.push(Message::ImportStarUsage {
filename: path.to_path_buf(),
location: stmt.location,
});
} }
} }
StmtKind::Global { .. } => {}
StmtKind::Nonlocal { .. } => {}
StmtKind::Expr { .. } => {}
StmtKind::Pass => {}
StmtKind::Break => {}
StmtKind::Continue => {}
} }
StmtKind::Global { .. } => {}
StmtKind::Nonlocal { .. } => {}
StmtKind::Expr { .. } => {}
StmtKind::Pass => {}
StmtKind::Break => {}
StmtKind::Continue => {}
} }
messages messages
} }
pub fn check_ast(path: &Path, python_ast: &Suite) -> Vec<Message> {
python_ast
.iter()
.flat_map(|stmt| check_statement(path, stmt))
.collect()
}

View file

@ -27,26 +27,27 @@ pub enum Message {
#[serde(with = "LocationDef")] #[serde(with = "LocationDef")]
location: Location, location: Location,
}, },
IfTuple {
filename: PathBuf,
#[serde(with = "LocationDef")]
location: Location,
},
} }
impl Message { impl Message {
/// A four-letter shorthand code for the message. /// A four-letter shorthand code for the message.
pub fn code(&self) -> &'static str { pub fn code(&self) -> &'static str {
match self { match self {
Message::ImportStarUsage { Message::ImportStarUsage { .. } => "F403",
filename: _, Message::IfTuple { .. } => "F634",
location: _,
} => "F403",
} }
} }
/// The body text for the message. /// The body text for the message.
pub fn body(&self) -> &'static str { pub fn body(&self) -> &'static str {
match self { match self {
Message::ImportStarUsage { Message::ImportStarUsage { .. } => "Unable to detect undefined names",
filename: _, Message::IfTuple { .. } => "If test is a tuple, which is always `True`",
location: _,
} => "Unable to detect undefined names",
} }
} }
} }
@ -65,6 +66,17 @@ impl fmt::Display for Message {
self.code().red().bold(), self.code().red().bold(),
self.body() self.body()
), ),
Message::IfTuple { filename, location } => write!(
f,
"{}{}{}{}{}\t{}\t{}",
filename.to_string_lossy().white().bold(),
":".cyan(),
location.column(),
":".cyan(),
location.row(),
self.code().red().bold(),
self.body()
),
} }
} }
} }

View file