mirror of https://github.com/astral-sh/ruff
Merge c017332867 into b0bc990cbf
This commit is contained in:
commit
15e0364c58
|
|
@ -4,6 +4,7 @@ extend-exclude = [
|
||||||
"crates/ty_vendored/vendor/**/*",
|
"crates/ty_vendored/vendor/**/*",
|
||||||
"**/resources/**/*",
|
"**/resources/**/*",
|
||||||
"**/snapshots/**/*",
|
"**/snapshots/**/*",
|
||||||
|
"crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/collection_literal.rs",
|
||||||
# Completion tests tend to have a lot of incomplete
|
# Completion tests tend to have a lot of incomplete
|
||||||
# words naturally. It's annoying to have to make all
|
# words naturally. It's annoying to have to make all
|
||||||
# of them actually words. So just ignore typos here.
|
# of them actually words. So just ignore typos here.
|
||||||
|
|
|
||||||
66
crates/ruff_linter/resources/test/fixtures/flake8_implicit_str_concat/ISC004.py
vendored
Normal file
66
crates/ruff_linter/resources/test/fixtures/flake8_implicit_str_concat/ISC004.py
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
facts = (
|
||||||
|
"Lobsters have blue blood.",
|
||||||
|
"The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
)
|
||||||
|
|
||||||
|
facts = [
|
||||||
|
"Lobsters have blue blood.",
|
||||||
|
"The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
]
|
||||||
|
|
||||||
|
facts = {
|
||||||
|
"Lobsters have blue blood.",
|
||||||
|
"The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
}
|
||||||
|
|
||||||
|
facts = {
|
||||||
|
(
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon."
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
facts = (
|
||||||
|
"Octopuses have three hearts."
|
||||||
|
# Missing comma here.
|
||||||
|
"Honey never spoils.",
|
||||||
|
)
|
||||||
|
|
||||||
|
facts = [
|
||||||
|
"Octopuses have three hearts."
|
||||||
|
# Missing comma here.
|
||||||
|
"Honey never spoils.",
|
||||||
|
]
|
||||||
|
|
||||||
|
facts = {
|
||||||
|
"Octopuses have three hearts."
|
||||||
|
# Missing comma here.
|
||||||
|
"Honey never spoils.",
|
||||||
|
}
|
||||||
|
|
||||||
|
facts = (
|
||||||
|
(
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
facts = [
|
||||||
|
(
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon."
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
facts = (
|
||||||
|
"Lobsters have blue blood.\n"
|
||||||
|
"The liver is the only human organ that can fully regenerate itself.\n"
|
||||||
|
"Clarinets are made almost entirely out of wood from the mpingo tree.\n"
|
||||||
|
"In 1971, astronaut Alan Shepard played golf on the moon.\n"
|
||||||
|
)
|
||||||
|
|
@ -214,6 +214,13 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||||
range: _,
|
range: _,
|
||||||
node_index: _,
|
node_index: _,
|
||||||
}) => {
|
}) => {
|
||||||
|
if checker.is_rule_enabled(Rule::ImplicitStringConcatenationInCollectionLiteral) {
|
||||||
|
flake8_implicit_str_concat::rules::implicit_string_concatenation_in_collection_literal(
|
||||||
|
checker,
|
||||||
|
expr,
|
||||||
|
elts,
|
||||||
|
);
|
||||||
|
}
|
||||||
if ctx.is_store() {
|
if ctx.is_store() {
|
||||||
let check_too_many_expressions =
|
let check_too_many_expressions =
|
||||||
checker.is_rule_enabled(Rule::ExpressionsInStarAssignment);
|
checker.is_rule_enabled(Rule::ExpressionsInStarAssignment);
|
||||||
|
|
@ -1329,6 +1336,13 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Set(set) => {
|
Expr::Set(set) => {
|
||||||
|
if checker.is_rule_enabled(Rule::ImplicitStringConcatenationInCollectionLiteral) {
|
||||||
|
flake8_implicit_str_concat::rules::implicit_string_concatenation_in_collection_literal(
|
||||||
|
checker,
|
||||||
|
expr,
|
||||||
|
&set.elts,
|
||||||
|
);
|
||||||
|
}
|
||||||
if checker.is_rule_enabled(Rule::DuplicateValue) {
|
if checker.is_rule_enabled(Rule::DuplicateValue) {
|
||||||
flake8_bugbear::rules::duplicate_value(checker, set);
|
flake8_bugbear::rules::duplicate_value(checker, set);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -454,6 +454,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Flake8ImplicitStrConcat, "001") => rules::flake8_implicit_str_concat::rules::SingleLineImplicitStringConcatenation,
|
(Flake8ImplicitStrConcat, "001") => rules::flake8_implicit_str_concat::rules::SingleLineImplicitStringConcatenation,
|
||||||
(Flake8ImplicitStrConcat, "002") => rules::flake8_implicit_str_concat::rules::MultiLineImplicitStringConcatenation,
|
(Flake8ImplicitStrConcat, "002") => rules::flake8_implicit_str_concat::rules::MultiLineImplicitStringConcatenation,
|
||||||
(Flake8ImplicitStrConcat, "003") => rules::flake8_implicit_str_concat::rules::ExplicitStringConcatenation,
|
(Flake8ImplicitStrConcat, "003") => rules::flake8_implicit_str_concat::rules::ExplicitStringConcatenation,
|
||||||
|
(Flake8ImplicitStrConcat, "004") => rules::flake8_implicit_str_concat::rules::ImplicitStringConcatenationInCollectionLiteral,
|
||||||
|
|
||||||
// flake8-print
|
// flake8-print
|
||||||
(Flake8Print, "1") => rules::flake8_print::rules::Print,
|
(Flake8Print, "1") => rules::flake8_print::rules::Print,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ mod tests {
|
||||||
Path::new("ISC_syntax_error_2.py")
|
Path::new("ISC_syntax_error_2.py")
|
||||||
)]
|
)]
|
||||||
#[test_case(Rule::ExplicitStringConcatenation, Path::new("ISC.py"))]
|
#[test_case(Rule::ExplicitStringConcatenation, Path::new("ISC.py"))]
|
||||||
|
#[test_case(
|
||||||
|
Rule::ImplicitStringConcatenationInCollectionLiteral,
|
||||||
|
Path::new("ISC004.py")
|
||||||
|
)]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
|
use ruff_python_ast::{Expr, StringLike};
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for implicitly concatenated strings inside list, tuple, and set literals.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// In collection literals, implicit string concatenation is often the result of
|
||||||
|
/// a missing comma between elements, which can silently merge items together.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// facts = (
|
||||||
|
/// "Lobsters have blue blood.",
|
||||||
|
/// "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
/// "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
/// "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Instead, you likely intended:
|
||||||
|
/// ```python
|
||||||
|
/// facts = (
|
||||||
|
/// "Lobsters have blue blood.",
|
||||||
|
/// "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
/// "Clarinets are made almost entirely out of wood from the mpingo tree.",
|
||||||
|
/// "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If the concatenation is intentional, wrap it in parentheses to make it
|
||||||
|
/// explicit:
|
||||||
|
/// ```python
|
||||||
|
/// facts = (
|
||||||
|
/// "Lobsters have blue blood.",
|
||||||
|
/// "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
/// (
|
||||||
|
/// "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
/// "In 1971, astronaut Alan Shepard played golf on the moon."
|
||||||
|
/// ),
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Fix safety
|
||||||
|
/// The fix is safe in that it does not change the semantics of your code.
|
||||||
|
/// However, the issue is that you may often want to change semantics
|
||||||
|
/// by adding a missing comma.
|
||||||
|
#[derive(ViolationMetadata)]
|
||||||
|
#[violation_metadata(preview_since = "0.14.10")]
|
||||||
|
pub(crate) struct ImplicitStringConcatenationInCollectionLiteral;
|
||||||
|
|
||||||
|
impl Violation for ImplicitStringConcatenationInCollectionLiteral {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Always;
|
||||||
|
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"Unparenthesized implicit string concatenation in collection".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_title(&self) -> Option<String> {
|
||||||
|
Some("Wrap implicitly concatenated strings in parentheses".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ISC004
|
||||||
|
pub(crate) fn implicit_string_concatenation_in_collection_literal(
|
||||||
|
checker: &Checker,
|
||||||
|
expr: &Expr,
|
||||||
|
elements: &[Expr],
|
||||||
|
) {
|
||||||
|
for element in elements {
|
||||||
|
let Ok(string_like) = StringLike::try_from(element) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !string_like.is_implicit_concatenated() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if parenthesized_range(
|
||||||
|
string_like.as_expression_ref(),
|
||||||
|
expr.into(),
|
||||||
|
checker.tokens(),
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
|
ImplicitStringConcatenationInCollectionLiteral,
|
||||||
|
string_like.range(),
|
||||||
|
);
|
||||||
|
diagnostic.help("Did you forget a comma?");
|
||||||
|
diagnostic.set_fix(Fix::unsafe_edits(
|
||||||
|
Edit::insertion("(".to_string(), string_like.range().start()),
|
||||||
|
[Edit::insertion(")".to_string(), string_like.range().end())],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
pub(crate) use collection_literal::*;
|
||||||
pub(crate) use explicit::*;
|
pub(crate) use explicit::*;
|
||||||
pub(crate) use implicit::*;
|
pub(crate) use implicit::*;
|
||||||
|
|
||||||
|
mod collection_literal;
|
||||||
mod explicit;
|
mod explicit;
|
||||||
mod implicit;
|
mod implicit;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_implicit_str_concat/mod.rs
|
||||||
|
---
|
||||||
|
ISC004 [*] Unparenthesized implicit string concatenation in collection
|
||||||
|
--> ISC004.py:4:5
|
||||||
|
|
|
||||||
|
2 | "Lobsters have blue blood.",
|
||||||
|
3 | "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
4 | / "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
5 | | "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
| |______________________________________________________________^
|
||||||
|
6 | )
|
||||||
|
|
|
||||||
|
help: Wrap implicitly concatenated strings in parentheses
|
||||||
|
help: Did you forget a comma?
|
||||||
|
1 | facts = (
|
||||||
|
2 | "Lobsters have blue blood.",
|
||||||
|
3 | "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
- "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
- "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
4 + ("Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
5 + "In 1971, astronaut Alan Shepard played golf on the moon."),
|
||||||
|
6 | )
|
||||||
|
7 |
|
||||||
|
8 | facts = [
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
ISC004 [*] Unparenthesized implicit string concatenation in collection
|
||||||
|
--> ISC004.py:11:5
|
||||||
|
|
|
||||||
|
9 | "Lobsters have blue blood.",
|
||||||
|
10 | "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
11 | / "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
12 | | "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
| |______________________________________________________________^
|
||||||
|
13 | ]
|
||||||
|
|
|
||||||
|
help: Wrap implicitly concatenated strings in parentheses
|
||||||
|
help: Did you forget a comma?
|
||||||
|
8 | facts = [
|
||||||
|
9 | "Lobsters have blue blood.",
|
||||||
|
10 | "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
- "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
- "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
11 + ("Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
12 + "In 1971, astronaut Alan Shepard played golf on the moon."),
|
||||||
|
13 | ]
|
||||||
|
14 |
|
||||||
|
15 | facts = {
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
ISC004 [*] Unparenthesized implicit string concatenation in collection
|
||||||
|
--> ISC004.py:18:5
|
||||||
|
|
|
||||||
|
16 | "Lobsters have blue blood.",
|
||||||
|
17 | "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
18 | / "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
19 | | "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
| |______________________________________________________________^
|
||||||
|
20 | }
|
||||||
|
|
|
||||||
|
help: Wrap implicitly concatenated strings in parentheses
|
||||||
|
help: Did you forget a comma?
|
||||||
|
15 | facts = {
|
||||||
|
16 | "Lobsters have blue blood.",
|
||||||
|
17 | "The liver is the only human organ that can fully regenerate itself.",
|
||||||
|
- "Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
- "In 1971, astronaut Alan Shepard played golf on the moon.",
|
||||||
|
18 + ("Clarinets are made almost entirely out of wood from the mpingo tree."
|
||||||
|
19 + "In 1971, astronaut Alan Shepard played golf on the moon."),
|
||||||
|
20 | }
|
||||||
|
21 |
|
||||||
|
22 | facts = {
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
ISC004 [*] Unparenthesized implicit string concatenation in collection
|
||||||
|
--> ISC004.py:30:5
|
||||||
|
|
|
||||||
|
29 | facts = (
|
||||||
|
30 | / "Octopuses have three hearts."
|
||||||
|
31 | | # Missing comma here.
|
||||||
|
32 | | "Honey never spoils.",
|
||||||
|
| |_________________________^
|
||||||
|
33 | )
|
||||||
|
|
|
||||||
|
help: Wrap implicitly concatenated strings in parentheses
|
||||||
|
help: Did you forget a comma?
|
||||||
|
27 | }
|
||||||
|
28 |
|
||||||
|
29 | facts = (
|
||||||
|
- "Octopuses have three hearts."
|
||||||
|
30 + ("Octopuses have three hearts."
|
||||||
|
31 | # Missing comma here.
|
||||||
|
- "Honey never spoils.",
|
||||||
|
32 + "Honey never spoils."),
|
||||||
|
33 | )
|
||||||
|
34 |
|
||||||
|
35 | facts = [
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
ISC004 [*] Unparenthesized implicit string concatenation in collection
|
||||||
|
--> ISC004.py:36:5
|
||||||
|
|
|
||||||
|
35 | facts = [
|
||||||
|
36 | / "Octopuses have three hearts."
|
||||||
|
37 | | # Missing comma here.
|
||||||
|
38 | | "Honey never spoils.",
|
||||||
|
| |_________________________^
|
||||||
|
39 | ]
|
||||||
|
|
|
||||||
|
help: Wrap implicitly concatenated strings in parentheses
|
||||||
|
help: Did you forget a comma?
|
||||||
|
33 | )
|
||||||
|
34 |
|
||||||
|
35 | facts = [
|
||||||
|
- "Octopuses have three hearts."
|
||||||
|
36 + ("Octopuses have three hearts."
|
||||||
|
37 | # Missing comma here.
|
||||||
|
- "Honey never spoils.",
|
||||||
|
38 + "Honey never spoils."),
|
||||||
|
39 | ]
|
||||||
|
40 |
|
||||||
|
41 | facts = {
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
ISC004 [*] Unparenthesized implicit string concatenation in collection
|
||||||
|
--> ISC004.py:42:5
|
||||||
|
|
|
||||||
|
41 | facts = {
|
||||||
|
42 | / "Octopuses have three hearts."
|
||||||
|
43 | | # Missing comma here.
|
||||||
|
44 | | "Honey never spoils.",
|
||||||
|
| |_________________________^
|
||||||
|
45 | }
|
||||||
|
|
|
||||||
|
help: Wrap implicitly concatenated strings in parentheses
|
||||||
|
help: Did you forget a comma?
|
||||||
|
39 | ]
|
||||||
|
40 |
|
||||||
|
41 | facts = {
|
||||||
|
- "Octopuses have three hearts."
|
||||||
|
42 + ("Octopuses have three hearts."
|
||||||
|
43 | # Missing comma here.
|
||||||
|
- "Honey never spoils.",
|
||||||
|
44 + "Honey never spoils."),
|
||||||
|
45 | }
|
||||||
|
46 |
|
||||||
|
47 | facts = (
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
@ -3482,6 +3482,7 @@
|
||||||
"ISC001",
|
"ISC001",
|
||||||
"ISC002",
|
"ISC002",
|
||||||
"ISC003",
|
"ISC003",
|
||||||
|
"ISC004",
|
||||||
"LOG",
|
"LOG",
|
||||||
"LOG0",
|
"LOG0",
|
||||||
"LOG00",
|
"LOG00",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue