mirror of https://github.com/astral-sh/ruff
Ignore precedence when identifying explicit concatenations
This commit is contained in:
parent
da33c26238
commit
c8cbf45ec2
|
|
@ -59,3 +59,13 @@ _ = "abc" + "def" + foo
|
||||||
_ = foo + bar + "abc"
|
_ = foo + bar + "abc"
|
||||||
_ = "abc" + foo + bar
|
_ = "abc" + foo + bar
|
||||||
_ = foo + "abc" + bar
|
_ = foo + "abc" + bar
|
||||||
|
|
||||||
|
_ = (
|
||||||
|
a + f"abc" +
|
||||||
|
"def"
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = (
|
||||||
|
f"abc" +
|
||||||
|
"def" + a
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ use crate::registry::Rule;
|
||||||
use crate::rules::{
|
use crate::rules::{
|
||||||
flake8_2020, flake8_async, flake8_bandit, flake8_boolean_trap, flake8_bugbear, flake8_builtins,
|
flake8_2020, flake8_async, flake8_bandit, flake8_boolean_trap, flake8_bugbear, flake8_builtins,
|
||||||
flake8_comprehensions, flake8_datetimez, flake8_debugger, flake8_django,
|
flake8_comprehensions, flake8_datetimez, flake8_debugger, flake8_django,
|
||||||
flake8_future_annotations, flake8_gettext, flake8_implicit_str_concat, flake8_logging_format,
|
flake8_future_annotations, flake8_gettext, flake8_logging_format, flake8_pie, flake8_print,
|
||||||
flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_self, flake8_simplify,
|
flake8_pyi, flake8_pytest_style, flake8_self, flake8_simplify, flake8_tidy_imports,
|
||||||
flake8_tidy_imports, flake8_use_pathlib, flynt, numpy, pandas_vet, pep8_naming, pycodestyle,
|
flake8_use_pathlib, flynt, numpy, pandas_vet, pep8_naming, pycodestyle, pyflakes, pygrep_hooks,
|
||||||
pyflakes, pygrep_hooks, pylint, pyupgrade, ruff,
|
pylint, pyupgrade, ruff,
|
||||||
};
|
};
|
||||||
use crate::settings::types::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
|
|
||||||
|
|
@ -1055,13 +1055,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
Expr::BinOp(ast::ExprBinOp {
|
Expr::BinOp(ast::ExprBinOp {
|
||||||
op: Operator::Add, ..
|
op: Operator::Add, ..
|
||||||
}) => {
|
}) => {
|
||||||
if checker.enabled(Rule::ExplicitStringConcatenation) {
|
|
||||||
if let Some(diagnostic) =
|
|
||||||
flake8_implicit_str_concat::rules::explicit(expr, checker.locator)
|
|
||||||
{
|
|
||||||
checker.diagnostics.push(diagnostic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if checker.enabled(Rule::CollectionLiteralConcatenation) {
|
if checker.enabled(Rule::CollectionLiteralConcatenation) {
|
||||||
ruff::rules::collection_literal_concatenation(checker, expr);
|
ruff::rules::collection_literal_concatenation(checker, expr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ pub(crate) fn check_tokens(
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.rules.any_enabled(&[
|
if settings.rules.any_enabled(&[
|
||||||
Rule::SingleLineImplicitStringConcatenation,
|
|
||||||
Rule::MultiLineImplicitStringConcatenation,
|
Rule::MultiLineImplicitStringConcatenation,
|
||||||
|
Rule::SingleLineImplicitStringConcatenation,
|
||||||
]) {
|
]) {
|
||||||
flake8_implicit_str_concat::rules::implicit(
|
flake8_implicit_str_concat::rules::implicit(
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
|
|
@ -130,6 +130,10 @@ pub(crate) fn check_tokens(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.rules.enabled(Rule::ExplicitStringConcatenation) {
|
||||||
|
flake8_implicit_str_concat::rules::explicit(&mut diagnostics, tokens);
|
||||||
|
}
|
||||||
|
|
||||||
if settings.rules.any_enabled(&[
|
if settings.rules.any_enabled(&[
|
||||||
Rule::MissingTrailingComma,
|
Rule::MissingTrailingComma,
|
||||||
Rule::TrailingCommaOnBareTuple,
|
Rule::TrailingCommaOnBareTuple,
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,7 @@ impl Rule {
|
||||||
| Rule::BlanketNOQA
|
| Rule::BlanketNOQA
|
||||||
| Rule::BlanketTypeIgnore
|
| Rule::BlanketTypeIgnore
|
||||||
| Rule::CommentedOutCode
|
| Rule::CommentedOutCode
|
||||||
|
| Rule::ExplicitStringConcatenation
|
||||||
| Rule::ExtraneousParentheses
|
| Rule::ExtraneousParentheses
|
||||||
| Rule::InvalidCharacterBackspace
|
| Rule::InvalidCharacterBackspace
|
||||||
| Rule::InvalidCharacterEsc
|
| Rule::InvalidCharacterEsc
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use rustpython_parser::ast::{self, Constant, Expr, Operator, Ranged};
|
use itertools::Itertools;
|
||||||
|
use ruff_text_size::TextRange;
|
||||||
|
use rustpython_parser::ast::Ranged;
|
||||||
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
use rustpython_parser::Tok;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::source_code::Locator;
|
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for string literals that are explicitly concatenated (using the
|
/// Checks for string literals that are explicitly concatenated (using the
|
||||||
|
|
@ -39,34 +42,31 @@ impl Violation for ExplicitStringConcatenation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ISC003
|
/// ISC003
|
||||||
pub(crate) fn explicit(expr: &Expr, locator: &Locator) -> Option<Diagnostic> {
|
pub(crate) fn explicit(diagnostics: &mut Vec<Diagnostic>, tokens: &[LexResult]) {
|
||||||
if let Expr::BinOp(ast::ExprBinOp {
|
for ((a_tok, a_range), (b_tok, _), (c_tok, _), (d_tok, d_range)) in tokens
|
||||||
left,
|
.iter()
|
||||||
op,
|
.flatten()
|
||||||
right,
|
.filter(|(tok, _)| !tok.is_comment())
|
||||||
range,
|
.tuple_windows()
|
||||||
}) = expr
|
|
||||||
{
|
{
|
||||||
if matches!(op, Operator::Add) {
|
if matches!(
|
||||||
if matches!(
|
(a_tok, b_tok, c_tok, d_tok),
|
||||||
left.as_ref(),
|
(
|
||||||
Expr::JoinedStr(_)
|
Tok::String { .. },
|
||||||
| Expr::Constant(ast::ExprConstant {
|
Tok::NonLogicalNewline,
|
||||||
value: Constant::Str(..) | Constant::Bytes(..),
|
Tok::Plus,
|
||||||
..
|
Tok::String { .. }
|
||||||
})
|
) | (
|
||||||
) && matches!(
|
Tok::String { .. },
|
||||||
right.as_ref(),
|
Tok::Plus,
|
||||||
Expr::JoinedStr(_)
|
Tok::NonLogicalNewline,
|
||||||
| Expr::Constant(ast::ExprConstant {
|
Tok::String { .. }
|
||||||
value: Constant::Str(..) | Constant::Bytes(..),
|
)
|
||||||
..
|
) {
|
||||||
})
|
diagnostics.push(Diagnostic::new(
|
||||||
) && locator.contains_line_break(*range)
|
ExplicitStringConcatenation,
|
||||||
{
|
TextRange::new(a_range.start(), d_range.end()),
|
||||||
return Some(Diagnostic::new(ExplicitStringConcatenation, expr.range()));
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,24 @@ ISC.py:19:3: ISC003 Explicitly concatenated string should be implicitly concaten
|
||||||
21 | )
|
21 | )
|
||||||
|
|
|
|
||||||
|
|
||||||
|
ISC.py:64:7: ISC003 Explicitly concatenated string should be implicitly concatenated
|
||||||
|
|
|
||||||
|
63 | _ = (
|
||||||
|
64 | a + f"abc" +
|
||||||
|
| _______^
|
||||||
|
65 | | "def"
|
||||||
|
| |_______^ ISC003
|
||||||
|
66 | )
|
||||||
|
|
|
||||||
|
|
||||||
|
ISC.py:69:3: ISC003 Explicitly concatenated string should be implicitly concatenated
|
||||||
|
|
|
||||||
|
68 | _ = (
|
||||||
|
69 | f"abc" +
|
||||||
|
| ___^
|
||||||
|
70 | | "def" + a
|
||||||
|
| |_______^ ISC003
|
||||||
|
71 | )
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,24 @@ ISC.py:19:3: ISC003 Explicitly concatenated string should be implicitly concaten
|
||||||
21 | )
|
21 | )
|
||||||
|
|
|
|
||||||
|
|
||||||
|
ISC.py:64:7: ISC003 Explicitly concatenated string should be implicitly concatenated
|
||||||
|
|
|
||||||
|
63 | _ = (
|
||||||
|
64 | a + f"abc" +
|
||||||
|
| _______^
|
||||||
|
65 | | "def"
|
||||||
|
| |_______^ ISC003
|
||||||
|
66 | )
|
||||||
|
|
|
||||||
|
|
||||||
|
ISC.py:69:3: ISC003 Explicitly concatenated string should be implicitly concatenated
|
||||||
|
|
|
||||||
|
68 | _ = (
|
||||||
|
69 | f"abc" +
|
||||||
|
| ___^
|
||||||
|
70 | | "def" + a
|
||||||
|
| |_______^ ISC003
|
||||||
|
71 | )
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue