mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 02:12:22 +00:00
Add another check
This commit is contained in:
parent
6257dd00c7
commit
211849901c
7 changed files with 120 additions and 54 deletions
11
README.md
11
README.md
|
@ -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
|
||||
```
|
||||
|
|
8
resources/test/src/bar.py
Normal file
8
resources/test/src/bar.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
if (1, 2):
|
||||
pass
|
||||
|
||||
for _ in range(5):
|
||||
if True:
|
||||
pass
|
||||
elif (1, 2):
|
||||
pass
|
127
src/check.rs
127
src/check.rs
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue