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
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 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 rustpython_parser::ast::{Located, StmtKind, Suite};
use rustpython_parser::ast::{ExprKind, Stmt, StmtKind, Suite};
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![];
for statement in python_ast {
let Located {
location,
custom: _,
node,
} = statement;
match node {
StmtKind::FunctionDef { .. } => {}
StmtKind::AsyncFunctionDef { .. } => {}
StmtKind::ClassDef { .. } => {}
StmtKind::Return { .. } => {}
StmtKind::Delete { .. } => {}
StmtKind::Assign { .. } => {}
StmtKind::AugAssign { .. } => {}
StmtKind::AnnAssign { .. } => {}
StmtKind::For { .. } => {}
StmtKind::AsyncFor { .. } => {}
StmtKind::While { .. } => {}
StmtKind::If { .. } => {}
StmtKind::With { .. } => {}
StmtKind::AsyncWith { .. } => {}
StmtKind::Raise { .. } => {}
StmtKind::Try { .. } => {}
StmtKind::Assert { .. } => {}
StmtKind::Import { .. } => {}
StmtKind::ImportFrom {
level: _,
module: _,
names,
} => {
for alias in names {
if alias.name == "*" {
messages.push(Message::ImportStarUsage {
filename: path.to_path_buf(),
location: *location,
});
}
match &stmt.node {
StmtKind::FunctionDef { body, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::AsyncFunctionDef { body, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::ClassDef { body, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::Return { .. } => {}
StmtKind::Delete { .. } => {}
StmtKind::Assign { .. } => {}
StmtKind::AugAssign { .. } => {}
StmtKind::AnnAssign { .. } => {}
StmtKind::For { body, orelse, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::AsyncFor { body, orelse, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::While { body, orelse, .. } => {
messages.extend(body.iter().flat_map(|stmt| check_statement(path, stmt)));
messages.extend(orelse.iter().flat_map(|stmt| check_statement(path, stmt)));
}
StmtKind::If { test, body, orelse } => {
if let ExprKind::Tuple { .. } = test.node {
messages.push(Message::IfTuple {
filename: path.to_path_buf(),
location: stmt.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
}
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")]
location: Location,
},
IfTuple {
filename: PathBuf,
#[serde(with = "LocationDef")]
location: Location,
},
}
impl Message {
/// A four-letter shorthand code for the message.
pub fn code(&self) -> &'static str {
match self {
Message::ImportStarUsage {
filename: _,
location: _,
} => "F403",
Message::ImportStarUsage { .. } => "F403",
Message::IfTuple { .. } => "F634",
}
}
/// The body text for the message.
pub fn body(&self) -> &'static str {
match self {
Message::ImportStarUsage {
filename: _,
location: _,
} => "Unable to detect undefined names",
Message::ImportStarUsage { .. } => "Unable to detect undefined names",
Message::IfTuple { .. } => "If test is a tuple, which is always `True`",
}
}
}
@ -65,6 +66,17 @@ impl fmt::Display for Message {
self.code().red().bold(),
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