Compare commits
13 Commits
david/skip
...
dylan/stab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8316b909d9 | ||
|
|
829acf498d | ||
|
|
e07f352f99 | ||
|
|
8d0b6882b7 | ||
|
|
65a2daea02 | ||
|
|
8baaa2f7f3 | ||
|
|
8b1ce32f04 | ||
|
|
eb5abda8ac | ||
|
|
9c4ecf77b6 | ||
|
|
0809d88ca0 | ||
|
|
5c59167686 | ||
|
|
e2ea301c74 | ||
|
|
62364ea47e |
@@ -5436,14 +5436,15 @@ match 2:
|
||||
print("it's one")
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
test.py:2:1: SyntaxError: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
// syntax error on 3.9 with preview
|
||||
|
||||
@@ -65,7 +65,7 @@ use crate::docstrings::extraction::ExtractionTarget;
|
||||
use crate::importer::{ImportRequest, Importer, ResolutionError};
|
||||
use crate::noqa::NoqaMapping;
|
||||
use crate::package::PackageRoot;
|
||||
use crate::preview::{is_semantic_errors_enabled, is_undefined_export_in_dunder_init_enabled};
|
||||
use crate::preview::is_undefined_export_in_dunder_init_enabled;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::rules::pyflakes::rules::{
|
||||
LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
|
||||
@@ -663,9 +663,7 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
| SemanticSyntaxErrorKind::AsyncComprehensionInSyncComprehension(_)
|
||||
| SemanticSyntaxErrorKind::DuplicateParameter(_)
|
||||
| SemanticSyntaxErrorKind::NonlocalDeclarationAtModuleLevel => {
|
||||
if is_semantic_errors_enabled(self.settings) {
|
||||
self.semantic_errors.borrow_mut().push(error);
|
||||
}
|
||||
self.semantic_errors.borrow_mut().push(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::fix::edits::delete_comment;
|
||||
use crate::noqa::{
|
||||
Code, Directive, FileExemption, FileNoqaDirectives, NoqaDirectives, NoqaMapping,
|
||||
};
|
||||
use crate::preview::is_check_file_level_directives_enabled;
|
||||
use crate::registry::{AsRule, Rule, RuleSet};
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::rules::pygrep_hooks;
|
||||
@@ -112,25 +111,16 @@ pub(crate) fn check_noqa(
|
||||
&& !exemption.includes(Rule::UnusedNOQA)
|
||||
&& !per_file_ignores.contains(Rule::UnusedNOQA)
|
||||
{
|
||||
let directives: Vec<_> = if is_check_file_level_directives_enabled(settings) {
|
||||
noqa_directives
|
||||
.lines()
|
||||
.iter()
|
||||
.map(|line| (&line.directive, &line.matches, false))
|
||||
.chain(
|
||||
file_noqa_directives
|
||||
.lines()
|
||||
.iter()
|
||||
.map(|line| (&line.parsed_file_exemption, &line.matches, true)),
|
||||
)
|
||||
.collect()
|
||||
} else {
|
||||
noqa_directives
|
||||
.lines()
|
||||
.iter()
|
||||
.map(|line| (&line.directive, &line.matches, false))
|
||||
.collect()
|
||||
};
|
||||
let directives = noqa_directives
|
||||
.lines()
|
||||
.iter()
|
||||
.map(|line| (&line.directive, &line.matches, false))
|
||||
.chain(
|
||||
file_noqa_directives
|
||||
.lines()
|
||||
.iter()
|
||||
.map(|line| (&line.parsed_file_exemption, &line.matches, true)),
|
||||
);
|
||||
for (directive, matches, is_file_level) in directives {
|
||||
match directive {
|
||||
Directive::All(directive) => {
|
||||
|
||||
@@ -1019,7 +1019,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "049") => (RuleGroup::Preview, rules::ruff::rules::DataclassEnum),
|
||||
(Ruff, "051") => (RuleGroup::Stable, rules::ruff::rules::IfKeyInDictDel),
|
||||
(Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable),
|
||||
(Ruff, "053") => (RuleGroup::Preview, rules::ruff::rules::ClassWithMixedTypeVars),
|
||||
(Ruff, "053") => (RuleGroup::Stable, rules::ruff::rules::ClassWithMixedTypeVars),
|
||||
(Ruff, "054") => (RuleGroup::Preview, rules::ruff::rules::IndentedFormFeed),
|
||||
(Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression),
|
||||
(Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback),
|
||||
@@ -1121,7 +1121,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Refurb, "132") => (RuleGroup::Preview, rules::refurb::rules::CheckAndRemoveFromSet),
|
||||
(Refurb, "136") => (RuleGroup::Stable, rules::refurb::rules::IfExprMinMax),
|
||||
(Refurb, "140") => (RuleGroup::Preview, rules::refurb::rules::ReimplementedStarmap),
|
||||
(Refurb, "142") => (RuleGroup::Preview, rules::refurb::rules::ForLoopSetMutations),
|
||||
(Refurb, "142") => (RuleGroup::Stable, rules::refurb::rules::ForLoopSetMutations),
|
||||
(Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy),
|
||||
(Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate),
|
||||
(Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant),
|
||||
@@ -1129,7 +1129,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Refurb, "156") => (RuleGroup::Preview, rules::refurb::rules::HardcodedStringCharset),
|
||||
(Refurb, "157") => (RuleGroup::Preview, rules::refurb::rules::VerboseDecimalConstructor),
|
||||
(Refurb, "161") => (RuleGroup::Stable, rules::refurb::rules::BitCount),
|
||||
(Refurb, "162") => (RuleGroup::Preview, rules::refurb::rules::FromisoformatReplaceZ),
|
||||
(Refurb, "162") => (RuleGroup::Stable, rules::refurb::rules::FromisoformatReplaceZ),
|
||||
(Refurb, "163") => (RuleGroup::Stable, rules::refurb::rules::RedundantLogBase),
|
||||
(Refurb, "164") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryFromFloat),
|
||||
(Refurb, "166") => (RuleGroup::Preview, rules::refurb::rules::IntOnSlicedStr),
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::fix::{FixResult, fix_file};
|
||||
use crate::message::Message;
|
||||
use crate::noqa::add_noqa;
|
||||
use crate::package::PackageRoot;
|
||||
use crate::preview::{is_py314_support_enabled, is_unsupported_syntax_enabled};
|
||||
use crate::preview::is_py314_support_enabled;
|
||||
use crate::registry::{AsRule, Rule, RuleSet};
|
||||
#[cfg(any(feature = "test-rules", test))]
|
||||
use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule};
|
||||
@@ -447,11 +447,7 @@ pub fn check_path(
|
||||
}
|
||||
}
|
||||
|
||||
let syntax_errors = if is_unsupported_syntax_enabled(settings) {
|
||||
parsed.unsupported_syntax_errors()
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
let syntax_errors = parsed.unsupported_syntax_errors();
|
||||
|
||||
diagnostics_to_messages(
|
||||
diagnostics,
|
||||
|
||||
@@ -7,17 +7,6 @@
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
// https://github.com/astral-sh/ruff/issues/17412
|
||||
// https://github.com/astral-sh/ruff/issues/11934
|
||||
pub(crate) const fn is_semantic_errors_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/16429
|
||||
pub(crate) const fn is_unsupported_syntax_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
pub(crate) const fn is_py314_support_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
@@ -29,23 +18,11 @@ pub(crate) const fn is_full_path_match_source_strategy_enabled(settings: &Linter
|
||||
|
||||
// Rule-specific behavior
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/17136
|
||||
pub(crate) const fn is_shell_injection_only_trusted_input_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/15541
|
||||
pub(crate) const fn is_suspicious_function_reference_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/7501
|
||||
pub(crate) const fn is_bool_subtype_of_annotation_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/10759
|
||||
pub(crate) const fn is_comprehension_with_min_max_sum_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
@@ -63,21 +40,11 @@ pub(crate) const fn is_bad_version_info_in_non_stub_enabled(settings: &LinterSet
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/12676
|
||||
pub(crate) const fn is_fix_future_annotations_in_stub_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/11074
|
||||
pub(crate) const fn is_only_add_return_none_at_end_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/12796
|
||||
pub(crate) const fn is_simplify_ternary_to_binary_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/16719
|
||||
pub(crate) const fn is_fix_manual_dict_comprehension_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
@@ -104,13 +71,6 @@ pub(crate) const fn is_unicode_to_unicode_confusables_enabled(settings: &LinterS
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/17078
|
||||
pub(crate) const fn is_support_slices_in_literal_concatenation_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/11370
|
||||
pub(crate) const fn is_undefined_export_in_dunder_init_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
@@ -121,16 +81,9 @@ pub(crate) const fn is_allow_nested_roots_enabled(settings: &LinterSettings) ->
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/17061
|
||||
pub(crate) const fn is_check_file_level_directives_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/17644
|
||||
pub(crate) const fn is_readlines_in_for_fix_safe_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
pub(crate) const fn multiple_with_statements_fix_safe_enabled(settings: &LinterSettings) -> bool {
|
||||
// https://github.com/astral-sh/ruff/pull/18208
|
||||
pub(crate) const fn is_multiple_with_statements_fix_safe_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ mod tests {
|
||||
#[test_case(Rule::SuspiciousURLOpenUsage, Path::new("S310.py"))]
|
||||
#[test_case(Rule::SuspiciousNonCryptographicRandomUsage, Path::new("S311.py"))]
|
||||
#[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"))]
|
||||
#[test_case(Rule::SubprocessWithoutShellEqualsTrue, Path::new("S603.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
||||
@@ -7,7 +7,6 @@ use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::Violation;
|
||||
use crate::preview::is_shell_injection_only_trusted_input_enabled;
|
||||
use crate::{
|
||||
checkers::ast::Checker, registry::Rule, rules::flake8_bandit::helpers::string_literal,
|
||||
};
|
||||
@@ -325,9 +324,7 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
|
||||
}
|
||||
// S603
|
||||
_ => {
|
||||
if !is_trusted_input(arg)
|
||||
|| !is_shell_injection_only_trusted_input_enabled(checker.settings)
|
||||
{
|
||||
if !is_trusted_input(arg) {
|
||||
if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) {
|
||||
checker.report_diagnostic(
|
||||
SubprocessWithoutShellEqualsTrue,
|
||||
|
||||
@@ -106,74 +106,6 @@ S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
23 | # Literals are fine, they're trusted.
|
||||
|
|
||||
|
||||
S603.py:24:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
23 | # Literals are fine, they're trusted.
|
||||
24 | run("true")
|
||||
| ^^^ S603
|
||||
25 | Popen(["true"])
|
||||
26 | Popen("true", shell=False)
|
||||
|
|
||||
|
||||
S603.py:25:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
23 | # Literals are fine, they're trusted.
|
||||
24 | run("true")
|
||||
25 | Popen(["true"])
|
||||
| ^^^^^ S603
|
||||
26 | Popen("true", shell=False)
|
||||
27 | call("true", shell=False)
|
||||
|
|
||||
|
||||
S603.py:26:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
24 | run("true")
|
||||
25 | Popen(["true"])
|
||||
26 | Popen("true", shell=False)
|
||||
| ^^^^^ S603
|
||||
27 | call("true", shell=False)
|
||||
28 | check_call("true", shell=False)
|
||||
|
|
||||
|
||||
S603.py:27:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
25 | Popen(["true"])
|
||||
26 | Popen("true", shell=False)
|
||||
27 | call("true", shell=False)
|
||||
| ^^^^ S603
|
||||
28 | check_call("true", shell=False)
|
||||
29 | check_output("true", shell=False)
|
||||
|
|
||||
|
||||
S603.py:28:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
26 | Popen("true", shell=False)
|
||||
27 | call("true", shell=False)
|
||||
28 | check_call("true", shell=False)
|
||||
| ^^^^^^^^^^ S603
|
||||
29 | check_output("true", shell=False)
|
||||
30 | run("true", shell=False)
|
||||
|
|
||||
|
||||
S603.py:29:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
27 | call("true", shell=False)
|
||||
28 | check_call("true", shell=False)
|
||||
29 | check_output("true", shell=False)
|
||||
| ^^^^^^^^^^^^ S603
|
||||
30 | run("true", shell=False)
|
||||
|
|
||||
|
||||
S603.py:30:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
28 | check_call("true", shell=False)
|
||||
29 | check_output("true", shell=False)
|
||||
30 | run("true", shell=False)
|
||||
| ^^^ S603
|
||||
31 |
|
||||
32 | # Not through assignments though.
|
||||
|
|
||||
|
||||
S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
32 | # Not through assignments though.
|
||||
@@ -184,15 +116,6 @@ S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
36 | # Instant named expressions are fine.
|
||||
|
|
||||
|
||||
S603.py:37:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
36 | # Instant named expressions are fine.
|
||||
37 | run(c := "true")
|
||||
| ^^^ S603
|
||||
38 |
|
||||
39 | # But non-instant are not.
|
||||
|
|
||||
|
||||
S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
39 | # But non-instant are not.
|
||||
@@ -200,20 +123,3 @@ S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
41 | run(e)
|
||||
| ^^^ S603
|
||||
|
|
||||
|
||||
S603.py:46:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
44 | # https://github.com/astral-sh/ruff/issues/17798
|
||||
45 | # Tuple literals are trusted
|
||||
46 | check_output(("literal", "cmd", "using", "tuple"), text=True)
|
||||
| ^^^^^^^^^^^^ S603
|
||||
47 | Popen(("literal", "cmd", "using", "tuple"))
|
||||
|
|
||||
|
||||
S603.py:47:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
45 | # Tuple literals are trusted
|
||||
46 | check_output(("literal", "cmd", "using", "tuple"), text=True)
|
||||
47 | Popen(("literal", "cmd", "using", "tuple"))
|
||||
| ^^^^^ S603
|
||||
|
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
|
||||
---
|
||||
S603.py:5:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
3 | # Different Popen wrappers are checked.
|
||||
4 | a = input()
|
||||
5 | Popen(a, shell=False)
|
||||
| ^^^^^ S603
|
||||
6 | call(a, shell=False)
|
||||
7 | check_call(a, shell=False)
|
||||
|
|
||||
|
||||
S603.py:6:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
4 | a = input()
|
||||
5 | Popen(a, shell=False)
|
||||
6 | call(a, shell=False)
|
||||
| ^^^^ S603
|
||||
7 | check_call(a, shell=False)
|
||||
8 | check_output(a, shell=False)
|
||||
|
|
||||
|
||||
S603.py:7:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
5 | Popen(a, shell=False)
|
||||
6 | call(a, shell=False)
|
||||
7 | check_call(a, shell=False)
|
||||
| ^^^^^^^^^^ S603
|
||||
8 | check_output(a, shell=False)
|
||||
9 | run(a, shell=False)
|
||||
|
|
||||
|
||||
S603.py:8:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
6 | call(a, shell=False)
|
||||
7 | check_call(a, shell=False)
|
||||
8 | check_output(a, shell=False)
|
||||
| ^^^^^^^^^^^^ S603
|
||||
9 | run(a, shell=False)
|
||||
|
|
||||
|
||||
S603.py:9:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
7 | check_call(a, shell=False)
|
||||
8 | check_output(a, shell=False)
|
||||
9 | run(a, shell=False)
|
||||
| ^^^ S603
|
||||
10 |
|
||||
11 | # Falsey values are treated as false.
|
||||
|
|
||||
|
||||
S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
11 | # Falsey values are treated as false.
|
||||
12 | Popen(a, shell=0)
|
||||
| ^^^^^ S603
|
||||
13 | Popen(a, shell=[])
|
||||
14 | Popen(a, shell={})
|
||||
|
|
||||
|
||||
S603.py:13:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
11 | # Falsey values are treated as false.
|
||||
12 | Popen(a, shell=0)
|
||||
13 | Popen(a, shell=[])
|
||||
| ^^^^^ S603
|
||||
14 | Popen(a, shell={})
|
||||
15 | Popen(a, shell=None)
|
||||
|
|
||||
|
||||
S603.py:14:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
12 | Popen(a, shell=0)
|
||||
13 | Popen(a, shell=[])
|
||||
14 | Popen(a, shell={})
|
||||
| ^^^^^ S603
|
||||
15 | Popen(a, shell=None)
|
||||
|
|
||||
|
||||
S603.py:15:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
13 | Popen(a, shell=[])
|
||||
14 | Popen(a, shell={})
|
||||
15 | Popen(a, shell=None)
|
||||
| ^^^^^ S603
|
||||
16 |
|
||||
17 | # Unknown values are treated as falsey.
|
||||
|
|
||||
|
||||
S603.py:18:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
17 | # Unknown values are treated as falsey.
|
||||
18 | Popen(a, shell=True if True else False)
|
||||
| ^^^^^ S603
|
||||
19 |
|
||||
20 | # No value is also caught.
|
||||
|
|
||||
|
||||
S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
20 | # No value is also caught.
|
||||
21 | Popen(a)
|
||||
| ^^^^^ S603
|
||||
22 |
|
||||
23 | # Literals are fine, they're trusted.
|
||||
|
|
||||
|
||||
S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
32 | # Not through assignments though.
|
||||
33 | cmd = ["true"]
|
||||
34 | run(cmd)
|
||||
| ^^^ S603
|
||||
35 |
|
||||
36 | # Instant named expressions are fine.
|
||||
|
|
||||
|
||||
S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input
|
||||
|
|
||||
39 | # But non-instant are not.
|
||||
40 | (e := "echo")
|
||||
41 | run(e)
|
||||
| ^^^ S603
|
||||
|
|
||||
@@ -12,7 +12,6 @@ mod tests {
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
@@ -29,24 +28,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::BooleanTypeHintPositionalArgument, Path::new("FBT.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_boolean_trap").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_allowed_callable() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -7,12 +7,12 @@ use ruff_python_semantic::analyze::visibility;
|
||||
|
||||
use crate::Violation;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_bool_subtype_of_annotation_enabled;
|
||||
use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of boolean positional arguments in function definitions,
|
||||
/// as determined by the presence of a `bool` type hint.
|
||||
/// as determined by the presence of a type hint containing `bool` as an
|
||||
/// evident subtype - e.g. `bool`, `bool | int`, `typing.Optional[bool]`, etc.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Calling a function with boolean positional arguments is confusing as the
|
||||
@@ -30,9 +30,6 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||
/// Dunder methods that define operators are exempt from this rule, as are
|
||||
/// setters and `@override` definitions.
|
||||
///
|
||||
/// In [preview], this rule will also flag annotations that include boolean
|
||||
/// variants, like `bool | int`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```python
|
||||
@@ -96,8 +93,6 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||
/// ## References
|
||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
||||
///
|
||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct BooleanTypeHintPositionalArgument;
|
||||
|
||||
@@ -128,14 +123,8 @@ pub(crate) fn boolean_type_hint_positional_argument(
|
||||
let Some(annotation) = parameter.annotation() else {
|
||||
continue;
|
||||
};
|
||||
if is_bool_subtype_of_annotation_enabled(checker.settings) {
|
||||
if !match_annotation_to_complex_bool(annotation, checker.semantic()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if !match_annotation_to_literal_bool(annotation) {
|
||||
continue;
|
||||
}
|
||||
if !match_annotation_to_complex_bool(annotation, checker.semantic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allow Boolean type hints in setters.
|
||||
@@ -161,17 +150,6 @@ pub(crate) fn boolean_type_hint_positional_argument(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the annotation is a boolean type hint (e.g., `bool`).
|
||||
fn match_annotation_to_literal_bool(annotation: &Expr) -> bool {
|
||||
match annotation {
|
||||
// Ex) `True`
|
||||
Expr::Name(name) => &name.id == "bool",
|
||||
// Ex) `"True"`
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool",
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the annotation is a boolean type hint (e.g., `bool`), or a type hint that
|
||||
/// includes boolean as a variant (e.g., `bool | int`).
|
||||
fn match_annotation_to_complex_bool(annotation: &Expr, semantic: &SemanticModel) -> bool {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
FBT.py:4:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
@@ -89,3 +88,17 @@ FBT.py:90:19: FBT001 Boolean-typed positional argument in function definition
|
||||
| ^^^^^ FBT001
|
||||
91 | pass
|
||||
|
|
||||
|
||||
FBT.py:100:10: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
100 | def func(x: Union[list, Optional[int | str | float | bool]]):
|
||||
| ^ FBT001
|
||||
101 | pass
|
||||
|
|
||||
|
||||
FBT.py:104:10: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
104 | def func(x: bool | str):
|
||||
| ^ FBT001
|
||||
105 | pass
|
||||
|
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
FBT.py:4:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
2 | posonly_nohint,
|
||||
3 | posonly_nonboolhint: int,
|
||||
4 | posonly_boolhint: bool,
|
||||
| ^^^^^^^^^^^^^^^^ FBT001
|
||||
5 | posonly_boolstrhint: "bool",
|
||||
6 | /,
|
||||
|
|
||||
|
||||
FBT.py:5:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
3 | posonly_nonboolhint: int,
|
||||
4 | posonly_boolhint: bool,
|
||||
5 | posonly_boolstrhint: "bool",
|
||||
| ^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
6 | /,
|
||||
7 | offset,
|
||||
|
|
||||
|
||||
FBT.py:10:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
8 | posorkw_nonvalued_nohint,
|
||||
9 | posorkw_nonvalued_nonboolhint: int,
|
||||
10 | posorkw_nonvalued_boolhint: bool,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
11 | posorkw_nonvalued_boolstrhint: "bool",
|
||||
12 | posorkw_boolvalued_nohint=True,
|
||||
|
|
||||
|
||||
FBT.py:11:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
9 | posorkw_nonvalued_nonboolhint: int,
|
||||
10 | posorkw_nonvalued_boolhint: bool,
|
||||
11 | posorkw_nonvalued_boolstrhint: "bool",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
12 | posorkw_boolvalued_nohint=True,
|
||||
13 | posorkw_boolvalued_nonboolhint: int = True,
|
||||
|
|
||||
|
||||
FBT.py:14:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
12 | posorkw_boolvalued_nohint=True,
|
||||
13 | posorkw_boolvalued_nonboolhint: int = True,
|
||||
14 | posorkw_boolvalued_boolhint: bool = True,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
15 | posorkw_boolvalued_boolstrhint: "bool" = True,
|
||||
16 | posorkw_nonboolvalued_nohint=1,
|
||||
|
|
||||
|
||||
FBT.py:15:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
13 | posorkw_boolvalued_nonboolhint: int = True,
|
||||
14 | posorkw_boolvalued_boolhint: bool = True,
|
||||
15 | posorkw_boolvalued_boolstrhint: "bool" = True,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
16 | posorkw_nonboolvalued_nohint=1,
|
||||
17 | posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
|
|
||||
|
||||
FBT.py:18:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
16 | posorkw_nonboolvalued_nohint=1,
|
||||
17 | posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
18 | posorkw_nonboolvalued_boolhint: bool = 3,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
19 | posorkw_nonboolvalued_boolstrhint: "bool" = 4,
|
||||
20 | *,
|
||||
|
|
||||
|
||||
FBT.py:19:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
17 | posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
18 | posorkw_nonboolvalued_boolhint: bool = 3,
|
||||
19 | posorkw_nonboolvalued_boolstrhint: "bool" = 4,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
20 | *,
|
||||
21 | kwonly_nonvalued_nohint,
|
||||
|
|
||||
|
||||
FBT.py:90:19: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
89 | # FBT001: Boolean positional arg in function definition
|
||||
90 | def foo(self, value: bool) -> None:
|
||||
| ^^^^^ FBT001
|
||||
91 | pass
|
||||
|
|
||||
|
||||
FBT.py:100:10: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
100 | def func(x: Union[list, Optional[int | str | float | bool]]):
|
||||
| ^ FBT001
|
||||
101 | pass
|
||||
|
|
||||
|
||||
FBT.py:104:10: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
104 | def func(x: bool | str):
|
||||
| ^ FBT001
|
||||
105 | pass
|
||||
|
|
||||
@@ -11,7 +11,6 @@ mod tests {
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::pep8_naming;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
@@ -172,22 +171,4 @@ mod tests {
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_pyi").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruff_python_ast::StmtImportFrom;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
|
||||
use crate::{Fix, FixAvailability, Violation};
|
||||
use crate::{checkers::ast::Checker, fix, preview::is_fix_future_annotations_in_stub_enabled};
|
||||
use crate::{checkers::ast::Checker, fix};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of the `from __future__ import annotations` import
|
||||
@@ -55,20 +55,18 @@ pub(crate) fn from_future_import(checker: &Checker, target: &StmtImportFrom) {
|
||||
|
||||
let mut diagnostic = checker.report_diagnostic(FutureAnnotationsInStub, *range);
|
||||
|
||||
if is_fix_future_annotations_in_stub_enabled(checker.settings) {
|
||||
let stmt = checker.semantic().current_statement();
|
||||
let stmt = checker.semantic().current_statement();
|
||||
|
||||
diagnostic.try_set_fix(|| {
|
||||
let edit = fix::edits::remove_unused_imports(
|
||||
std::iter::once("annotations"),
|
||||
stmt,
|
||||
None,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
)?;
|
||||
diagnostic.try_set_fix(|| {
|
||||
let edit = fix::edits::remove_unused_imports(
|
||||
std::iter::once("annotations"),
|
||||
stmt,
|
||||
None,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
)?;
|
||||
|
||||
Ok(Fix::safe_edit(edit))
|
||||
});
|
||||
}
|
||||
Ok(Fix::safe_edit(edit))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI044.pyi:2:1: PYI044 `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
PYI044.pyi:2:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
|
|
||||
1 | # Bad import.
|
||||
2 | from __future__ import annotations # PYI044.
|
||||
@@ -10,7 +10,14 @@ PYI044.pyi:2:1: PYI044 `from __future__ import annotations` has no effect in stu
|
||||
|
|
||||
= help: Remove `from __future__ import annotations`
|
||||
|
||||
PYI044.pyi:3:1: PYI044 `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
ℹ Safe fix
|
||||
1 1 | # Bad import.
|
||||
2 |-from __future__ import annotations # PYI044.
|
||||
3 2 | from __future__ import annotations, with_statement # PYI044.
|
||||
4 3 |
|
||||
5 4 | # Good imports.
|
||||
|
||||
PYI044.pyi:3:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
|
|
||||
1 | # Bad import.
|
||||
2 | from __future__ import annotations # PYI044.
|
||||
@@ -20,3 +27,12 @@ PYI044.pyi:3:1: PYI044 `from __future__ import annotations` has no effect in stu
|
||||
5 | # Good imports.
|
||||
|
|
||||
= help: Remove `from __future__ import annotations`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Bad import.
|
||||
2 2 | from __future__ import annotations # PYI044.
|
||||
3 |-from __future__ import annotations, with_statement # PYI044.
|
||||
3 |+from __future__ import with_statement # PYI044.
|
||||
4 4 |
|
||||
5 5 | # Good imports.
|
||||
6 6 | from __future__ import with_statement
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI044.pyi:2:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
|
|
||||
1 | # Bad import.
|
||||
2 | from __future__ import annotations # PYI044.
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI044
|
||||
3 | from __future__ import annotations, with_statement # PYI044.
|
||||
|
|
||||
= help: Remove `from __future__ import annotations`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Bad import.
|
||||
2 |-from __future__ import annotations # PYI044.
|
||||
3 2 | from __future__ import annotations, with_statement # PYI044.
|
||||
4 3 |
|
||||
5 4 | # Good imports.
|
||||
|
||||
PYI044.pyi:3:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
|
|
||||
1 | # Bad import.
|
||||
2 | from __future__ import annotations # PYI044.
|
||||
3 | from __future__ import annotations, with_statement # PYI044.
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI044
|
||||
4 |
|
||||
5 | # Good imports.
|
||||
|
|
||||
= help: Remove `from __future__ import annotations`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Bad import.
|
||||
2 2 | from __future__ import annotations # PYI044.
|
||||
3 |-from __future__ import annotations, with_statement # PYI044.
|
||||
3 |+from __future__ import with_statement # PYI044.
|
||||
4 4 |
|
||||
5 5 | # Good imports.
|
||||
6 6 | from __future__ import with_statement
|
||||
@@ -58,7 +58,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::IfElseBlockInsteadOfIfExp, Path::new("SIM108.py"))]
|
||||
#[test_case(Rule::MultipleWithStatements, Path::new("SIM117.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::fix_with;
|
||||
use crate::Fix;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::preview::multiple_with_statements_fix_safe_enabled;
|
||||
use crate::preview::is_multiple_with_statements_fix_safe_enabled;
|
||||
use crate::{FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
@@ -195,7 +195,7 @@ pub(crate) fn multiple_with_statements(
|
||||
checker.settings.tab_size,
|
||||
)
|
||||
}) {
|
||||
if multiple_with_statements_fix_safe_enabled(checker.settings) {
|
||||
if is_multiple_with_statements_fix_safe_enabled(checker.settings) {
|
||||
Ok(Some(Fix::safe_edit(edit)))
|
||||
} else {
|
||||
Ok(Some(Fix::unsafe_edit(edit)))
|
||||
|
||||
@@ -7,13 +7,14 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::preview::is_simplify_ternary_to_binary_enabled;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Check for `if`-`else`-blocks that can be replaced with a ternary operator.
|
||||
/// Moreover, in [preview], check if these ternary expressions can be
|
||||
/// further simplified to binary expressions.
|
||||
/// Check for `if`-`else`-blocks that can be replaced with a ternary
|
||||
/// or binary operator.
|
||||
///
|
||||
/// The lint is suppressed if the suggested replacement would exceed
|
||||
/// the maximum line length configured in [pycodestyle.max-line-length].
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `if`-`else`-blocks that assign a value to a variable in both branches can
|
||||
@@ -33,7 +34,7 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
/// bar = x if foo else y
|
||||
/// ```
|
||||
///
|
||||
/// Or, in [preview]:
|
||||
/// Or:
|
||||
///
|
||||
/// ```python
|
||||
/// if cond:
|
||||
@@ -57,8 +58,8 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
/// ## References
|
||||
/// - [Python documentation: Conditional expressions](https://docs.python.org/3/reference/expressions.html#conditional-expressions)
|
||||
///
|
||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||
/// [code coverage]: https://github.com/nedbat/coveragepy/issues/509
|
||||
/// [pycodestyle.max-line-length]: https://docs.astral.sh/ruff/settings/#lint_pycodestyle_max-line-length
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct IfElseBlockInsteadOfIfExp {
|
||||
/// The ternary or binary expression to replace the `if`-`else`-block.
|
||||
@@ -183,16 +184,12 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast::
|
||||
//
|
||||
// The match statement below implements the following
|
||||
// logic:
|
||||
// - If `test == body_value` and preview enabled, replace with `target_var = test or else_value`
|
||||
// - If `test == not body_value` and preview enabled, replace with `target_var = body_value and else_value`
|
||||
// - If `not test == body_value` and preview enabled, replace with `target_var = body_value and else_value`
|
||||
// - If `test == body_value`, replace with `target_var = test or else_value`
|
||||
// - If `test == not body_value`, replace with `target_var = body_value and else_value`
|
||||
// - If `not test == body_value`, replace with `target_var = body_value and else_value`
|
||||
// - Otherwise, replace with `target_var = body_value if test else else_value`
|
||||
let (contents, assignment_kind) = match (
|
||||
is_simplify_ternary_to_binary_enabled(checker.settings),
|
||||
test,
|
||||
body_value,
|
||||
) {
|
||||
(true, test_node, body_node)
|
||||
let (contents, assignment_kind) = match (test, body_value) {
|
||||
(test_node, body_node)
|
||||
if ComparableExpr::from(test_node) == ComparableExpr::from(body_node)
|
||||
&& !contains_effect(test_node, |id| checker.semantic().has_builtin_binding(id)) =>
|
||||
{
|
||||
@@ -200,7 +197,7 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast::
|
||||
let binary = assignment_binary_or(target_var, body_value, else_value);
|
||||
(checker.generator().stmt(&binary), AssignmentKind::Binary)
|
||||
}
|
||||
(true, test_node, body_node)
|
||||
(test_node, body_node)
|
||||
if (test_node.as_unary_op_expr().is_some_and(|op_expr| {
|
||||
op_expr.op.is_not()
|
||||
&& ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(body_node)
|
||||
|
||||
@@ -118,7 +118,7 @@ SIM108.py:117:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `x = 3 if True else 5`
|
||||
|
||||
SIM108.py:141:1: SIM108 [*] Use ternary operator `z = cond if cond else other_cond` instead of `if`-`else`-block
|
||||
SIM108.py:141:1: SIM108 [*] Use binary operator `z = cond or other_cond` instead of `if`-`else`-block
|
||||
|
|
||||
139 | # SIM108 - should suggest
|
||||
140 | # z = cond or other_cond
|
||||
@@ -130,7 +130,7 @@ SIM108.py:141:1: SIM108 [*] Use ternary operator `z = cond if cond else other_co
|
||||
145 |
|
||||
146 | # SIM108 - should suggest
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = cond if cond else other_cond`
|
||||
= help: Replace `if`-`else`-block with `z = cond or other_cond`
|
||||
|
||||
ℹ Unsafe fix
|
||||
138 138 |
|
||||
@@ -140,12 +140,12 @@ SIM108.py:141:1: SIM108 [*] Use ternary operator `z = cond if cond else other_co
|
||||
142 |- z = cond
|
||||
143 |-else:
|
||||
144 |- z = other_cond
|
||||
141 |+z = cond if cond else other_cond
|
||||
141 |+z = cond or other_cond
|
||||
145 142 |
|
||||
146 143 | # SIM108 - should suggest
|
||||
147 144 | # z = cond and other_cond
|
||||
|
||||
SIM108.py:148:1: SIM108 [*] Use ternary operator `z = cond if not cond else other_cond` instead of `if`-`else`-block
|
||||
SIM108.py:148:1: SIM108 [*] Use binary operator `z = cond and other_cond` instead of `if`-`else`-block
|
||||
|
|
||||
146 | # SIM108 - should suggest
|
||||
147 | # z = cond and other_cond
|
||||
@@ -157,7 +157,7 @@ SIM108.py:148:1: SIM108 [*] Use ternary operator `z = cond if not cond else othe
|
||||
152 |
|
||||
153 | # SIM108 - should suggest
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = cond if not cond else other_cond`
|
||||
= help: Replace `if`-`else`-block with `z = cond and other_cond`
|
||||
|
||||
ℹ Unsafe fix
|
||||
145 145 |
|
||||
@@ -167,12 +167,12 @@ SIM108.py:148:1: SIM108 [*] Use ternary operator `z = cond if not cond else othe
|
||||
149 |- z = cond
|
||||
150 |-else:
|
||||
151 |- z = other_cond
|
||||
148 |+z = cond if not cond else other_cond
|
||||
148 |+z = cond and other_cond
|
||||
152 149 |
|
||||
153 150 | # SIM108 - should suggest
|
||||
154 151 | # z = not cond and other_cond
|
||||
|
||||
SIM108.py:155:1: SIM108 [*] Use ternary operator `z = not cond if cond else other_cond` instead of `if`-`else`-block
|
||||
SIM108.py:155:1: SIM108 [*] Use binary operator `z = not cond and other_cond` instead of `if`-`else`-block
|
||||
|
|
||||
153 | # SIM108 - should suggest
|
||||
154 | # z = not cond and other_cond
|
||||
@@ -184,7 +184,7 @@ SIM108.py:155:1: SIM108 [*] Use ternary operator `z = not cond if cond else othe
|
||||
159 |
|
||||
160 | # SIM108 does not suggest
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = not cond if cond else other_cond`
|
||||
= help: Replace `if`-`else`-block with `z = not cond and other_cond`
|
||||
|
||||
ℹ Unsafe fix
|
||||
152 152 |
|
||||
@@ -194,7 +194,7 @@ SIM108.py:155:1: SIM108 [*] Use ternary operator `z = not cond if cond else othe
|
||||
156 |- z = not cond
|
||||
157 |-else:
|
||||
158 |- z = other_cond
|
||||
155 |+z = not cond if cond else other_cond
|
||||
155 |+z = not cond and other_cond
|
||||
159 156 |
|
||||
160 157 | # SIM108 does not suggest
|
||||
161 158 | # a binary option in these cases,
|
||||
|
||||
@@ -1,382 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM108.py:2:1: SIM108 [*] Use ternary operator `b = c if a else d` instead of `if`-`else`-block
|
||||
|
|
||||
1 | # SIM108
|
||||
2 | / if a:
|
||||
3 | | b = c
|
||||
4 | | else:
|
||||
5 | | b = d
|
||||
| |_________^ SIM108
|
||||
6 |
|
||||
7 | # OK
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `b = c if a else d`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 1 | # SIM108
|
||||
2 |-if a:
|
||||
3 |- b = c
|
||||
4 |-else:
|
||||
5 |- b = d
|
||||
2 |+b = c if a else d
|
||||
6 3 |
|
||||
7 4 | # OK
|
||||
8 5 | b = c if a else d
|
||||
|
||||
SIM108.py:30:5: SIM108 [*] Use ternary operator `b = 1 if a else 2` instead of `if`-`else`-block
|
||||
|
|
||||
28 | pass
|
||||
29 | else:
|
||||
30 | / if a:
|
||||
31 | | b = 1
|
||||
32 | | else:
|
||||
33 | | b = 2
|
||||
| |_____________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `b = 1 if a else 2`
|
||||
|
||||
ℹ Unsafe fix
|
||||
27 27 | if True:
|
||||
28 28 | pass
|
||||
29 29 | else:
|
||||
30 |- if a:
|
||||
31 |- b = 1
|
||||
32 |- else:
|
||||
33 |- b = 2
|
||||
30 |+ b = 1 if a else 2
|
||||
34 31 |
|
||||
35 32 |
|
||||
36 33 | import sys
|
||||
|
||||
SIM108.py:58:1: SIM108 Use ternary operator `abc = x if x > 0 else -x` instead of `if`-`else`-block
|
||||
|
|
||||
57 | # SIM108 (without fix due to comments)
|
||||
58 | / if x > 0:
|
||||
59 | | # test test
|
||||
60 | | abc = x
|
||||
61 | | else:
|
||||
62 | | # test test test
|
||||
63 | | abc = -x
|
||||
| |____________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `abc = x if x > 0 else -x`
|
||||
|
||||
SIM108.py:82:1: SIM108 [*] Use ternary operator `b = "cccccccccccccccccccccccccccccccccß" if a else "ddddddddddddddddddddddddddddddddd💣"` instead of `if`-`else`-block
|
||||
|
|
||||
81 | # SIM108
|
||||
82 | / if a:
|
||||
83 | | b = "cccccccccccccccccccccccccccccccccß"
|
||||
84 | | else:
|
||||
85 | | b = "ddddddddddddddddddddddddddddddddd💣"
|
||||
| |_____________________________________________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `b = "cccccccccccccccccccccccccccccccccß" if a else "ddddddddddddddddddddddddddddddddd💣"`
|
||||
|
||||
ℹ Unsafe fix
|
||||
79 79 |
|
||||
80 80 |
|
||||
81 81 | # SIM108
|
||||
82 |-if a:
|
||||
83 |- b = "cccccccccccccccccccccccccccccccccß"
|
||||
84 |-else:
|
||||
85 |- b = "ddddddddddddddddddddddddddddddddd💣"
|
||||
82 |+b = "cccccccccccccccccccccccccccccccccß" if a else "ddddddddddddddddddddddddddddddddd💣"
|
||||
86 83 |
|
||||
87 84 |
|
||||
88 85 | # OK (too long)
|
||||
|
||||
SIM108.py:105:1: SIM108 Use ternary operator `exitcode = 0 if True else 1` instead of `if`-`else`-block
|
||||
|
|
||||
104 | # SIM108 (without fix due to trailing comment)
|
||||
105 | / if True:
|
||||
106 | | exitcode = 0
|
||||
107 | | else:
|
||||
108 | | exitcode = 1 # Trailing comment
|
||||
| |________________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `exitcode = 0 if True else 1`
|
||||
|
||||
SIM108.py:112:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block
|
||||
|
|
||||
111 | # SIM108
|
||||
112 | / if True: x = 3 # Foo
|
||||
113 | | else: x = 5
|
||||
| |___________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `x = 3 if True else 5`
|
||||
|
||||
SIM108.py:117:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block
|
||||
|
|
||||
116 | # SIM108
|
||||
117 | / if True: # Foo
|
||||
118 | | x = 3
|
||||
119 | | else:
|
||||
120 | | x = 5
|
||||
| |_________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `x = 3 if True else 5`
|
||||
|
||||
SIM108.py:141:1: SIM108 [*] Use binary operator `z = cond or other_cond` instead of `if`-`else`-block
|
||||
|
|
||||
139 | # SIM108 - should suggest
|
||||
140 | # z = cond or other_cond
|
||||
141 | / if cond:
|
||||
142 | | z = cond
|
||||
143 | | else:
|
||||
144 | | z = other_cond
|
||||
| |__________________^ SIM108
|
||||
145 |
|
||||
146 | # SIM108 - should suggest
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = cond or other_cond`
|
||||
|
||||
ℹ Unsafe fix
|
||||
138 138 |
|
||||
139 139 | # SIM108 - should suggest
|
||||
140 140 | # z = cond or other_cond
|
||||
141 |-if cond:
|
||||
142 |- z = cond
|
||||
143 |-else:
|
||||
144 |- z = other_cond
|
||||
141 |+z = cond or other_cond
|
||||
145 142 |
|
||||
146 143 | # SIM108 - should suggest
|
||||
147 144 | # z = cond and other_cond
|
||||
|
||||
SIM108.py:148:1: SIM108 [*] Use binary operator `z = cond and other_cond` instead of `if`-`else`-block
|
||||
|
|
||||
146 | # SIM108 - should suggest
|
||||
147 | # z = cond and other_cond
|
||||
148 | / if not cond:
|
||||
149 | | z = cond
|
||||
150 | | else:
|
||||
151 | | z = other_cond
|
||||
| |__________________^ SIM108
|
||||
152 |
|
||||
153 | # SIM108 - should suggest
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = cond and other_cond`
|
||||
|
||||
ℹ Unsafe fix
|
||||
145 145 |
|
||||
146 146 | # SIM108 - should suggest
|
||||
147 147 | # z = cond and other_cond
|
||||
148 |-if not cond:
|
||||
149 |- z = cond
|
||||
150 |-else:
|
||||
151 |- z = other_cond
|
||||
148 |+z = cond and other_cond
|
||||
152 149 |
|
||||
153 150 | # SIM108 - should suggest
|
||||
154 151 | # z = not cond and other_cond
|
||||
|
||||
SIM108.py:155:1: SIM108 [*] Use binary operator `z = not cond and other_cond` instead of `if`-`else`-block
|
||||
|
|
||||
153 | # SIM108 - should suggest
|
||||
154 | # z = not cond and other_cond
|
||||
155 | / if cond:
|
||||
156 | | z = not cond
|
||||
157 | | else:
|
||||
158 | | z = other_cond
|
||||
| |__________________^ SIM108
|
||||
159 |
|
||||
160 | # SIM108 does not suggest
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = not cond and other_cond`
|
||||
|
||||
ℹ Unsafe fix
|
||||
152 152 |
|
||||
153 153 | # SIM108 - should suggest
|
||||
154 154 | # z = not cond and other_cond
|
||||
155 |-if cond:
|
||||
156 |- z = not cond
|
||||
157 |-else:
|
||||
158 |- z = other_cond
|
||||
155 |+z = not cond and other_cond
|
||||
159 156 |
|
||||
160 157 | # SIM108 does not suggest
|
||||
161 158 | # a binary option in these cases,
|
||||
|
||||
SIM108.py:167:1: SIM108 [*] Use ternary operator `z = 1 if True else other` instead of `if`-`else`-block
|
||||
|
|
||||
165 | # (Of course, these specific expressions
|
||||
166 | # should be simplified for other reasons...)
|
||||
167 | / if True:
|
||||
168 | | z = 1
|
||||
169 | | else:
|
||||
170 | | z = other
|
||||
| |_____________^ SIM108
|
||||
171 |
|
||||
172 | if False:
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = 1 if True else other`
|
||||
|
||||
ℹ Unsafe fix
|
||||
164 164 | # so, e.g. `True == 1`.
|
||||
165 165 | # (Of course, these specific expressions
|
||||
166 166 | # should be simplified for other reasons...)
|
||||
167 |-if True:
|
||||
168 |- z = 1
|
||||
169 |-else:
|
||||
170 |- z = other
|
||||
167 |+z = 1 if True else other
|
||||
171 168 |
|
||||
172 169 | if False:
|
||||
173 170 | z = 1
|
||||
|
||||
SIM108.py:172:1: SIM108 [*] Use ternary operator `z = 1 if False else other` instead of `if`-`else`-block
|
||||
|
|
||||
170 | z = other
|
||||
171 |
|
||||
172 | / if False:
|
||||
173 | | z = 1
|
||||
174 | | else:
|
||||
175 | | z = other
|
||||
| |_____________^ SIM108
|
||||
176 |
|
||||
177 | if 1:
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = 1 if False else other`
|
||||
|
||||
ℹ Unsafe fix
|
||||
169 169 | else:
|
||||
170 170 | z = other
|
||||
171 171 |
|
||||
172 |-if False:
|
||||
173 |- z = 1
|
||||
174 |-else:
|
||||
175 |- z = other
|
||||
172 |+z = 1 if False else other
|
||||
176 173 |
|
||||
177 174 | if 1:
|
||||
178 175 | z = True
|
||||
|
||||
SIM108.py:177:1: SIM108 [*] Use ternary operator `z = True if 1 else other` instead of `if`-`else`-block
|
||||
|
|
||||
175 | z = other
|
||||
176 |
|
||||
177 | / if 1:
|
||||
178 | | z = True
|
||||
179 | | else:
|
||||
180 | | z = other
|
||||
| |_____________^ SIM108
|
||||
181 |
|
||||
182 | # SIM108 does not suggest a binary option in this
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = True if 1 else other`
|
||||
|
||||
ℹ Unsafe fix
|
||||
174 174 | else:
|
||||
175 175 | z = other
|
||||
176 176 |
|
||||
177 |-if 1:
|
||||
178 |- z = True
|
||||
179 |-else:
|
||||
180 |- z = other
|
||||
177 |+z = True if 1 else other
|
||||
181 178 |
|
||||
182 179 | # SIM108 does not suggest a binary option in this
|
||||
183 180 | # case, since we'd be reducing the number of calls
|
||||
|
||||
SIM108.py:185:1: SIM108 [*] Use ternary operator `z = foo() if foo() else other` instead of `if`-`else`-block
|
||||
|
|
||||
183 | # case, since we'd be reducing the number of calls
|
||||
184 | # from Two to one.
|
||||
185 | / if foo():
|
||||
186 | | z = foo()
|
||||
187 | | else:
|
||||
188 | | z = other
|
||||
| |_____________^ SIM108
|
||||
189 |
|
||||
190 | # SIM108 does not suggest a binary option in this
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = foo() if foo() else other`
|
||||
|
||||
ℹ Unsafe fix
|
||||
182 182 | # SIM108 does not suggest a binary option in this
|
||||
183 183 | # case, since we'd be reducing the number of calls
|
||||
184 184 | # from Two to one.
|
||||
185 |-if foo():
|
||||
186 |- z = foo()
|
||||
187 |-else:
|
||||
188 |- z = other
|
||||
185 |+z = foo() if foo() else other
|
||||
189 186 |
|
||||
190 187 | # SIM108 does not suggest a binary option in this
|
||||
191 188 | # case, since we'd be reducing the number of calls
|
||||
|
||||
SIM108.py:193:1: SIM108 [*] Use ternary operator `z = not foo() if foo() else other` instead of `if`-`else`-block
|
||||
|
|
||||
191 | # case, since we'd be reducing the number of calls
|
||||
192 | # from Two to one.
|
||||
193 | / if foo():
|
||||
194 | | z = not foo()
|
||||
195 | | else:
|
||||
196 | | z = other
|
||||
| |_____________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `z = not foo() if foo() else other`
|
||||
|
||||
ℹ Unsafe fix
|
||||
190 190 | # SIM108 does not suggest a binary option in this
|
||||
191 191 | # case, since we'd be reducing the number of calls
|
||||
192 192 | # from Two to one.
|
||||
193 |-if foo():
|
||||
194 |- z = not foo()
|
||||
195 |-else:
|
||||
196 |- z = other
|
||||
193 |+z = not foo() if foo() else other
|
||||
197 194 |
|
||||
198 195 |
|
||||
199 196 | # These two cases double as tests for f-string quote preservation. The first
|
||||
|
||||
SIM108.py:202:1: SIM108 [*] Use ternary operator `var = "str" if cond else f"{first}-{second}"` instead of `if`-`else`-block
|
||||
|
|
||||
200 | # f-string should preserve its double quotes, and the second should preserve
|
||||
201 | # single quotes
|
||||
202 | / if cond:
|
||||
203 | | var = "str"
|
||||
204 | | else:
|
||||
205 | | var = f"{first}-{second}"
|
||||
| |_____________________________^ SIM108
|
||||
206 |
|
||||
207 | if cond:
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `var = "str" if cond else f"{first}-{second}"`
|
||||
|
||||
ℹ Unsafe fix
|
||||
199 199 | # These two cases double as tests for f-string quote preservation. The first
|
||||
200 200 | # f-string should preserve its double quotes, and the second should preserve
|
||||
201 201 | # single quotes
|
||||
202 |-if cond:
|
||||
203 |- var = "str"
|
||||
204 |-else:
|
||||
205 |- var = f"{first}-{second}"
|
||||
202 |+var = "str" if cond else f"{first}-{second}"
|
||||
206 203 |
|
||||
207 204 | if cond:
|
||||
208 205 | var = "str"
|
||||
|
||||
SIM108.py:207:1: SIM108 [*] Use ternary operator `var = "str" if cond else f'{first}-{second}'` instead of `if`-`else`-block
|
||||
|
|
||||
205 | var = f"{first}-{second}"
|
||||
206 |
|
||||
207 | / if cond:
|
||||
208 | | var = "str"
|
||||
209 | | else:
|
||||
210 | | var = f'{first}-{second}'
|
||||
| |_____________________________^ SIM108
|
||||
|
|
||||
= help: Replace `if`-`else`-block with `var = "str" if cond else f'{first}-{second}'`
|
||||
|
||||
ℹ Unsafe fix
|
||||
204 204 | else:
|
||||
205 205 | var = f"{first}-{second}"
|
||||
206 206 |
|
||||
207 |-if cond:
|
||||
208 |- var = "str"
|
||||
209 |-else:
|
||||
210 |- var = f'{first}-{second}'
|
||||
207 |+var = "str" if cond else f'{first}-{second}'
|
||||
@@ -62,24 +62,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::ReadlinesInFor, Path::new("FURB129.py"))]
|
||||
fn preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("refurb").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: settings::types::PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_whole_file_python_39() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -7,7 +7,6 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::pad_end;
|
||||
use crate::preview::is_readlines_in_for_fix_safe_enabled;
|
||||
use crate::{AlwaysFixableViolation, Edit, Fix};
|
||||
|
||||
/// ## What it does
|
||||
@@ -106,9 +105,5 @@ fn readlines_in_iter(checker: &Checker, iter_expr: &Expr) {
|
||||
};
|
||||
|
||||
let mut diagnostic = checker.report_diagnostic(ReadlinesInFor, expr_call.range());
|
||||
diagnostic.set_fix(if is_readlines_in_for_fix_safe_enabled(checker.settings) {
|
||||
Fix::safe_edit(edit)
|
||||
} else {
|
||||
Fix::unsafe_edit(edit)
|
||||
});
|
||||
diagnostic.set_fix(Fix::safe_edit(edit));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ FURB129.py:7:18: FURB129 [*] Instead of calling `readlines()`, iterate over file
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
4 4 |
|
||||
5 5 | # Errors
|
||||
6 6 | with open("FURB129.py") as f:
|
||||
@@ -33,7 +33,7 @@ FURB129.py:9:35: FURB129 [*] Instead of calling `readlines()`, iterate over file
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
6 6 | with open("FURB129.py") as f:
|
||||
7 7 | for _line in f.readlines():
|
||||
8 8 | pass
|
||||
@@ -53,7 +53,7 @@ FURB129.py:10:35: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
7 7 | for _line in f.readlines():
|
||||
8 8 | pass
|
||||
9 9 | a = [line.lower() for line in f.readlines()]
|
||||
@@ -74,7 +74,7 @@ FURB129.py:11:49: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
8 8 | pass
|
||||
9 9 | a = [line.lower() for line in f.readlines()]
|
||||
10 10 | b = {line.upper() for line in f.readlines()}
|
||||
@@ -93,7 +93,7 @@ FURB129.py:14:18: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
11 11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
12 12 |
|
||||
13 13 | with Path("FURB129.py").open() as f:
|
||||
@@ -113,7 +113,7 @@ FURB129.py:17:14: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
14 14 | for _line in f.readlines():
|
||||
15 15 | pass
|
||||
16 16 |
|
||||
@@ -133,7 +133,7 @@ FURB129.py:20:14: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
17 17 | for _line in open("FURB129.py").readlines():
|
||||
18 18 | pass
|
||||
19 19 |
|
||||
@@ -154,7 +154,7 @@ FURB129.py:26:18: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
23 23 |
|
||||
24 24 | def func():
|
||||
25 25 | f = Path("FURB129.py").open()
|
||||
@@ -173,7 +173,7 @@ FURB129.py:32:18: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
29 29 |
|
||||
30 30 |
|
||||
31 31 | def func(f: io.BytesIO):
|
||||
@@ -194,7 +194,7 @@ FURB129.py:38:22: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
35 35 |
|
||||
36 36 | def func():
|
||||
37 37 | with (open("FURB129.py") as f, foo as bar):
|
||||
@@ -213,7 +213,7 @@ FURB129.py:47:17: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
44 44 | import builtins
|
||||
45 45 |
|
||||
46 46 | with builtins.open("FURB129.py") as f:
|
||||
@@ -232,7 +232,7 @@ FURB129.py:54:17: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
51 51 | from builtins import open as o
|
||||
52 52 |
|
||||
53 53 | with o("FURB129.py") as f:
|
||||
@@ -252,7 +252,7 @@ FURB129.py:93:17: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
90 90 |
|
||||
91 91 | # https://github.com/astral-sh/ruff/issues/18231
|
||||
92 92 | with open("furb129.py") as f:
|
||||
@@ -270,7 +270,7 @@ FURB129.py:97:23: FURB129 [*] Instead of calling `readlines()`, iterate over fil
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
94 94 | pass
|
||||
95 95 |
|
||||
96 96 | with open("furb129.py") as f:
|
||||
@@ -290,7 +290,7 @@ FURB129.py:101:17: FURB129 [*] Instead of calling `readlines()`, iterate over fi
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
98 98 |
|
||||
99 99 |
|
||||
100 100 | with open("furb129.py") as f:
|
||||
@@ -310,7 +310,7 @@ FURB129.py:103:16: FURB129 [*] Instead of calling `readlines()`, iterate over fi
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
100 100 | with open("furb129.py") as f:
|
||||
101 101 | for line in (((f))).readlines():
|
||||
102 102 | pass
|
||||
@@ -328,7 +328,7 @@ FURB129.py:107:29: FURB129 [*] Instead of calling `readlines()`, iterate over fi
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Unsafe fix
|
||||
ℹ Safe fix
|
||||
104 104 | pass
|
||||
105 105 |
|
||||
106 106 | # Test case for issue #17683 (missing space before keyword)
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/refurb/mod.rs
|
||||
---
|
||||
FURB129.py:7:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
5 | # Errors
|
||||
6 | with open("FURB129.py") as f:
|
||||
7 | for _line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
8 | pass
|
||||
9 | a = [line.lower() for line in f.readlines()]
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 |
|
||||
5 5 | # Errors
|
||||
6 6 | with open("FURB129.py") as f:
|
||||
7 |- for _line in f.readlines():
|
||||
7 |+ for _line in f:
|
||||
8 8 | pass
|
||||
9 9 | a = [line.lower() for line in f.readlines()]
|
||||
10 10 | b = {line.upper() for line in f.readlines()}
|
||||
|
||||
FURB129.py:9:35: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
7 | for _line in f.readlines():
|
||||
8 | pass
|
||||
9 | a = [line.lower() for line in f.readlines()]
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
10 | b = {line.upper() for line in f.readlines()}
|
||||
11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
6 6 | with open("FURB129.py") as f:
|
||||
7 7 | for _line in f.readlines():
|
||||
8 8 | pass
|
||||
9 |- a = [line.lower() for line in f.readlines()]
|
||||
9 |+ a = [line.lower() for line in f]
|
||||
10 10 | b = {line.upper() for line in f.readlines()}
|
||||
11 11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
12 12 |
|
||||
|
||||
FURB129.py:10:35: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
8 | pass
|
||||
9 | a = [line.lower() for line in f.readlines()]
|
||||
10 | b = {line.upper() for line in f.readlines()}
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 | for _line in f.readlines():
|
||||
8 8 | pass
|
||||
9 9 | a = [line.lower() for line in f.readlines()]
|
||||
10 |- b = {line.upper() for line in f.readlines()}
|
||||
10 |+ b = {line.upper() for line in f}
|
||||
11 11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
12 12 |
|
||||
13 13 | with Path("FURB129.py").open() as f:
|
||||
|
||||
FURB129.py:11:49: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
9 | a = [line.lower() for line in f.readlines()]
|
||||
10 | b = {line.upper() for line in f.readlines()}
|
||||
11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
12 |
|
||||
13 | with Path("FURB129.py").open() as f:
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 | pass
|
||||
9 9 | a = [line.lower() for line in f.readlines()]
|
||||
10 10 | b = {line.upper() for line in f.readlines()}
|
||||
11 |- c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
11 |+ c = {line.lower(): line.upper() for line in f}
|
||||
12 12 |
|
||||
13 13 | with Path("FURB129.py").open() as f:
|
||||
14 14 | for _line in f.readlines():
|
||||
|
||||
FURB129.py:14:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
13 | with Path("FURB129.py").open() as f:
|
||||
14 | for _line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
15 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 | c = {line.lower(): line.upper() for line in f.readlines()}
|
||||
12 12 |
|
||||
13 13 | with Path("FURB129.py").open() as f:
|
||||
14 |- for _line in f.readlines():
|
||||
14 |+ for _line in f:
|
||||
15 15 | pass
|
||||
16 16 |
|
||||
17 17 | for _line in open("FURB129.py").readlines():
|
||||
|
||||
FURB129.py:17:14: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
15 | pass
|
||||
16 |
|
||||
17 | for _line in open("FURB129.py").readlines():
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB129
|
||||
18 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
14 14 | for _line in f.readlines():
|
||||
15 15 | pass
|
||||
16 16 |
|
||||
17 |-for _line in open("FURB129.py").readlines():
|
||||
17 |+for _line in open("FURB129.py"):
|
||||
18 18 | pass
|
||||
19 19 |
|
||||
20 20 | for _line in Path("FURB129.py").open().readlines():
|
||||
|
||||
FURB129.py:20:14: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
18 | pass
|
||||
19 |
|
||||
20 | for _line in Path("FURB129.py").open().readlines():
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB129
|
||||
21 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
17 17 | for _line in open("FURB129.py").readlines():
|
||||
18 18 | pass
|
||||
19 19 |
|
||||
20 |-for _line in Path("FURB129.py").open().readlines():
|
||||
20 |+for _line in Path("FURB129.py").open():
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
|
||||
FURB129.py:26:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
24 | def func():
|
||||
25 | f = Path("FURB129.py").open()
|
||||
26 | for _line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
27 | pass
|
||||
28 | f.close()
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
23 23 |
|
||||
24 24 | def func():
|
||||
25 25 | f = Path("FURB129.py").open()
|
||||
26 |- for _line in f.readlines():
|
||||
26 |+ for _line in f:
|
||||
27 27 | pass
|
||||
28 28 | f.close()
|
||||
29 29 |
|
||||
|
||||
FURB129.py:32:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
31 | def func(f: io.BytesIO):
|
||||
32 | for _line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
33 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
29 29 |
|
||||
30 30 |
|
||||
31 31 | def func(f: io.BytesIO):
|
||||
32 |- for _line in f.readlines():
|
||||
32 |+ for _line in f:
|
||||
33 33 | pass
|
||||
34 34 |
|
||||
35 35 |
|
||||
|
||||
FURB129.py:38:22: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
36 | def func():
|
||||
37 | with (open("FURB129.py") as f, foo as bar):
|
||||
38 | for _line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
39 | pass
|
||||
40 | for _line in bar.readlines():
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
35 35 |
|
||||
36 36 | def func():
|
||||
37 37 | with (open("FURB129.py") as f, foo as bar):
|
||||
38 |- for _line in f.readlines():
|
||||
38 |+ for _line in f:
|
||||
39 39 | pass
|
||||
40 40 | for _line in bar.readlines():
|
||||
41 41 | pass
|
||||
|
||||
FURB129.py:47:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
46 | with builtins.open("FURB129.py") as f:
|
||||
47 | for line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
48 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
44 44 | import builtins
|
||||
45 45 |
|
||||
46 46 | with builtins.open("FURB129.py") as f:
|
||||
47 |- for line in f.readlines():
|
||||
47 |+ for line in f:
|
||||
48 48 | pass
|
||||
49 49 |
|
||||
50 50 |
|
||||
|
||||
FURB129.py:54:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
53 | with o("FURB129.py") as f:
|
||||
54 | for line in f.readlines():
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
55 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
51 51 | from builtins import open as o
|
||||
52 52 |
|
||||
53 53 | with o("FURB129.py") as f:
|
||||
54 |- for line in f.readlines():
|
||||
54 |+ for line in f:
|
||||
55 55 | pass
|
||||
56 56 |
|
||||
57 57 |
|
||||
|
||||
FURB129.py:93:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
91 | # https://github.com/astral-sh/ruff/issues/18231
|
||||
92 | with open("furb129.py") as f:
|
||||
93 | for line in (f).readlines():
|
||||
| ^^^^^^^^^^^^^^^ FURB129
|
||||
94 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
90 90 |
|
||||
91 91 | # https://github.com/astral-sh/ruff/issues/18231
|
||||
92 92 | with open("furb129.py") as f:
|
||||
93 |- for line in (f).readlines():
|
||||
93 |+ for line in (f):
|
||||
94 94 | pass
|
||||
95 95 |
|
||||
96 96 | with open("furb129.py") as f:
|
||||
|
||||
FURB129.py:97:23: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
96 | with open("furb129.py") as f:
|
||||
97 | [line for line in (f).readlines()]
|
||||
| ^^^^^^^^^^^^^^^ FURB129
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
94 94 | pass
|
||||
95 95 |
|
||||
96 96 | with open("furb129.py") as f:
|
||||
97 |- [line for line in (f).readlines()]
|
||||
97 |+ [line for line in (f)]
|
||||
98 98 |
|
||||
99 99 |
|
||||
100 100 | with open("furb129.py") as f:
|
||||
|
||||
FURB129.py:101:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
100 | with open("furb129.py") as f:
|
||||
101 | for line in (((f))).readlines():
|
||||
| ^^^^^^^^^^^^^^^^^^^ FURB129
|
||||
102 | pass
|
||||
103 | for line in(f).readlines():
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
98 98 |
|
||||
99 99 |
|
||||
100 100 | with open("furb129.py") as f:
|
||||
101 |- for line in (((f))).readlines():
|
||||
101 |+ for line in (((f))):
|
||||
102 102 | pass
|
||||
103 103 | for line in(f).readlines():
|
||||
104 104 | pass
|
||||
|
||||
FURB129.py:103:16: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
101 | for line in (((f))).readlines():
|
||||
102 | pass
|
||||
103 | for line in(f).readlines():
|
||||
| ^^^^^^^^^^^^^^^ FURB129
|
||||
104 | pass
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
100 100 | with open("furb129.py") as f:
|
||||
101 101 | for line in (((f))).readlines():
|
||||
102 102 | pass
|
||||
103 |- for line in(f).readlines():
|
||||
103 |+ for line in(f):
|
||||
104 104 | pass
|
||||
105 105 |
|
||||
106 106 | # Test case for issue #17683 (missing space before keyword)
|
||||
|
||||
FURB129.py:107:29: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly
|
||||
|
|
||||
106 | # Test case for issue #17683 (missing space before keyword)
|
||||
107 | print([line for line in f.readlines()if True])
|
||||
| ^^^^^^^^^^^^^ FURB129
|
||||
|
|
||||
= help: Remove `readlines()`
|
||||
|
||||
ℹ Safe fix
|
||||
104 104 | pass
|
||||
105 105 |
|
||||
106 106 | # Test case for issue #17683 (missing space before keyword)
|
||||
107 |- print([line for line in f.readlines()if True])
|
||||
107 |+ print([line for line in f if True])
|
||||
@@ -24,6 +24,7 @@ mod tests {
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
#[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005.py"))]
|
||||
#[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005_slices.py"))]
|
||||
#[test_case(Rule::AsyncioDanglingTask, Path::new("RUF006.py"))]
|
||||
#[test_case(Rule::ZipInsteadOfPairwise, Path::new("RUF007.py"))]
|
||||
#[test_case(Rule::MutableDataclassDefault, Path::new("RUF008.py"))]
|
||||
@@ -94,6 +95,7 @@ mod tests {
|
||||
#[test_case(Rule::MapIntVersionParsing, Path::new("RUF048_1.py"))]
|
||||
#[test_case(Rule::IfKeyInDictDel, Path::new("RUF051.py"))]
|
||||
#[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"))]
|
||||
#[test_case(Rule::ClassWithMixedTypeVars, Path::new("RUF053.py"))]
|
||||
#[test_case(Rule::FalsyDictGetFallback, Path::new("RUF056.py"))]
|
||||
#[test_case(Rule::UnusedUnpackedVariable, Path::new("RUF059_0.py"))]
|
||||
#[test_case(Rule::UnusedUnpackedVariable, Path::new("RUF059_1.py"))]
|
||||
@@ -322,10 +324,7 @@ mod tests {
|
||||
fn ruff_noqa_filedirective_unused() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("ruff/RUF100_6.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rules(vec![Rule::UnusedNOQA])
|
||||
},
|
||||
&settings::LinterSettings::for_rules(vec![Rule::UnusedNOQA]),
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
@@ -335,15 +334,12 @@ mod tests {
|
||||
fn ruff_noqa_filedirective_unused_last_of_many() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("ruff/RUF100_7.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rules(vec![
|
||||
Rule::UnusedNOQA,
|
||||
Rule::FStringMissingPlaceholders,
|
||||
Rule::LineTooLong,
|
||||
Rule::UnusedVariable,
|
||||
])
|
||||
},
|
||||
&settings::LinterSettings::for_rules(vec![
|
||||
Rule::UnusedNOQA,
|
||||
Rule::FStringMissingPlaceholders,
|
||||
Rule::LineTooLong,
|
||||
Rule::UnusedVariable,
|
||||
]),
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
@@ -480,10 +476,8 @@ mod tests {
|
||||
#[test_case(Rule::DataclassEnum, Path::new("RUF049.py"))]
|
||||
#[test_case(Rule::StarmapZip, Path::new("RUF058_0.py"))]
|
||||
#[test_case(Rule::StarmapZip, Path::new("RUF058_1.py"))]
|
||||
#[test_case(Rule::ClassWithMixedTypeVars, Path::new("RUF053.py"))]
|
||||
#[test_case(Rule::IndentedFormFeed, Path::new("RUF054.py"))]
|
||||
#[test_case(Rule::ImplicitClassVarInDataclass, Path::new("RUF045.py"))]
|
||||
#[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005_slices.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
||||
@@ -4,7 +4,6 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
use crate::preview::is_support_slices_in_literal_concatenation_enabled;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
@@ -96,7 +95,7 @@ enum Type {
|
||||
}
|
||||
|
||||
/// Recursively merge all the tuples and lists in the expression.
|
||||
fn concatenate_expressions(expr: &Expr, should_support_slices: bool) -> Option<(Expr, Type)> {
|
||||
fn concatenate_expressions(expr: &Expr) -> Option<(Expr, Type)> {
|
||||
let Expr::BinOp(ast::ExprBinOp {
|
||||
left,
|
||||
op: Operator::Add,
|
||||
@@ -108,22 +107,18 @@ fn concatenate_expressions(expr: &Expr, should_support_slices: bool) -> Option<(
|
||||
};
|
||||
|
||||
let new_left = match left.as_ref() {
|
||||
Expr::BinOp(ast::ExprBinOp { .. }) => {
|
||||
match concatenate_expressions(left, should_support_slices) {
|
||||
Some((new_left, _)) => new_left,
|
||||
None => *left.clone(),
|
||||
}
|
||||
}
|
||||
Expr::BinOp(ast::ExprBinOp { .. }) => match concatenate_expressions(left) {
|
||||
Some((new_left, _)) => new_left,
|
||||
None => *left.clone(),
|
||||
},
|
||||
_ => *left.clone(),
|
||||
};
|
||||
|
||||
let new_right = match right.as_ref() {
|
||||
Expr::BinOp(ast::ExprBinOp { .. }) => {
|
||||
match concatenate_expressions(right, should_support_slices) {
|
||||
Some((new_right, _)) => new_right,
|
||||
None => *right.clone(),
|
||||
}
|
||||
}
|
||||
Expr::BinOp(ast::ExprBinOp { .. }) => match concatenate_expressions(right) {
|
||||
Some((new_right, _)) => new_right,
|
||||
None => *right.clone(),
|
||||
},
|
||||
_ => *right.clone(),
|
||||
};
|
||||
|
||||
@@ -151,9 +146,7 @@ fn concatenate_expressions(expr: &Expr, should_support_slices: bool) -> Option<(
|
||||
make_splat_elts(splat_element, other_elements, splat_at_left)
|
||||
}
|
||||
// Subscripts are also considered safe-ish to splat if the indexer is a slice.
|
||||
Expr::Subscript(ast::ExprSubscript { slice, .. })
|
||||
if should_support_slices && matches!(&**slice, Expr::Slice(_)) =>
|
||||
{
|
||||
Expr::Subscript(ast::ExprSubscript { slice, .. }) if matches!(&**slice, Expr::Slice(_)) => {
|
||||
make_splat_elts(splat_element, other_elements, splat_at_left)
|
||||
}
|
||||
// If the splat element is itself a list/tuple, insert them in the other list/tuple.
|
||||
@@ -198,10 +191,7 @@ pub(crate) fn collection_literal_concatenation(checker: &Checker, expr: &Expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
let should_support_slices =
|
||||
is_support_slices_in_literal_concatenation_enabled(checker.settings);
|
||||
|
||||
let Some((new_expr, type_)) = concatenate_expressions(expr, should_support_slices) else {
|
||||
let Some((new_expr, type_)) = concatenate_expressions(expr) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use ruff_python_semantic::analyze::function_type::is_stub;
|
||||
|
||||
use crate::Violation;
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
use crate::rules::fastapi::rules::is_fastapi_route;
|
||||
|
||||
/// ## What it does
|
||||
|
||||
@@ -65,7 +65,7 @@ fn syntax_error() {
|
||||
fn unsupported_syntax_error() {
|
||||
check!(
|
||||
"match 2:\n case 1: ...",
|
||||
r#"{"preview": true, "target-version": "py39"}"#,
|
||||
r#"{"target-version": "py39"}"#,
|
||||
[ExpandedMessage {
|
||||
code: None,
|
||||
message: "SyntaxError: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user