mirror of https://github.com/astral-sh/ruff
perf(logical-lines): Various small perf improvements (#4022)
This commit is contained in:
parent
cab65b25da
commit
f3e6ddda62
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
use rustpython_parser::lexer::LexResult;
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Fix};
|
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
|
||||||
use ruff_python_ast::source_code::{Locator, Stylist};
|
use ruff_python_ast::source_code::{Locator, Stylist};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ pub fn check_logical_lines(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
autofix: flags::Autofix,
|
autofix: flags::Autofix,
|
||||||
) -> Vec<Diagnostic> {
|
) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics = vec![];
|
let mut context = LogicalLinesContext::new(settings);
|
||||||
|
|
||||||
#[cfg(feature = "logical_lines")]
|
#[cfg(feature = "logical_lines")]
|
||||||
let should_fix_missing_whitespace =
|
let should_fix_missing_whitespace =
|
||||||
|
|
@ -59,106 +59,33 @@ pub fn check_logical_lines(
|
||||||
|
|
||||||
for line in &LogicalLines::from_tokens(tokens, locator) {
|
for line in &LogicalLines::from_tokens(tokens, locator) {
|
||||||
if line.flags().contains(TokenFlags::OPERATOR) {
|
if line.flags().contains(TokenFlags::OPERATOR) {
|
||||||
for (location, kind) in space_around_operator(&line) {
|
space_around_operator(&line, &mut context);
|
||||||
if settings.rules.enabled(kind.rule()) {
|
whitespace_around_named_parameter_equals(&line, &mut context);
|
||||||
diagnostics.push(Diagnostic {
|
missing_whitespace_around_operator(&line, &mut context);
|
||||||
kind,
|
missing_whitespace(&line, should_fix_missing_whitespace, &mut context);
|
||||||
range: TextRange::empty(location),
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (location, kind) in whitespace_around_named_parameter_equals(&line.tokens()) {
|
|
||||||
if settings.rules.enabled(kind.rule()) {
|
|
||||||
diagnostics.push(Diagnostic {
|
|
||||||
kind,
|
|
||||||
range: TextRange::empty(location),
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (location, kind) in missing_whitespace_around_operator(&line.tokens()) {
|
|
||||||
if settings.rules.enabled(kind.rule()) {
|
|
||||||
diagnostics.push(Diagnostic {
|
|
||||||
kind,
|
|
||||||
range: TextRange::empty(location),
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for diagnostic in missing_whitespace(&line, should_fix_missing_whitespace) {
|
|
||||||
if settings.rules.enabled(diagnostic.kind.rule()) {
|
|
||||||
diagnostics.push(diagnostic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if line
|
if line
|
||||||
.flags()
|
.flags()
|
||||||
.contains(TokenFlags::OPERATOR | TokenFlags::PUNCTUATION)
|
.contains(TokenFlags::OPERATOR | TokenFlags::PUNCTUATION)
|
||||||
{
|
{
|
||||||
for (location, kind) in extraneous_whitespace(&line) {
|
extraneous_whitespace(&line, &mut context);
|
||||||
if settings.rules.enabled(kind.rule()) {
|
|
||||||
diagnostics.push(Diagnostic {
|
|
||||||
kind,
|
|
||||||
range: TextRange::empty(location),
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if line.flags().contains(TokenFlags::KEYWORD) {
|
if line.flags().contains(TokenFlags::KEYWORD) {
|
||||||
for (location, kind) in whitespace_around_keywords(&line) {
|
whitespace_around_keywords(&line, &mut context);
|
||||||
if settings.rules.enabled(kind.rule()) {
|
missing_whitespace_after_keyword(&line, &mut context);
|
||||||
diagnostics.push(Diagnostic {
|
|
||||||
kind,
|
|
||||||
range: TextRange::empty(location),
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (location, kind) in missing_whitespace_after_keyword(&line.tokens()) {
|
|
||||||
if settings.rules.enabled(kind.rule()) {
|
|
||||||
diagnostics.push(Diagnostic {
|
|
||||||
kind,
|
|
||||||
range: TextRange::empty(location),
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.flags().contains(TokenFlags::COMMENT) {
|
if line.flags().contains(TokenFlags::COMMENT) {
|
||||||
for (range, kind) in
|
whitespace_before_comment(&line, locator, prev_line.is_none(), &mut context);
|
||||||
whitespace_before_comment(&line.tokens(), locator, prev_line.is_none())
|
|
||||||
{
|
|
||||||
if settings.rules.enabled(kind.rule()) {
|
|
||||||
diagnostics.push(Diagnostic {
|
|
||||||
kind,
|
|
||||||
range,
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.flags().contains(TokenFlags::BRACKET) {
|
if line.flags().contains(TokenFlags::BRACKET) {
|
||||||
for diagnostic in whitespace_before_parameters(
|
whitespace_before_parameters(
|
||||||
&line.tokens(),
|
&line,
|
||||||
should_fix_whitespace_before_parameters,
|
should_fix_whitespace_before_parameters,
|
||||||
) {
|
&mut context,
|
||||||
if settings.rules.enabled(diagnostic.kind.rule()) {
|
);
|
||||||
diagnostics.push(diagnostic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the indentation level.
|
// Extract the indentation level.
|
||||||
|
|
@ -185,12 +112,7 @@ pub fn check_logical_lines(
|
||||||
indent_size,
|
indent_size,
|
||||||
) {
|
) {
|
||||||
if settings.rules.enabled(kind.rule()) {
|
if settings.rules.enabled(kind.rule()) {
|
||||||
diagnostics.push(Diagnostic {
|
context.push(kind, range);
|
||||||
kind,
|
|
||||||
range,
|
|
||||||
fix: Fix::empty(),
|
|
||||||
parent: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,7 +121,40 @@ pub fn check_logical_lines(
|
||||||
prev_indent_level = Some(indent_level);
|
prev_indent_level = Some(indent_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diagnostics
|
context.diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct LogicalLinesContext<'a> {
|
||||||
|
settings: &'a Settings,
|
||||||
|
diagnostics: Vec<Diagnostic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LogicalLinesContext<'a> {
|
||||||
|
fn new(settings: &'a Settings) -> Self {
|
||||||
|
Self {
|
||||||
|
settings,
|
||||||
|
diagnostics: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push<K: Into<DiagnosticKind>>(&mut self, kind: K, range: TextRange) {
|
||||||
|
let kind = kind.into();
|
||||||
|
if self.settings.rules.enabled(kind.rule()) {
|
||||||
|
self.diagnostics.push(Diagnostic {
|
||||||
|
kind,
|
||||||
|
range,
|
||||||
|
fix: Fix::empty(),
|
||||||
|
parent: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
|
||||||
|
if self.settings.rules.enabled(diagnostic.kind.rule()) {
|
||||||
|
self.diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use ruff_text_size::TextSize;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
use super::{LogicalLine, Whitespace};
|
use super::{LogicalLine, Whitespace};
|
||||||
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use ruff_diagnostics::DiagnosticKind;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
|
@ -101,17 +102,15 @@ impl Violation for WhitespaceBeforePunctuation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// E201, E202, E203
|
/// E201, E202, E203
|
||||||
pub(crate) fn extraneous_whitespace(line: &LogicalLine) -> Vec<(TextSize, DiagnosticKind)> {
|
pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &mut LogicalLinesContext) {
|
||||||
let mut diagnostics = vec![];
|
let mut last_token = TokenKind::EndOfFile;
|
||||||
let mut last_token: Option<TokenKind> = None;
|
|
||||||
|
|
||||||
for token in line.tokens() {
|
for token in line.tokens() {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
match kind {
|
match kind {
|
||||||
TokenKind::Lbrace | TokenKind::Lpar | TokenKind::Lsqb => {
|
TokenKind::Lbrace | TokenKind::Lpar | TokenKind::Lsqb => {
|
||||||
if !matches!(line.trailing_whitespace(&token), Whitespace::None) {
|
if !matches!(line.trailing_whitespace(token), Whitespace::None) {
|
||||||
let end = token.end();
|
context.push(WhitespaceAfterOpenBracket, TextRange::empty(token.end()));
|
||||||
diagnostics.push((end, WhitespaceAfterOpenBracket.into()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenKind::Rbrace
|
TokenKind::Rbrace
|
||||||
|
|
@ -120,19 +119,20 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine) -> Vec<(TextSize, Diagno
|
||||||
| TokenKind::Comma
|
| TokenKind::Comma
|
||||||
| TokenKind::Semi
|
| TokenKind::Semi
|
||||||
| TokenKind::Colon => {
|
| TokenKind::Colon => {
|
||||||
let diagnostic_kind =
|
|
||||||
if matches!(kind, TokenKind::Comma | TokenKind::Semi | TokenKind::Colon) {
|
|
||||||
DiagnosticKind::from(WhitespaceBeforePunctuation)
|
|
||||||
} else {
|
|
||||||
DiagnosticKind::from(WhitespaceBeforeCloseBracket)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) =
|
if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) =
|
||||||
line.leading_whitespace(&token)
|
line.leading_whitespace(token)
|
||||||
{
|
{
|
||||||
if !matches!(last_token, Some(TokenKind::Comma)) {
|
if !matches!(last_token, TokenKind::Comma | TokenKind::EndOfFile) {
|
||||||
let start = token.start();
|
let diagnostic_kind = if matches!(
|
||||||
diagnostics.push((start - offset, diagnostic_kind));
|
kind,
|
||||||
|
TokenKind::Comma | TokenKind::Semi | TokenKind::Colon
|
||||||
|
) {
|
||||||
|
DiagnosticKind::from(WhitespaceBeforePunctuation)
|
||||||
|
} else {
|
||||||
|
DiagnosticKind::from(WhitespaceBeforeCloseBracket)
|
||||||
|
};
|
||||||
|
|
||||||
|
context.push(diagnostic_kind, TextRange::empty(token.start() - offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,8 +140,6 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine) -> Vec<(TextSize, Diagno
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
last_token = Some(kind);
|
last_token = kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use super::LogicalLine;
|
use super::LogicalLine;
|
||||||
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
use ruff_diagnostics::Edit;
|
use ruff_diagnostics::Edit;
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct MissingWhitespace {
|
pub struct MissingWhitespace {
|
||||||
|
|
@ -35,12 +36,14 @@ impl AlwaysAutofixableViolation for MissingWhitespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// E231
|
/// E231
|
||||||
pub(crate) fn missing_whitespace(line: &LogicalLine, autofix: bool) -> Vec<Diagnostic> {
|
pub(crate) fn missing_whitespace(
|
||||||
let mut diagnostics = vec![];
|
line: &LogicalLine,
|
||||||
|
autofix: bool,
|
||||||
|
context: &mut LogicalLinesContext,
|
||||||
|
) {
|
||||||
let mut open_parentheses = 0u32;
|
let mut open_parentheses = 0u32;
|
||||||
let mut prev_lsqb = None;
|
let mut prev_lsqb = TextSize::default();
|
||||||
let mut prev_lbrace = None;
|
let mut prev_lbrace = TextSize::default();
|
||||||
let mut iter = line.tokens().iter().peekable();
|
let mut iter = line.tokens().iter().peekable();
|
||||||
|
|
||||||
while let Some(token) = iter.next() {
|
while let Some(token) = iter.next() {
|
||||||
|
|
@ -48,17 +51,17 @@ pub(crate) fn missing_whitespace(line: &LogicalLine, autofix: bool) -> Vec<Diagn
|
||||||
match kind {
|
match kind {
|
||||||
TokenKind::Lsqb => {
|
TokenKind::Lsqb => {
|
||||||
open_parentheses += 1;
|
open_parentheses += 1;
|
||||||
prev_lsqb = Some(token.start());
|
prev_lsqb = token.start();
|
||||||
}
|
}
|
||||||
TokenKind::Rsqb => {
|
TokenKind::Rsqb => {
|
||||||
open_parentheses += 1;
|
open_parentheses += 1;
|
||||||
}
|
}
|
||||||
TokenKind::Lbrace => {
|
TokenKind::Lbrace => {
|
||||||
prev_lbrace = Some(token.start());
|
prev_lbrace = token.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenKind::Comma | TokenKind::Semi | TokenKind::Colon => {
|
TokenKind::Comma | TokenKind::Semi | TokenKind::Colon => {
|
||||||
let after = line.text_after(&token);
|
let after = line.text_after(token);
|
||||||
|
|
||||||
if !after.chars().next().map_or(false, char::is_whitespace) {
|
if !after.chars().next().map_or(false, char::is_whitespace) {
|
||||||
if let Some(next_token) = iter.peek() {
|
if let Some(next_token) = iter.peek() {
|
||||||
|
|
@ -85,11 +88,10 @@ pub(crate) fn missing_whitespace(line: &LogicalLine, autofix: bool) -> Vec<Diagn
|
||||||
if autofix {
|
if autofix {
|
||||||
diagnostic.set_fix(Edit::insertion(" ".to_string(), token.end()));
|
diagnostic.set_fix(Edit::insertion(" ".to_string(), token.end()));
|
||||||
}
|
}
|
||||||
diagnostics.push(diagnostic);
|
context.push_diagnostic(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_text_size::TextSize;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
|
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
|
|
||||||
use super::LogicalLineTokens;
|
|
||||||
|
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct MissingWhitespaceAfterKeyword;
|
pub struct MissingWhitespaceAfterKeyword;
|
||||||
|
|
||||||
|
|
@ -20,11 +19,10 @@ impl Violation for MissingWhitespaceAfterKeyword {
|
||||||
|
|
||||||
/// E275
|
/// E275
|
||||||
pub(crate) fn missing_whitespace_after_keyword(
|
pub(crate) fn missing_whitespace_after_keyword(
|
||||||
tokens: &LogicalLineTokens,
|
line: &LogicalLine,
|
||||||
) -> Vec<(TextSize, DiagnosticKind)> {
|
context: &mut LogicalLinesContext,
|
||||||
let mut diagnostics = vec![];
|
) {
|
||||||
|
for (tok0, tok1) in line.tokens().iter().tuple_windows() {
|
||||||
for (tok0, tok1) in tokens.iter().tuple_windows() {
|
|
||||||
let tok0_kind = tok0.kind();
|
let tok0_kind = tok0.kind();
|
||||||
let tok1_kind = tok1.kind();
|
let tok1_kind = tok1.kind();
|
||||||
|
|
||||||
|
|
@ -36,8 +34,7 @@ pub(crate) fn missing_whitespace_after_keyword(
|
||||||
|| matches!(tok1_kind, TokenKind::Colon | TokenKind::Newline))
|
|| matches!(tok1_kind, TokenKind::Colon | TokenKind::Newline))
|
||||||
&& tok0.end() == tok1.start()
|
&& tok0.end() == tok1.start()
|
||||||
{
|
{
|
||||||
diagnostics.push((tok0.end(), MissingWhitespaceAfterKeyword.into()));
|
context.push(MissingWhitespaceAfterKeyword, TextRange::empty(tok0.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
use ruff_text_size::TextSize;
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
use crate::rules::pycodestyle::rules::logical_lines::LogicalLineTokens;
|
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
|
||||||
|
|
||||||
// E225
|
// E225
|
||||||
#[violation]
|
#[violation]
|
||||||
|
|
@ -53,18 +53,24 @@ impl Violation for MissingWhitespaceAroundModuloOperator {
|
||||||
/// E225, E226, E227, E228
|
/// E225, E226, E227, E228
|
||||||
#[allow(clippy::if_same_then_else)]
|
#[allow(clippy::if_same_then_else)]
|
||||||
pub(crate) fn missing_whitespace_around_operator(
|
pub(crate) fn missing_whitespace_around_operator(
|
||||||
tokens: &LogicalLineTokens,
|
line: &LogicalLine,
|
||||||
) -> Vec<(TextSize, DiagnosticKind)> {
|
context: &mut LogicalLinesContext,
|
||||||
let mut diagnostics = vec![];
|
) {
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
enum NeedsSpace {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
Unset,
|
||||||
|
}
|
||||||
|
|
||||||
let mut needs_space_main: Option<bool> = Some(false);
|
let mut needs_space_main = NeedsSpace::No;
|
||||||
let mut needs_space_aux: Option<bool> = None;
|
let mut needs_space_aux = NeedsSpace::Unset;
|
||||||
let mut prev_end_aux: Option<TextSize> = None;
|
let mut prev_end_aux = TextSize::default();
|
||||||
let mut parens = 0u32;
|
let mut parens = 0u32;
|
||||||
let mut prev_type: Option<TokenKind> = None;
|
let mut prev_type: TokenKind = TokenKind::EndOfFile;
|
||||||
let mut prev_end: Option<TextSize> = None;
|
let mut prev_end = TextSize::default();
|
||||||
|
|
||||||
for token in tokens {
|
for token in line.tokens() {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
|
|
||||||
if kind.is_skip_comment() {
|
if kind.is_skip_comment() {
|
||||||
|
|
@ -77,100 +83,104 @@ pub(crate) fn missing_whitespace_around_operator(
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let needs_space =
|
let needs_space = needs_space_main == NeedsSpace::Yes
|
||||||
needs_space_main == Some(true) || needs_space_aux.is_some() || prev_end_aux.is_some();
|
|| needs_space_aux != NeedsSpace::Unset
|
||||||
|
|| prev_end_aux != TextSize::new(0);
|
||||||
if needs_space {
|
if needs_space {
|
||||||
if Some(token.start()) != prev_end {
|
if token.start() > prev_end {
|
||||||
if needs_space_main != Some(true) && needs_space_aux != Some(true) {
|
if needs_space_main != NeedsSpace::Yes && needs_space_aux != NeedsSpace::Yes {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
prev_end_aux.unwrap(),
|
MissingWhitespaceAroundOperator,
|
||||||
MissingWhitespaceAroundOperator.into(),
|
TextRange::empty(prev_end_aux),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
needs_space_main = Some(false);
|
needs_space_main = NeedsSpace::No;
|
||||||
needs_space_aux = None;
|
needs_space_aux = NeedsSpace::Unset;
|
||||||
prev_end_aux = None;
|
prev_end_aux = TextSize::new(0);
|
||||||
} else if kind == TokenKind::Greater
|
} else if kind == TokenKind::Greater
|
||||||
&& matches!(prev_type, Some(TokenKind::Less | TokenKind::Minus))
|
&& matches!(prev_type, TokenKind::Less | TokenKind::Minus)
|
||||||
{
|
{
|
||||||
// Tolerate the "<>" operator, even if running Python 3
|
// Tolerate the "<>" operator, even if running Python 3
|
||||||
// Deal with Python 3's annotated return value "->"
|
// Deal with Python 3's annotated return value "->"
|
||||||
} else if prev_type == Some(TokenKind::Slash)
|
} else if prev_type == TokenKind::Slash
|
||||||
&& matches!(kind, TokenKind::Comma | TokenKind::Rpar | TokenKind::Colon)
|
&& matches!(kind, TokenKind::Comma | TokenKind::Rpar | TokenKind::Colon)
|
||||||
|| (prev_type == Some(TokenKind::Rpar) && kind == TokenKind::Colon)
|
|| (prev_type == TokenKind::Rpar && kind == TokenKind::Colon)
|
||||||
{
|
{
|
||||||
// Tolerate the "/" operator in function definition
|
// Tolerate the "/" operator in function definition
|
||||||
// For more info see PEP570
|
// For more info see PEP570
|
||||||
} else {
|
} else {
|
||||||
if needs_space_main == Some(true) || needs_space_aux == Some(true) {
|
if needs_space_main == NeedsSpace::Yes || needs_space_aux == NeedsSpace::Yes {
|
||||||
diagnostics.push((prev_end.unwrap(), MissingWhitespaceAroundOperator.into()));
|
context.push(MissingWhitespaceAroundOperator, TextRange::empty(prev_end));
|
||||||
} else if prev_type != Some(TokenKind::DoubleStar) {
|
} else if prev_type != TokenKind::DoubleStar {
|
||||||
if prev_type == Some(TokenKind::Percent) {
|
if prev_type == TokenKind::Percent {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
prev_end_aux.unwrap(),
|
MissingWhitespaceAroundModuloOperator,
|
||||||
MissingWhitespaceAroundModuloOperator.into(),
|
TextRange::empty(prev_end_aux),
|
||||||
));
|
);
|
||||||
} else if !prev_type.unwrap().is_arithmetic() {
|
} else if !prev_type.is_arithmetic() {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
prev_end_aux.unwrap(),
|
MissingWhitespaceAroundBitwiseOrShiftOperator,
|
||||||
MissingWhitespaceAroundBitwiseOrShiftOperator.into(),
|
TextRange::empty(prev_end_aux),
|
||||||
));
|
);
|
||||||
} else {
|
} else {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
prev_end_aux.unwrap(),
|
MissingWhitespaceAroundArithmeticOperator,
|
||||||
MissingWhitespaceAroundArithmeticOperator.into(),
|
TextRange::empty(prev_end_aux),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
needs_space_main = Some(false);
|
needs_space_main = NeedsSpace::No;
|
||||||
needs_space_aux = None;
|
needs_space_aux = NeedsSpace::Unset;
|
||||||
prev_end_aux = None;
|
prev_end_aux = TextSize::new(0);
|
||||||
}
|
}
|
||||||
} else if (kind.is_operator() || matches!(kind, TokenKind::Name)) && prev_end.is_some() {
|
} else if (kind.is_operator() || matches!(kind, TokenKind::Name))
|
||||||
|
&& prev_end != TextSize::default()
|
||||||
|
{
|
||||||
if kind == TokenKind::Equal && parens > 0 {
|
if kind == TokenKind::Equal && parens > 0 {
|
||||||
// Allow keyword args or defaults: foo(bar=None).
|
// Allow keyword args or defaults: foo(bar=None).
|
||||||
} else if kind.is_whitespace_needed() {
|
} else if kind.is_whitespace_needed() {
|
||||||
needs_space_main = Some(true);
|
needs_space_main = NeedsSpace::Yes;
|
||||||
needs_space_aux = None;
|
needs_space_aux = NeedsSpace::Unset;
|
||||||
prev_end_aux = None;
|
prev_end_aux = TextSize::new(0);
|
||||||
} else if kind.is_unary() {
|
} else if kind.is_unary() {
|
||||||
// Check if the operator is used as a binary operator
|
// Check if the operator is used as a binary operator
|
||||||
// Allow unary operators: -123, -x, +1.
|
// Allow unary operators: -123, -x, +1.
|
||||||
// Allow argument unpacking: foo(*args, **kwargs)
|
// Allow argument unpacking: foo(*args, **kwargs)
|
||||||
if let Some(prev_type) = prev_type {
|
if (matches!(
|
||||||
if (matches!(
|
prev_type,
|
||||||
prev_type,
|
TokenKind::Rpar | TokenKind::Rsqb | TokenKind::Rbrace
|
||||||
TokenKind::Rpar | TokenKind::Rsqb | TokenKind::Rbrace
|
)) || (!prev_type.is_operator()
|
||||||
)) || (!prev_type.is_operator() && !prev_type.is_keyword())
|
&& !prev_type.is_keyword()
|
||||||
&& (!prev_type.is_soft_keyword())
|
&& !prev_type.is_soft_keyword())
|
||||||
{
|
{
|
||||||
needs_space_main = None;
|
needs_space_main = NeedsSpace::Unset;
|
||||||
needs_space_aux = None;
|
needs_space_aux = NeedsSpace::Unset;
|
||||||
prev_end_aux = None;
|
prev_end_aux = TextSize::new(0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if kind.is_whitespace_optional() {
|
} else if kind.is_whitespace_optional() {
|
||||||
needs_space_main = None;
|
needs_space_main = NeedsSpace::Unset;
|
||||||
needs_space_aux = None;
|
needs_space_aux = NeedsSpace::Unset;
|
||||||
prev_end_aux = None;
|
prev_end_aux = TextSize::new(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if needs_space_main.is_none() {
|
if needs_space_main == NeedsSpace::Unset {
|
||||||
// Surrounding space is optional, but ensure that
|
// Surrounding space is optional, but ensure that
|
||||||
// trailing space matches opening space
|
// trailing space matches opening space
|
||||||
prev_end_aux = prev_end;
|
prev_end_aux = prev_end;
|
||||||
needs_space_aux = Some(Some(token.start()) != prev_end_aux);
|
needs_space_aux = if token.start() == prev_end {
|
||||||
} else if needs_space_main == Some(true) && Some(token.start()) == prev_end_aux {
|
NeedsSpace::No
|
||||||
|
} else {
|
||||||
|
NeedsSpace::Yes
|
||||||
|
};
|
||||||
|
} else if needs_space_main == NeedsSpace::Yes && token.start() == prev_end_aux {
|
||||||
// A needed opening space was not found
|
// A needed opening space was not found
|
||||||
diagnostics.push((prev_end.unwrap(), MissingWhitespaceAroundOperator.into()));
|
context.push(MissingWhitespaceAroundOperator, TextRange::empty(prev_end));
|
||||||
needs_space_main = Some(false);
|
needs_space_main = NeedsSpace::No;
|
||||||
needs_space_aux = None;
|
needs_space_aux = NeedsSpace::Unset;
|
||||||
prev_end_aux = None;
|
prev_end_aux = TextSize::new(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev_type = Some(kind);
|
prev_type = kind;
|
||||||
prev_end = Some(token.end());
|
prev_end = token.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ bitflags! {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct LogicalLines<'a> {
|
pub(crate) struct LogicalLines<'a> {
|
||||||
tokens: Tokens,
|
tokens: Vec<LogicalLineToken>,
|
||||||
lines: Vec<Line>,
|
lines: Vec<Line>,
|
||||||
locator: &'a Locator<'a>,
|
locator: &'a Locator<'a>,
|
||||||
}
|
}
|
||||||
|
|
@ -160,65 +160,69 @@ impl<'a> LogicalLine<'a> {
|
||||||
|
|
||||||
/// Returns logical line's text including comments, indents, dedent and trailing new lines.
|
/// Returns logical line's text including comments, indents, dedent and trailing new lines.
|
||||||
pub fn text(&self) -> &'a str {
|
pub fn text(&self) -> &'a str {
|
||||||
self.tokens().text()
|
let tokens = self.tokens();
|
||||||
|
match (tokens.first(), tokens.last()) {
|
||||||
|
(Some(first), Some(last)) => self
|
||||||
|
.lines
|
||||||
|
.locator
|
||||||
|
.slice(TextRange::new(first.start(), last.end())),
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the text without any leading or trailing newline, comment, indent, or dedent of this line
|
/// Returns the text without any leading or trailing newline, comment, indent, or dedent of this line
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn text_trimmed(&self) -> &'a str {
|
pub fn text_trimmed(&self) -> &'a str {
|
||||||
self.tokens_trimmed().text()
|
let tokens = self.tokens_trimmed();
|
||||||
|
|
||||||
|
match (tokens.first(), tokens.last()) {
|
||||||
|
(Some(first), Some(last)) => self
|
||||||
|
.lines
|
||||||
|
.locator
|
||||||
|
.slice(TextRange::new(first.start(), last.end())),
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tokens_trimmed(&self) -> LogicalLineTokens<'a> {
|
pub fn tokens_trimmed(&self) -> &'a [LogicalLineToken] {
|
||||||
let mut front = self.line.tokens_start as usize;
|
let tokens = self.tokens();
|
||||||
let mut back = self.line.tokens_end as usize;
|
|
||||||
|
|
||||||
let mut kinds = self.lines.tokens.kinds[front..back].iter();
|
let start = tokens
|
||||||
|
.iter()
|
||||||
|
.position(|t| {
|
||||||
|
!matches!(
|
||||||
|
t.kind(),
|
||||||
|
TokenKind::Newline
|
||||||
|
| TokenKind::NonLogicalNewline
|
||||||
|
| TokenKind::Indent
|
||||||
|
| TokenKind::Dedent
|
||||||
|
| TokenKind::Comment,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(tokens.len());
|
||||||
|
|
||||||
for kind in kinds.by_ref() {
|
let tokens = &tokens[start..];
|
||||||
if !matches!(
|
|
||||||
kind,
|
|
||||||
TokenKind::Newline
|
|
||||||
| TokenKind::NonLogicalNewline
|
|
||||||
| TokenKind::Indent
|
|
||||||
| TokenKind::Dedent
|
|
||||||
| TokenKind::Comment
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
front += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for kind in kinds.rev() {
|
let end = tokens
|
||||||
if !matches!(
|
.iter()
|
||||||
kind,
|
.rposition(|t| {
|
||||||
TokenKind::Newline
|
!matches!(
|
||||||
| TokenKind::NonLogicalNewline
|
t.kind(),
|
||||||
| TokenKind::Indent
|
TokenKind::Newline
|
||||||
| TokenKind::Dedent
|
| TokenKind::NonLogicalNewline
|
||||||
| TokenKind::Comment
|
| TokenKind::Indent
|
||||||
) {
|
| TokenKind::Dedent
|
||||||
break;
|
| TokenKind::Comment,
|
||||||
}
|
)
|
||||||
back -= 1;
|
})
|
||||||
}
|
.map_or(0, |pos| pos + 1);
|
||||||
|
|
||||||
LogicalLineTokens {
|
&tokens[..end]
|
||||||
lines: self.lines,
|
|
||||||
front,
|
|
||||||
back,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the text after `token`
|
/// Returns the text after `token`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text_after(&self, token: &LogicalLineToken<'a>) -> &str {
|
pub fn text_after(&self, token: &'a LogicalLineToken) -> &str {
|
||||||
debug_assert!(
|
|
||||||
(self.line.tokens_start as usize..self.line.tokens_end as usize)
|
|
||||||
.contains(&token.position),
|
|
||||||
"Token does not belong to this line"
|
|
||||||
);
|
|
||||||
|
|
||||||
// SAFETY: The line must have at least one token or `token` would not belong to this line.
|
// SAFETY: The line must have at least one token or `token` would not belong to this line.
|
||||||
let last_token = self.tokens().last().unwrap();
|
let last_token = self.tokens().last().unwrap();
|
||||||
self.lines
|
self.lines
|
||||||
|
|
@ -228,13 +232,7 @@ impl<'a> LogicalLine<'a> {
|
||||||
|
|
||||||
/// Returns the text before `token`
|
/// Returns the text before `token`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text_before(&self, token: &LogicalLineToken<'a>) -> &str {
|
pub fn text_before(&self, token: &'a LogicalLineToken) -> &str {
|
||||||
debug_assert!(
|
|
||||||
(self.line.tokens_start as usize..self.line.tokens_end as usize)
|
|
||||||
.contains(&token.position),
|
|
||||||
"Token does not belong to this line"
|
|
||||||
);
|
|
||||||
|
|
||||||
// SAFETY: The line must have at least one token or `token` would not belong to this line.
|
// SAFETY: The line must have at least one token or `token` would not belong to this line.
|
||||||
let first_token = self.tokens().first().unwrap();
|
let first_token = self.tokens().first().unwrap();
|
||||||
self.lines
|
self.lines
|
||||||
|
|
@ -243,25 +241,21 @@ impl<'a> LogicalLine<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the whitespace *after* the `token`
|
/// Returns the whitespace *after* the `token`
|
||||||
pub fn trailing_whitespace(&self, token: &LogicalLineToken<'a>) -> Whitespace {
|
pub fn trailing_whitespace(&self, token: &'a LogicalLineToken) -> Whitespace {
|
||||||
Whitespace::leading(self.text_after(token))
|
Whitespace::leading(self.text_after(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the whitespace and whitespace byte-length *before* the `token`
|
/// Returns the whitespace and whitespace byte-length *before* the `token`
|
||||||
pub fn leading_whitespace(&self, token: &LogicalLineToken<'a>) -> (Whitespace, TextSize) {
|
pub fn leading_whitespace(&self, token: &'a LogicalLineToken) -> (Whitespace, TextSize) {
|
||||||
Whitespace::trailing(self.text_before(token))
|
Whitespace::trailing(self.text_before(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all tokens of the line, including comments and trailing new lines.
|
/// Returns all tokens of the line, including comments and trailing new lines.
|
||||||
pub fn tokens(&self) -> LogicalLineTokens<'a> {
|
pub fn tokens(&self) -> &'a [LogicalLineToken] {
|
||||||
LogicalLineTokens {
|
&self.lines.tokens[self.line.tokens_start as usize..self.line.tokens_end as usize]
|
||||||
lines: self.lines,
|
|
||||||
front: self.line.tokens_start as usize,
|
|
||||||
back: self.line.tokens_end as usize,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_token(&self) -> Option<LogicalLineToken> {
|
pub fn first_token(&self) -> Option<&'a LogicalLineToken> {
|
||||||
self.tokens().first()
|
self.tokens().first()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,160 +316,36 @@ impl ExactSizeIterator for LogicalLinesIter<'_> {}
|
||||||
|
|
||||||
impl FusedIterator for LogicalLinesIter<'_> {}
|
impl FusedIterator for LogicalLinesIter<'_> {}
|
||||||
|
|
||||||
/// The tokens of a logical line
|
|
||||||
pub(crate) struct LogicalLineTokens<'a> {
|
|
||||||
lines: &'a LogicalLines<'a>,
|
|
||||||
front: usize,
|
|
||||||
back: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LogicalLineTokens<'a> {
|
|
||||||
pub fn iter(&self) -> LogicalLineTokensIter<'a> {
|
|
||||||
LogicalLineTokensIter {
|
|
||||||
tokens: &self.lines.tokens,
|
|
||||||
front: self.front,
|
|
||||||
back: self.back,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text(&self) -> &'a str {
|
|
||||||
match (self.first(), self.last()) {
|
|
||||||
(Some(first), Some(last)) => {
|
|
||||||
let locator = self.lines.locator;
|
|
||||||
locator.slice(TextRange::new(first.start(), last.end()))
|
|
||||||
}
|
|
||||||
_ => "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the first token
|
|
||||||
pub fn first(&self) -> Option<LogicalLineToken<'a>> {
|
|
||||||
self.iter().next()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the last token
|
|
||||||
pub fn last(&self) -> Option<LogicalLineToken<'a>> {
|
|
||||||
self.iter().next_back()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for LogicalLineTokens<'a> {
|
|
||||||
type Item = LogicalLineToken<'a>;
|
|
||||||
type IntoIter = LogicalLineTokensIter<'a>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &LogicalLineTokens<'a> {
|
|
||||||
type Item = LogicalLineToken<'a>;
|
|
||||||
type IntoIter = LogicalLineTokensIter<'a>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for LogicalLineTokens<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_list().entries(self.iter()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator over the tokens of a [`LogicalLine`]
|
|
||||||
pub(crate) struct LogicalLineTokensIter<'a> {
|
|
||||||
tokens: &'a Tokens,
|
|
||||||
front: usize,
|
|
||||||
back: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for LogicalLineTokensIter<'a> {
|
|
||||||
type Item = LogicalLineToken<'a>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.front < self.back {
|
|
||||||
let result = Some(LogicalLineToken {
|
|
||||||
tokens: self.tokens,
|
|
||||||
position: self.front,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.front += 1;
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
let len = self.back - self.front;
|
|
||||||
(len, Some(len))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExactSizeIterator for LogicalLineTokensIter<'_> {}
|
|
||||||
|
|
||||||
impl FusedIterator for LogicalLineTokensIter<'_> {}
|
|
||||||
|
|
||||||
impl DoubleEndedIterator for LogicalLineTokensIter<'_> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.front < self.back {
|
|
||||||
self.back -= 1;
|
|
||||||
Some(LogicalLineToken {
|
|
||||||
position: self.back,
|
|
||||||
tokens: self.tokens,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A token of a [`LogicalLine`]
|
/// A token of a [`LogicalLine`]
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct LogicalLineToken<'a> {
|
pub(crate) struct LogicalLineToken {
|
||||||
tokens: &'a Tokens,
|
kind: TokenKind,
|
||||||
position: usize,
|
range: TextRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LogicalLineToken<'a> {
|
impl LogicalLineToken {
|
||||||
/// Returns the token's kind
|
/// Returns the token's kind
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> TokenKind {
|
pub const fn kind(&self) -> TokenKind {
|
||||||
#[allow(unsafe_code)]
|
self.kind
|
||||||
unsafe {
|
|
||||||
*self.tokens.kinds.get_unchecked(self.position)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the token's start location
|
/// Returns the token's start location
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn start(&self) -> TextSize {
|
pub const fn start(&self) -> TextSize {
|
||||||
self.range().start()
|
self.range.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the token's end location
|
/// Returns the token's end location
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn end(&self) -> TextSize {
|
pub const fn end(&self) -> TextSize {
|
||||||
self.range().end()
|
self.range.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tuple with the token's `(start, end)` locations
|
/// Returns a tuple with the token's `(start, end)` locations
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn range(&self) -> TextRange {
|
pub const fn range(&self) -> TextRange {
|
||||||
#[allow(unsafe_code)]
|
self.range
|
||||||
unsafe {
|
|
||||||
*self.tokens.ranges.get_unchecked(self.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for LogicalLineToken<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("LogicalLineToken")
|
|
||||||
.field("kind", &self.kind())
|
|
||||||
.field("range", &self.range())
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,15 +422,15 @@ struct CurrentLine {
|
||||||
/// Builder for [`LogicalLines`]
|
/// Builder for [`LogicalLines`]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct LogicalLinesBuilder {
|
struct LogicalLinesBuilder {
|
||||||
tokens: Tokens,
|
tokens: Vec<LogicalLineToken>,
|
||||||
lines: Vec<Line>,
|
lines: Vec<Line>,
|
||||||
current_line: Option<CurrentLine>,
|
current_line: CurrentLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogicalLinesBuilder {
|
impl LogicalLinesBuilder {
|
||||||
fn with_capacity(tokens: usize) -> Self {
|
fn with_capacity(tokens: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tokens: Tokens::with_capacity(tokens),
|
tokens: Vec::with_capacity(tokens),
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -568,12 +438,7 @@ impl LogicalLinesBuilder {
|
||||||
// SAFETY: `LogicalLines::from_tokens` asserts that the file has less than `u32::MAX` tokens and each tokens is at least one character long
|
// SAFETY: `LogicalLines::from_tokens` asserts that the file has less than `u32::MAX` tokens and each tokens is at least one character long
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn push_token(&mut self, kind: TokenKind, range: TextRange) {
|
fn push_token(&mut self, kind: TokenKind, range: TextRange) {
|
||||||
let tokens_start = self.tokens.len();
|
let line = &mut self.current_line;
|
||||||
|
|
||||||
let line = self.current_line.get_or_insert_with(|| CurrentLine {
|
|
||||||
flags: TokenFlags::empty(),
|
|
||||||
tokens_start: tokens_start as u32,
|
|
||||||
});
|
|
||||||
|
|
||||||
if matches!(kind, TokenKind::Comment) {
|
if matches!(kind, TokenKind::Comment) {
|
||||||
line.flags.insert(TokenFlags::COMMENT);
|
line.flags.insert(TokenFlags::COMMENT);
|
||||||
|
|
@ -612,18 +477,24 @@ impl LogicalLinesBuilder {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.tokens.push(kind, range);
|
self.tokens.push(LogicalLineToken { kind, range });
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: `LogicalLines::from_tokens` asserts that the file has less than `u32::MAX` tokens and each tokens is at least one character long
|
// SAFETY: `LogicalLines::from_tokens` asserts that the file has less than `u32::MAX` tokens and each tokens is at least one character long
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn finish_line(&mut self) {
|
fn finish_line(&mut self) {
|
||||||
if let Some(current) = self.current_line.take() {
|
let end = self.tokens.len() as u32;
|
||||||
|
if self.current_line.tokens_start < end {
|
||||||
self.lines.push(Line {
|
self.lines.push(Line {
|
||||||
flags: current.flags,
|
flags: self.current_line.flags,
|
||||||
tokens_start: current.tokens_start,
|
tokens_start: self.current_line.tokens_start,
|
||||||
tokens_end: self.tokens.len() as u32,
|
tokens_end: end,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.current_line = CurrentLine {
|
||||||
|
flags: TokenFlags::default(),
|
||||||
|
tokens_start: end,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,33 +515,3 @@ struct Line {
|
||||||
tokens_start: u32,
|
tokens_start: u32,
|
||||||
tokens_end: u32,
|
tokens_end: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
struct Tokens {
|
|
||||||
/// The token kinds
|
|
||||||
kinds: Vec<TokenKind>,
|
|
||||||
|
|
||||||
/// The ranges
|
|
||||||
ranges: Vec<TextRange>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tokens {
|
|
||||||
/// Creates new tokens with a reserved size of `capacity`
|
|
||||||
fn with_capacity(capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
kinds: Vec::with_capacity(capacity),
|
|
||||||
ranges: Vec::with_capacity(capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of stored tokens.
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.kinds.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new token with the given `kind` and `range`
|
|
||||||
fn push(&mut self, kind: TokenKind, range: TextRange) {
|
|
||||||
self.kinds.push(kind);
|
|
||||||
self.ranges.push(range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_text_size::TextSize;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
use super::{LogicalLine, Whitespace};
|
use super::{LogicalLine, Whitespace};
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
|
|
@ -123,8 +123,7 @@ impl Violation for MultipleSpacesAfterOperator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// E221, E222, E223, E224
|
/// E221, E222, E223, E224
|
||||||
pub(crate) fn space_around_operator(line: &LogicalLine) -> Vec<(TextSize, DiagnosticKind)> {
|
pub(crate) fn space_around_operator(line: &LogicalLine, context: &mut LogicalLinesContext) {
|
||||||
let mut diagnostics = vec![];
|
|
||||||
let mut after_operator = false;
|
let mut after_operator = false;
|
||||||
|
|
||||||
for token in line.tokens() {
|
for token in line.tokens() {
|
||||||
|
|
@ -132,27 +131,30 @@ pub(crate) fn space_around_operator(line: &LogicalLine) -> Vec<(TextSize, Diagno
|
||||||
|
|
||||||
if is_operator {
|
if is_operator {
|
||||||
if !after_operator {
|
if !after_operator {
|
||||||
match line.leading_whitespace(&token) {
|
match line.leading_whitespace(token) {
|
||||||
(Whitespace::Tab, offset) => {
|
(Whitespace::Tab, offset) => {
|
||||||
let start = token.start();
|
let start = token.start();
|
||||||
diagnostics.push((start - offset, TabBeforeOperator.into()));
|
context.push(TabBeforeOperator, TextRange::empty(start - offset));
|
||||||
}
|
}
|
||||||
(Whitespace::Many, offset) => {
|
(Whitespace::Many, offset) => {
|
||||||
let start = token.start();
|
let start = token.start();
|
||||||
diagnostics.push((start - offset, MultipleSpacesBeforeOperator.into()));
|
context.push(
|
||||||
|
MultipleSpacesBeforeOperator,
|
||||||
|
TextRange::empty(start - offset),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match line.trailing_whitespace(&token) {
|
match line.trailing_whitespace(token) {
|
||||||
Whitespace::Tab => {
|
Whitespace::Tab => {
|
||||||
let end = token.end();
|
let end = token.end();
|
||||||
diagnostics.push((end, TabAfterOperator.into()));
|
context.push(TabAfterOperator, TextRange::empty(end));
|
||||||
}
|
}
|
||||||
Whitespace::Many => {
|
Whitespace::Many => {
|
||||||
let end = token.end();
|
let end = token.end();
|
||||||
diagnostics.push((end, MultipleSpacesAfterOperator.into()));
|
context.push(MultipleSpacesAfterOperator, TextRange::empty(end));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -160,8 +162,6 @@ pub(crate) fn space_around_operator(line: &LogicalLine) -> Vec<(TextSize, Diagno
|
||||||
|
|
||||||
after_operator = is_operator;
|
after_operator = is_operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn is_operator_token(token: TokenKind) -> bool {
|
const fn is_operator_token(token: TokenKind) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::{LogicalLine, Whitespace};
|
use super::{LogicalLine, Whitespace};
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_text_size::TextSize;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for extraneous whitespace after keywords.
|
/// Checks for extraneous whitespace after keywords.
|
||||||
|
|
@ -108,35 +108,37 @@ impl Violation for TabBeforeKeyword {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// E271, E272, E273, E274
|
/// E271, E272, E273, E274
|
||||||
pub(crate) fn whitespace_around_keywords(line: &LogicalLine) -> Vec<(TextSize, DiagnosticKind)> {
|
pub(crate) fn whitespace_around_keywords(line: &LogicalLine, context: &mut LogicalLinesContext) {
|
||||||
let mut diagnostics = vec![];
|
|
||||||
let mut after_keyword = false;
|
let mut after_keyword = false;
|
||||||
|
|
||||||
for token in line.tokens() {
|
for token in line.tokens() {
|
||||||
let is_keyword = token.kind().is_keyword();
|
let is_keyword = token.kind().is_keyword();
|
||||||
if is_keyword {
|
if is_keyword {
|
||||||
if !after_keyword {
|
if !after_keyword {
|
||||||
match line.leading_whitespace(&token) {
|
match line.leading_whitespace(token) {
|
||||||
(Whitespace::Tab, offset) => {
|
(Whitespace::Tab, offset) => {
|
||||||
let start = token.start();
|
let start = token.start();
|
||||||
diagnostics.push((start - offset, TabBeforeKeyword.into()));
|
context.push(TabBeforeKeyword, TextRange::empty(start - offset));
|
||||||
}
|
}
|
||||||
(Whitespace::Many, offset) => {
|
(Whitespace::Many, offset) => {
|
||||||
let start = token.start();
|
let start = token.start();
|
||||||
diagnostics.push((start - offset, MultipleSpacesBeforeKeyword.into()));
|
context.push(
|
||||||
|
MultipleSpacesBeforeKeyword,
|
||||||
|
TextRange::empty(start - offset),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match line.trailing_whitespace(&token) {
|
match line.trailing_whitespace(token) {
|
||||||
Whitespace::Tab => {
|
Whitespace::Tab => {
|
||||||
let end = token.end();
|
let end = token.end();
|
||||||
diagnostics.push((end, TabAfterKeyword.into()));
|
context.push(TabAfterKeyword, TextRange::empty(end));
|
||||||
}
|
}
|
||||||
Whitespace::Many => {
|
Whitespace::Many => {
|
||||||
let end = token.end();
|
let end = token.end();
|
||||||
diagnostics.push((end, MultipleSpacesAfterKeyword.into()));
|
context.push(MultipleSpacesAfterKeyword, TextRange::empty(end));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +146,4 @@ pub(crate) fn whitespace_around_keywords(line: &LogicalLine) -> Vec<(TextSize, D
|
||||||
|
|
||||||
after_keyword = is_keyword;
|
after_keyword = is_keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
|
use crate::rules::pycodestyle::rules::logical_lines::{LogicalLine, LogicalLineToken};
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
use ruff_text_size::TextSize;
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
use super::LogicalLineTokens;
|
|
||||||
|
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct UnexpectedSpacesAroundKeywordParameterEquals;
|
pub struct UnexpectedSpacesAroundKeywordParameterEquals;
|
||||||
|
|
@ -26,7 +25,7 @@ impl Violation for MissingWhitespaceAroundParameterEquals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_in_def(tokens: &LogicalLineTokens) -> bool {
|
fn is_in_def(tokens: &[LogicalLineToken]) -> bool {
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
TokenKind::Async | TokenKind::Indent | TokenKind::Dedent => continue,
|
TokenKind::Async | TokenKind::Indent | TokenKind::Dedent => continue,
|
||||||
|
|
@ -40,15 +39,15 @@ fn is_in_def(tokens: &LogicalLineTokens) -> bool {
|
||||||
|
|
||||||
/// E251, E252
|
/// E251, E252
|
||||||
pub(crate) fn whitespace_around_named_parameter_equals(
|
pub(crate) fn whitespace_around_named_parameter_equals(
|
||||||
tokens: &LogicalLineTokens,
|
line: &LogicalLine,
|
||||||
) -> Vec<(TextSize, DiagnosticKind)> {
|
context: &mut LogicalLinesContext,
|
||||||
let mut diagnostics = vec![];
|
) {
|
||||||
let mut parens = 0u32;
|
let mut parens = 0u32;
|
||||||
let mut annotated_func_arg = false;
|
let mut annotated_func_arg = false;
|
||||||
let mut prev_end: Option<TextSize> = None;
|
let mut prev_end = TextSize::default();
|
||||||
|
|
||||||
let in_def = is_in_def(tokens);
|
let in_def = is_in_def(line.tokens());
|
||||||
let mut iter = tokens.iter().peekable();
|
let mut iter = line.tokens().iter().peekable();
|
||||||
|
|
||||||
while let Some(token) = iter.next() {
|
while let Some(token) = iter.next() {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
|
|
@ -78,8 +77,11 @@ pub(crate) fn whitespace_around_named_parameter_equals(
|
||||||
TokenKind::Equal if parens > 0 => {
|
TokenKind::Equal if parens > 0 => {
|
||||||
if annotated_func_arg && parens == 1 {
|
if annotated_func_arg && parens == 1 {
|
||||||
let start = token.start();
|
let start = token.start();
|
||||||
if Some(start) == prev_end {
|
if start == prev_end && prev_end != TextSize::new(0) {
|
||||||
diagnostics.push((start, MissingWhitespaceAroundParameterEquals.into()));
|
context.push(
|
||||||
|
MissingWhitespaceAroundParameterEquals,
|
||||||
|
TextRange::empty(start),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(next) = iter.peek() {
|
while let Some(next) = iter.peek() {
|
||||||
|
|
@ -89,20 +91,20 @@ pub(crate) fn whitespace_around_named_parameter_equals(
|
||||||
let next_start = next.start();
|
let next_start = next.start();
|
||||||
|
|
||||||
if next_start == token.end() {
|
if next_start == token.end() {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
next_start,
|
MissingWhitespaceAroundParameterEquals,
|
||||||
MissingWhitespaceAroundParameterEquals.into(),
|
TextRange::empty(next_start),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if Some(token.start()) != prev_end {
|
if token.start() != prev_end {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
prev_end.unwrap(),
|
UnexpectedSpacesAroundKeywordParameterEquals,
|
||||||
UnexpectedSpacesAroundKeywordParameterEquals.into(),
|
TextRange::empty(prev_end),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(next) = iter.peek() {
|
while let Some(next) = iter.peek() {
|
||||||
|
|
@ -110,10 +112,10 @@ pub(crate) fn whitespace_around_named_parameter_equals(
|
||||||
iter.next();
|
iter.next();
|
||||||
} else {
|
} else {
|
||||||
if next.start() != token.end() {
|
if next.start() != token.end() {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
token.end(),
|
UnexpectedSpacesAroundKeywordParameterEquals,
|
||||||
UnexpectedSpacesAroundKeywordParameterEquals.into(),
|
TextRange::empty(token.end()),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +125,6 @@ pub(crate) fn whitespace_around_named_parameter_equals(
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_end = Some(token.end());
|
prev_end = token.end();
|
||||||
}
|
}
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::LogicalLineTokens;
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
use ruff_diagnostics::DiagnosticKind;
|
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::source_code::Locator;
|
use ruff_python_ast::source_code::Locator;
|
||||||
|
|
@ -137,13 +137,13 @@ impl Violation for MultipleLeadingHashesForBlockComment {
|
||||||
|
|
||||||
/// E261, E262, E265, E266
|
/// E261, E262, E265, E266
|
||||||
pub(crate) fn whitespace_before_comment(
|
pub(crate) fn whitespace_before_comment(
|
||||||
tokens: &LogicalLineTokens,
|
line: &LogicalLine,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
is_first_row: bool,
|
is_first_row: bool,
|
||||||
) -> Vec<(TextRange, DiagnosticKind)> {
|
context: &mut LogicalLinesContext,
|
||||||
let mut diagnostics = vec![];
|
) {
|
||||||
let mut prev_end = TextSize::default();
|
let mut prev_end = TextSize::default();
|
||||||
for token in tokens {
|
for token in line.tokens() {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
|
|
||||||
if let TokenKind::Comment = kind {
|
if let TokenKind::Comment = kind {
|
||||||
|
|
@ -158,10 +158,10 @@ pub(crate) fn whitespace_before_comment(
|
||||||
let is_inline_comment = !line.trim().is_empty();
|
let is_inline_comment = !line.trim().is_empty();
|
||||||
if is_inline_comment {
|
if is_inline_comment {
|
||||||
if range.start() - prev_end < " ".text_len() {
|
if range.start() - prev_end < " ".text_len() {
|
||||||
diagnostics.push((
|
context.push(
|
||||||
|
TooFewSpacesBeforeInlineComment,
|
||||||
TextRange::new(prev_end, range.start()),
|
TextRange::new(prev_end, range.start()),
|
||||||
TooFewSpacesBeforeInlineComment.into(),
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,14 +179,14 @@ pub(crate) fn whitespace_before_comment(
|
||||||
if is_inline_comment {
|
if is_inline_comment {
|
||||||
if bad_prefix.is_some() || comment.chars().next().map_or(false, char::is_whitespace)
|
if bad_prefix.is_some() || comment.chars().next().map_or(false, char::is_whitespace)
|
||||||
{
|
{
|
||||||
diagnostics.push((range, NoSpaceAfterInlineComment.into()));
|
context.push(NoSpaceAfterInlineComment, range);
|
||||||
}
|
}
|
||||||
} else if let Some(bad_prefix) = bad_prefix {
|
} else if let Some(bad_prefix) = bad_prefix {
|
||||||
if bad_prefix != '!' || !is_first_row {
|
if bad_prefix != '!' || !is_first_row {
|
||||||
if bad_prefix != '#' {
|
if bad_prefix != '#' {
|
||||||
diagnostics.push((range, NoSpaceAfterBlockComment.into()));
|
context.push(NoSpaceAfterBlockComment, range);
|
||||||
} else if !comment.is_empty() {
|
} else if !comment.is_empty() {
|
||||||
diagnostics.push((range, MultipleLeadingHashesForBlockComment.into()));
|
context.push(MultipleLeadingHashesForBlockComment, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -194,5 +194,4 @@ pub(crate) fn whitespace_before_comment(
|
||||||
prev_end = token.end();
|
prev_end = token.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
|
use crate::checkers::logical_lines::LogicalLinesContext;
|
||||||
|
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::token_kind::TokenKind;
|
use ruff_python_ast::token_kind::TokenKind;
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
use super::LogicalLineTokens;
|
|
||||||
|
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct WhitespaceBeforeParameters {
|
pub struct WhitespaceBeforeParameters {
|
||||||
pub bracket: TokenKind,
|
pub bracket: TokenKind,
|
||||||
|
|
@ -35,17 +35,17 @@ impl AlwaysAutofixableViolation for WhitespaceBeforeParameters {
|
||||||
|
|
||||||
/// E211
|
/// E211
|
||||||
pub(crate) fn whitespace_before_parameters(
|
pub(crate) fn whitespace_before_parameters(
|
||||||
tokens: &LogicalLineTokens,
|
line: &LogicalLine,
|
||||||
autofix: bool,
|
autofix: bool,
|
||||||
) -> Vec<Diagnostic> {
|
context: &mut LogicalLinesContext,
|
||||||
let mut diagnostics = vec![];
|
) {
|
||||||
let previous = tokens.first().unwrap();
|
let previous = line.tokens().first().unwrap();
|
||||||
|
|
||||||
let mut pre_pre_kind: Option<TokenKind> = None;
|
let mut pre_pre_kind: Option<TokenKind> = None;
|
||||||
let mut prev_token = previous.kind();
|
let mut prev_token = previous.kind();
|
||||||
let mut prev_end = previous.end();
|
let mut prev_end = previous.end();
|
||||||
|
|
||||||
for token in tokens {
|
for token in line.tokens() {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
|
|
||||||
if matches!(kind, TokenKind::Lpar | TokenKind::Lsqb)
|
if matches!(kind, TokenKind::Lpar | TokenKind::Lsqb)
|
||||||
|
|
@ -65,11 +65,10 @@ pub(crate) fn whitespace_before_parameters(
|
||||||
if autofix {
|
if autofix {
|
||||||
diagnostic.set_fix(Edit::deletion(start, end));
|
diagnostic.set_fix(Edit::deletion(start, end));
|
||||||
}
|
}
|
||||||
diagnostics.push(diagnostic);
|
context.push_diagnostic(diagnostic);
|
||||||
}
|
}
|
||||||
pre_pre_kind = Some(prev_token);
|
pre_pre_kind = Some(prev_token);
|
||||||
prev_token = kind;
|
prev_token = kind;
|
||||||
prev_end = token.end();
|
prev_end = token.end();
|
||||||
}
|
}
|
||||||
diagnostics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Fix;
|
use crate::Fix;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct DiagnosticKind {
|
pub struct DiagnosticKind {
|
||||||
/// The identifier of the diagnostic, used to align the diagnostic with a rule.
|
/// The identifier of the diagnostic, used to align the diagnostic with a rule.
|
||||||
|
|
@ -20,7 +20,7 @@ pub struct DiagnosticKind {
|
||||||
pub fixable: bool,
|
pub fixable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Diagnostic {
|
pub struct Diagnostic {
|
||||||
pub kind: DiagnosticKind,
|
pub kind: DiagnosticKind,
|
||||||
pub range: TextRange,
|
pub range: TextRange,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::edit::Edit;
|
use crate::edit::Edit;
|
||||||
|
|
||||||
/// A collection of [`Edit`] elements to be applied to a source file.
|
/// A collection of [`Edit`] elements to be applied to a source file.
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Default, Debug, PartialEq, Eq, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Fix {
|
pub struct Fix {
|
||||||
edits: Vec<Edit>,
|
edits: Vec<Edit>,
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ struct LineIndexInner {
|
||||||
impl LineIndex {
|
impl LineIndex {
|
||||||
/// Builds the [`LineIndex`] from the source text of a file.
|
/// Builds the [`LineIndex`] from the source text of a file.
|
||||||
pub fn from_source_text(text: &str) -> Self {
|
pub fn from_source_text(text: &str) -> Self {
|
||||||
assert!(u32::try_from(text.len()).is_ok());
|
|
||||||
|
|
||||||
let mut line_starts: Vec<TextSize> = Vec::with_capacity(text.len() / 88);
|
let mut line_starts: Vec<TextSize> = Vec::with_capacity(text.len() / 88);
|
||||||
line_starts.push(TextSize::default());
|
line_starts.push(TextSize::default());
|
||||||
|
|
||||||
let bytes = text.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
let mut utf8 = false;
|
let mut utf8 = false;
|
||||||
|
|
||||||
|
assert!(u32::try_from(bytes.len()).is_ok());
|
||||||
|
|
||||||
for (i, byte) in bytes.iter().enumerate() {
|
for (i, byte) in bytes.iter().enumerate() {
|
||||||
utf8 |= !byte.is_ascii();
|
utf8 |= !byte.is_ascii();
|
||||||
|
|
||||||
|
|
@ -39,7 +39,9 @@ impl LineIndex {
|
||||||
// Only track one line break for `\r\n`.
|
// Only track one line break for `\r\n`.
|
||||||
b'\r' if bytes.get(i + 1) == Some(&b'\n') => continue,
|
b'\r' if bytes.get(i + 1) == Some(&b'\n') => continue,
|
||||||
b'\n' | b'\r' => {
|
b'\n' | b'\r' => {
|
||||||
line_starts.push(TextSize::try_from(i + 1).unwrap());
|
// SAFETY: Assertion above guarantees `i <= u32::MAX`
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
line_starts.push(TextSize::from(i as u32) + TextSize::from(1));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ pub enum TokenKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenKind {
|
impl TokenKind {
|
||||||
|
#[inline]
|
||||||
pub const fn is_whitespace_needed(&self) -> bool {
|
pub const fn is_whitespace_needed(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
|
|
@ -197,6 +198,7 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_whitespace_optional(&self) -> bool {
|
pub const fn is_whitespace_optional(&self) -> bool {
|
||||||
self.is_arithmetic()
|
self.is_arithmetic()
|
||||||
|| matches!(
|
|| matches!(
|
||||||
|
|
@ -210,6 +212,7 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_unary(&self) -> bool {
|
pub const fn is_unary(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
|
|
@ -221,6 +224,7 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_keyword(&self) -> bool {
|
pub const fn is_keyword(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
|
|
@ -261,6 +265,7 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_operator(&self) -> bool {
|
pub const fn is_operator(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
|
|
@ -313,10 +318,12 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_singleton(&self) -> bool {
|
pub const fn is_singleton(&self) -> bool {
|
||||||
matches!(self, TokenKind::False | TokenKind::True | TokenKind::None)
|
matches!(self, TokenKind::False | TokenKind::True | TokenKind::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_skip_comment(&self) -> bool {
|
pub const fn is_skip_comment(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
|
|
@ -328,6 +335,7 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_arithmetic(&self) -> bool {
|
pub const fn is_arithmetic(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
|
|
@ -340,6 +348,7 @@ impl TokenKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn is_soft_keyword(&self) -> bool {
|
pub const fn is_soft_keyword(&self) -> bool {
|
||||||
matches!(self, TokenKind::Match | TokenKind::Case)
|
matches!(self, TokenKind::Match | TokenKind::Case)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue