mirror of https://github.com/astral-sh/ruff
[`flake8-pyi`] Implement `PYI012` (#3743)
This commit is contained in:
parent
450c6780ff
commit
0eb5a22dd1
|
|
@ -0,0 +1,75 @@
|
|||
# Violations of PYI012
|
||||
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class OneAttributeClassRev:
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains pass.
|
||||
"""
|
||||
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
# Not violations (of PYI012)
|
||||
|
||||
|
||||
class EmptyClass:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
|
||||
class EmptyOneLine:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
|
||||
class EmptyEllipsis:
|
||||
...
|
||||
|
||||
|
||||
class NonEmptyEllipsis:
|
||||
value: int
|
||||
... # Y013 Non-empty class body must not contain `...`
|
||||
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
|
||||
pass
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Violations of PYI012
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class OneAttributeClassRev:
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains pass.
|
||||
"""
|
||||
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
# Not violations (of PYI012)
|
||||
|
||||
class EmptyClass:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
class EmptyOneLine:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
class EmptyEllipsis: ...
|
||||
|
||||
class NonEmptyEllipsis:
|
||||
value: int
|
||||
... # Y013 Non-empty class body must not contain `...`
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
pass
|
||||
|
|
@ -775,6 +775,9 @@ where
|
|||
if self.settings.rules.enabled(Rule::PassStatementStubBody) {
|
||||
flake8_pyi::rules::pass_statement_stub_body(self, body);
|
||||
}
|
||||
if self.settings.rules.enabled(Rule::PassInClassBody) {
|
||||
flake8_pyi::rules::pass_in_class_body(self, stmt, body);
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
|
|
|
|||
|
|
@ -569,6 +569,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
|||
(Flake8Pyi, "009") => Rule::PassStatementStubBody,
|
||||
(Flake8Pyi, "010") => Rule::NonEmptyStubBody,
|
||||
(Flake8Pyi, "011") => Rule::TypedArgumentDefaultInStub,
|
||||
(Flake8Pyi, "012") => Rule::PassInClassBody,
|
||||
(Flake8Pyi, "014") => Rule::ArgumentDefaultInStub,
|
||||
(Flake8Pyi, "015") => Rule::AssignmentDefaultInStub,
|
||||
(Flake8Pyi, "021") => Rule::DocstringInStub,
|
||||
|
|
|
|||
|
|
@ -525,6 +525,7 @@ ruff_macros::register_rules!(
|
|||
rules::flake8_pyi::rules::UnprefixedTypeParam,
|
||||
rules::flake8_pyi::rules::UnrecognizedPlatformCheck,
|
||||
rules::flake8_pyi::rules::UnrecognizedPlatformName,
|
||||
rules::flake8_pyi::rules::PassInClassBody,
|
||||
// flake8-pytest-style
|
||||
rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle,
|
||||
rules::flake8_pytest_style::rules::PytestFixturePositionalArgs,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ mod tests {
|
|||
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.pyi"))]
|
||||
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))]
|
||||
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.pyi"))]
|
||||
#[test_case(Rule::PassInClassBody, Path::new("PYI012.py"))]
|
||||
#[test_case(Rule::PassInClassBody, Path::new("PYI012.pyi"))]
|
||||
#[test_case(Rule::ArgumentDefaultInStub, Path::new("PYI014.py"))]
|
||||
#[test_case(Rule::ArgumentDefaultInStub, Path::new("PYI014.pyi"))]
|
||||
#[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.py"))]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pub use bad_version_info_comparison::{bad_version_info_comparison, BadVersionInfoComparison};
|
||||
pub use docstring_in_stubs::{docstring_in_stubs, DocstringInStub};
|
||||
pub use non_empty_stub_body::{non_empty_stub_body, NonEmptyStubBody};
|
||||
pub use pass_in_class_body::{pass_in_class_body, PassInClassBody};
|
||||
pub use pass_statement_stub_body::{pass_statement_stub_body, PassStatementStubBody};
|
||||
pub use prefix_type_params::{prefix_type_params, UnprefixedTypeParam};
|
||||
pub use simple_defaults::{
|
||||
|
|
@ -15,6 +16,7 @@ pub use unrecognized_platform::{
|
|||
mod bad_version_info_comparison;
|
||||
mod docstring_in_stubs;
|
||||
mod non_empty_stub_body;
|
||||
mod pass_in_class_body;
|
||||
mod pass_statement_stub_body;
|
||||
mod prefix_type_params;
|
||||
mod simple_defaults;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
use crate::autofix::helpers::delete_stmt;
|
||||
use log::error;
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, RefEquality};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
use crate::registry::AsRule;
|
||||
use rustpython_parser::ast::{Stmt, StmtKind};
|
||||
|
||||
#[violation]
|
||||
pub struct PassInClassBody;
|
||||
|
||||
impl AlwaysAutofixableViolation for PassInClassBody {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Class body must not contain `pass`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
format!("Remove unnecessary `pass`")
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI012
|
||||
pub fn pass_in_class_body<'a>(checker: &mut Checker<'a>, parent: &'a Stmt, body: &'a [Stmt]) {
|
||||
// `pass` is required in these situations (or handled by `pass_statement_stub_body`).
|
||||
if body.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
for stmt in body {
|
||||
if matches!(stmt.node, StmtKind::Pass) {
|
||||
let mut diagnostic = Diagnostic::new(PassInClassBody, Range::from(stmt));
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect();
|
||||
match delete_stmt(
|
||||
stmt,
|
||||
Some(parent),
|
||||
&deleted,
|
||||
checker.locator,
|
||||
checker.indexer,
|
||||
checker.stylist,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
checker.deletions.insert(RefEquality(stmt));
|
||||
}
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to delete `pass` statement: {}", e);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
name: PassInClassBody
|
||||
body: "Class body must not contain `pass`"
|
||||
suggestion: "Remove unnecessary `pass`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 5
|
||||
column: 4
|
||||
end_location:
|
||||
row: 5
|
||||
column: 8
|
||||
fix:
|
||||
edits:
|
||||
- content: ""
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
name: PassInClassBody
|
||||
body: "Class body must not contain `pass`"
|
||||
suggestion: "Remove unnecessary `pass`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
end_location:
|
||||
row: 8
|
||||
column: 8
|
||||
fix:
|
||||
edits:
|
||||
- content: ""
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
name: PassInClassBody
|
||||
body: "Class body must not contain `pass`"
|
||||
suggestion: "Remove unnecessary `pass`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 16
|
||||
column: 4
|
||||
end_location:
|
||||
row: 16
|
||||
column: 8
|
||||
fix:
|
||||
edits:
|
||||
- content: ""
|
||||
location:
|
||||
row: 16
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
name: PassInClassBody
|
||||
body: "Class body must not contain `pass`"
|
||||
suggestion: "Remove unnecessary `pass`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 20
|
||||
column: 4
|
||||
end_location:
|
||||
row: 20
|
||||
column: 8
|
||||
fix:
|
||||
edits:
|
||||
- content: ""
|
||||
location:
|
||||
row: 20
|
||||
column: 0
|
||||
end_location:
|
||||
row: 21
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
name: PassInClassBody
|
||||
body: "Class body must not contain `pass`"
|
||||
suggestion: "Remove unnecessary `pass`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 23
|
||||
column: 4
|
||||
end_location:
|
||||
row: 23
|
||||
column: 8
|
||||
fix:
|
||||
edits:
|
||||
- content: ""
|
||||
location:
|
||||
row: 23
|
||||
column: 0
|
||||
end_location:
|
||||
row: 24
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
name: PassInClassBody
|
||||
body: "Class body must not contain `pass`"
|
||||
suggestion: "Remove unnecessary `pass`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 28
|
||||
column: 4
|
||||
end_location:
|
||||
row: 28
|
||||
column: 8
|
||||
fix:
|
||||
edits:
|
||||
- content: ""
|
||||
location:
|
||||
row: 28
|
||||
column: 0
|
||||
end_location:
|
||||
row: 29
|
||||
column: 0
|
||||
parent: ~
|
||||
|
||||
|
|
@ -2034,6 +2034,7 @@
|
|||
"PYI01",
|
||||
"PYI010",
|
||||
"PYI011",
|
||||
"PYI012",
|
||||
"PYI014",
|
||||
"PYI015",
|
||||
"PYI02",
|
||||
|
|
|
|||
Loading…
Reference in New Issue