mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:24 +00:00
feat(E275): add Missing whitespace after keyword (#3225)
This commit is contained in:
parent
1c75071136
commit
484ce7b8fc
8 changed files with 179 additions and 2 deletions
|
@ -7,8 +7,8 @@ use crate::ast::types::Range;
|
||||||
use crate::registry::Diagnostic;
|
use crate::registry::Diagnostic;
|
||||||
use crate::rules::pycodestyle::logical_lines::{iter_logical_lines, TokenFlags};
|
use crate::rules::pycodestyle::logical_lines::{iter_logical_lines, TokenFlags};
|
||||||
use crate::rules::pycodestyle::rules::{
|
use crate::rules::pycodestyle::rules::{
|
||||||
extraneous_whitespace, indentation, space_around_operator, whitespace_around_keywords,
|
extraneous_whitespace, indentation, missing_whitespace_after_keyword, space_around_operator,
|
||||||
whitespace_before_comment,
|
whitespace_around_keywords, whitespace_before_comment,
|
||||||
};
|
};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::source_code::{Locator, Stylist};
|
use crate::source_code::{Locator, Stylist};
|
||||||
|
@ -106,6 +106,18 @@ pub fn check_logical_lines(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (location, kind) in missing_whitespace_after_keyword(&line.tokens) {
|
||||||
|
if settings.rules.enabled(kind.rule()) {
|
||||||
|
diagnostics.push(Diagnostic {
|
||||||
|
kind,
|
||||||
|
location,
|
||||||
|
end_location: location,
|
||||||
|
fix: None,
|
||||||
|
parent: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if line.flags.contains(TokenFlags::COMMENT) {
|
if line.flags.contains(TokenFlags::COMMENT) {
|
||||||
for (range, kind) in whitespace_before_comment(&line.tokens, locator) {
|
for (range, kind) in whitespace_before_comment(&line.tokens, locator) {
|
||||||
|
|
|
@ -52,6 +52,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||||
(Pycodestyle, "E273") => Rule::TabAfterKeyword,
|
(Pycodestyle, "E273") => Rule::TabAfterKeyword,
|
||||||
#[cfg(feature = "logical_lines")]
|
#[cfg(feature = "logical_lines")]
|
||||||
(Pycodestyle, "E274") => Rule::TabBeforeKeyword,
|
(Pycodestyle, "E274") => Rule::TabBeforeKeyword,
|
||||||
|
#[cfg(feature = "logical_lines")]
|
||||||
|
(Pycodestyle, "E275") => Rule::MissingWhitespaceAfterKeyword,
|
||||||
(Pycodestyle, "E401") => Rule::MultipleImportsOnOneLine,
|
(Pycodestyle, "E401") => Rule::MultipleImportsOnOneLine,
|
||||||
(Pycodestyle, "E402") => Rule::ModuleImportNotAtTopOfFile,
|
(Pycodestyle, "E402") => Rule::ModuleImportNotAtTopOfFile,
|
||||||
(Pycodestyle, "E501") => Rule::LineTooLong,
|
(Pycodestyle, "E501") => Rule::LineTooLong,
|
||||||
|
|
|
@ -53,6 +53,8 @@ ruff_macros::register_rules!(
|
||||||
#[cfg(feature = "logical_lines")]
|
#[cfg(feature = "logical_lines")]
|
||||||
rules::pycodestyle::rules::MultipleSpacesAfterKeyword,
|
rules::pycodestyle::rules::MultipleSpacesAfterKeyword,
|
||||||
#[cfg(feature = "logical_lines")]
|
#[cfg(feature = "logical_lines")]
|
||||||
|
rules::pycodestyle::rules::MissingWhitespaceAfterKeyword,
|
||||||
|
#[cfg(feature = "logical_lines")]
|
||||||
rules::pycodestyle::rules::MultipleSpacesBeforeKeyword,
|
rules::pycodestyle::rules::MultipleSpacesBeforeKeyword,
|
||||||
#[cfg(feature = "logical_lines")]
|
#[cfg(feature = "logical_lines")]
|
||||||
rules::pycodestyle::rules::TabAfterKeyword,
|
rules::pycodestyle::rules::TabAfterKeyword,
|
||||||
|
@ -824,6 +826,7 @@ impl Rule {
|
||||||
| Rule::MultipleSpacesAfterOperator
|
| Rule::MultipleSpacesAfterOperator
|
||||||
| Rule::MultipleSpacesBeforeKeyword
|
| Rule::MultipleSpacesBeforeKeyword
|
||||||
| Rule::MultipleSpacesBeforeOperator
|
| Rule::MultipleSpacesBeforeOperator
|
||||||
|
| Rule::MissingWhitespaceAfterKeyword
|
||||||
| Rule::NoIndentedBlock
|
| Rule::NoIndentedBlock
|
||||||
| Rule::NoIndentedBlockComment
|
| Rule::NoIndentedBlockComment
|
||||||
| Rule::NoSpaceAfterBlockComment
|
| Rule::NoSpaceAfterBlockComment
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
|
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
|
||||||
|
use rustpython_parser::Tok;
|
||||||
|
|
||||||
use crate::ast::helpers::{create_expr, unparse_expr};
|
use crate::ast::helpers::{create_expr, unparse_expr};
|
||||||
use crate::source_code::Stylist;
|
use crate::source_code::Stylist;
|
||||||
|
@ -56,3 +57,50 @@ pub fn is_overlong(
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_keyword_token(token: &Tok) -> bool {
|
||||||
|
matches!(
|
||||||
|
token,
|
||||||
|
Tok::False { .. }
|
||||||
|
| Tok::True { .. }
|
||||||
|
| Tok::None { .. }
|
||||||
|
| Tok::And { .. }
|
||||||
|
| Tok::As { .. }
|
||||||
|
| Tok::Assert { .. }
|
||||||
|
| Tok::Await { .. }
|
||||||
|
| Tok::Break { .. }
|
||||||
|
| Tok::Class { .. }
|
||||||
|
| Tok::Continue { .. }
|
||||||
|
| Tok::Def { .. }
|
||||||
|
| Tok::Del { .. }
|
||||||
|
| Tok::Elif { .. }
|
||||||
|
| Tok::Else { .. }
|
||||||
|
| Tok::Except { .. }
|
||||||
|
| Tok::Finally { .. }
|
||||||
|
| Tok::For { .. }
|
||||||
|
| Tok::From { .. }
|
||||||
|
| Tok::Global { .. }
|
||||||
|
| Tok::If { .. }
|
||||||
|
| Tok::Import { .. }
|
||||||
|
| Tok::In { .. }
|
||||||
|
| Tok::Is { .. }
|
||||||
|
| Tok::Lambda { .. }
|
||||||
|
| Tok::Nonlocal { .. }
|
||||||
|
| Tok::Not { .. }
|
||||||
|
| Tok::Or { .. }
|
||||||
|
| Tok::Pass { .. }
|
||||||
|
| Tok::Raise { .. }
|
||||||
|
| Tok::Return { .. }
|
||||||
|
| Tok::Try { .. }
|
||||||
|
| Tok::While { .. }
|
||||||
|
| Tok::With { .. }
|
||||||
|
| Tok::Yield { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_singleton_token(token: &Tok) -> bool {
|
||||||
|
matches!(
|
||||||
|
token,
|
||||||
|
Tok::False { .. } | Tok::True { .. } | Tok::None { .. },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ mod tests {
|
||||||
#[test_case(Rule::MultipleSpacesAfterKeyword, Path::new("E27.py"))]
|
#[test_case(Rule::MultipleSpacesAfterKeyword, Path::new("E27.py"))]
|
||||||
#[test_case(Rule::MultipleSpacesAfterOperator, Path::new("E22.py"))]
|
#[test_case(Rule::MultipleSpacesAfterOperator, Path::new("E22.py"))]
|
||||||
#[test_case(Rule::MultipleSpacesBeforeKeyword, Path::new("E27.py"))]
|
#[test_case(Rule::MultipleSpacesBeforeKeyword, Path::new("E27.py"))]
|
||||||
|
#[test_case(Rule::MissingWhitespaceAfterKeyword, Path::new("E27.py"))]
|
||||||
#[test_case(Rule::MultipleSpacesBeforeOperator, Path::new("E22.py"))]
|
#[test_case(Rule::MultipleSpacesBeforeOperator, Path::new("E22.py"))]
|
||||||
#[test_case(Rule::NoIndentedBlock, Path::new("E11.py"))]
|
#[test_case(Rule::NoIndentedBlock, Path::new("E11.py"))]
|
||||||
#[test_case(Rule::NoIndentedBlockComment, Path::new("E11.py"))]
|
#[test_case(Rule::NoIndentedBlockComment, Path::new("E11.py"))]
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use ruff_macros::{define_violation, derive_message_formats};
|
||||||
|
|
||||||
|
use crate::registry::DiagnosticKind;
|
||||||
|
use crate::violation::Violation;
|
||||||
|
|
||||||
|
use crate::rules::pycodestyle::helpers::{is_keyword_token, is_singleton_token};
|
||||||
|
|
||||||
|
use rustpython_parser::ast::Location;
|
||||||
|
use rustpython_parser::Tok;
|
||||||
|
|
||||||
|
define_violation!(
|
||||||
|
pub struct MissingWhitespaceAfterKeyword;
|
||||||
|
);
|
||||||
|
impl Violation for MissingWhitespaceAfterKeyword {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Missing whitespace after keyword")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// E275
|
||||||
|
#[cfg(feature = "logical_lines")]
|
||||||
|
pub fn missing_whitespace_after_keyword(
|
||||||
|
tokens: &[(Location, &Tok, Location)],
|
||||||
|
) -> Vec<(Location, DiagnosticKind)> {
|
||||||
|
let mut diagnostics = vec![];
|
||||||
|
|
||||||
|
for (tok0, tok1) in tokens.iter().zip(&tokens[1..]) {
|
||||||
|
if tok0.2 == tok1.0
|
||||||
|
&& is_keyword_token(tok0.1)
|
||||||
|
&& !is_singleton_token(tok0.1)
|
||||||
|
&& *tok0.1 != Tok::Async
|
||||||
|
&& *tok0.1 != Tok::Await
|
||||||
|
&& !(*tok0.1 == Tok::Except && *tok1.1 == Tok::Star)
|
||||||
|
&& !(*tok0.1 == Tok::Yield && *tok1.1 == Tok::Rpar)
|
||||||
|
&& *tok1.1 != Tok::Colon
|
||||||
|
&& *tok1.1 != Tok::Newline
|
||||||
|
{
|
||||||
|
diagnostics.push((tok0.2, MissingWhitespaceAfterKeyword.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "logical_lines"))]
|
||||||
|
pub fn missing_whitespace_after_keyword(
|
||||||
|
_tokens: &[(Location, &Tok, Location)],
|
||||||
|
) -> Vec<(Location, DiagnosticKind)> {
|
||||||
|
vec![]
|
||||||
|
}
|
|
@ -25,6 +25,9 @@ pub use invalid_escape_sequence::{invalid_escape_sequence, InvalidEscapeSequence
|
||||||
pub use lambda_assignment::{lambda_assignment, LambdaAssignment};
|
pub use lambda_assignment::{lambda_assignment, LambdaAssignment};
|
||||||
pub use line_too_long::{line_too_long, LineTooLong};
|
pub use line_too_long::{line_too_long, LineTooLong};
|
||||||
pub use literal_comparisons::{literal_comparisons, NoneComparison, TrueFalseComparison};
|
pub use literal_comparisons::{literal_comparisons, NoneComparison, TrueFalseComparison};
|
||||||
|
pub use missing_whitespace_after_keyword::{
|
||||||
|
missing_whitespace_after_keyword, MissingWhitespaceAfterKeyword,
|
||||||
|
};
|
||||||
pub use mixed_spaces_and_tabs::{mixed_spaces_and_tabs, MixedSpacesAndTabs};
|
pub use mixed_spaces_and_tabs::{mixed_spaces_and_tabs, MixedSpacesAndTabs};
|
||||||
pub use no_newline_at_end_of_file::{no_newline_at_end_of_file, NoNewLineAtEndOfFile};
|
pub use no_newline_at_end_of_file::{no_newline_at_end_of_file, NoNewLineAtEndOfFile};
|
||||||
pub use not_tests::{not_tests, NotInTest, NotIsTest};
|
pub use not_tests::{not_tests, NotInTest, NotIsTest};
|
||||||
|
@ -59,6 +62,7 @@ mod invalid_escape_sequence;
|
||||||
mod lambda_assignment;
|
mod lambda_assignment;
|
||||||
mod line_too_long;
|
mod line_too_long;
|
||||||
mod literal_comparisons;
|
mod literal_comparisons;
|
||||||
|
mod missing_whitespace_after_keyword;
|
||||||
mod mixed_spaces_and_tabs;
|
mod mixed_spaces_and_tabs;
|
||||||
mod no_newline_at_end_of_file;
|
mod no_newline_at_end_of_file;
|
||||||
mod not_tests;
|
mod not_tests;
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/pycodestyle/mod.rs
|
||||||
|
expression: diagnostics
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
MissingWhitespaceAfterKeyword: ~
|
||||||
|
location:
|
||||||
|
row: 37
|
||||||
|
column: 13
|
||||||
|
end_location:
|
||||||
|
row: 37
|
||||||
|
column: 13
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
MissingWhitespaceAfterKeyword: ~
|
||||||
|
location:
|
||||||
|
row: 39
|
||||||
|
column: 29
|
||||||
|
end_location:
|
||||||
|
row: 39
|
||||||
|
column: 29
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
MissingWhitespaceAfterKeyword: ~
|
||||||
|
location:
|
||||||
|
row: 42
|
||||||
|
column: 33
|
||||||
|
end_location:
|
||||||
|
row: 42
|
||||||
|
column: 33
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
MissingWhitespaceAfterKeyword: ~
|
||||||
|
location:
|
||||||
|
row: 46
|
||||||
|
column: 2
|
||||||
|
end_location:
|
||||||
|
row: 46
|
||||||
|
column: 2
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
MissingWhitespaceAfterKeyword: ~
|
||||||
|
location:
|
||||||
|
row: 54
|
||||||
|
column: 10
|
||||||
|
end_location:
|
||||||
|
row: 54
|
||||||
|
column: 10
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue