mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
Add rule removal infrastructure (#9691)
Similar to https://github.com/astral-sh/ruff/pull/9689 — retains removed rules for better error messages and documentation but removed rules _cannot_ be used in any context. Removes PLR1706 as a useful test case and something we want to accomplish in #9680 anyway. The rule was in preview so we do not need to deprecate it first. Closes https://github.com/astral-sh/ruff/issues/9007 ## Test plan <img width="1110" alt="Rules table" src="ac9fa682
-623c-44aa-8e51-d8ab0d308355"> <img width="1110" alt="Rule page" src="05850b2d
-7ca5-49bb-8df8-bb931bab25cd">
This commit is contained in:
parent
a0ef087e73
commit
e0bc08a758
13 changed files with 127 additions and 411 deletions
|
@ -1091,6 +1091,39 @@ fn preview_enabled_group_ignore() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn removed_direct() {
|
||||||
|
// Selection of a removed rule should fail
|
||||||
|
let mut cmd = RuffCheck::default().args(["--select", "PLR1706"]).build();
|
||||||
|
assert_cmd_snapshot!(cmd, @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
ruff failed
|
||||||
|
Cause: Rule `PLR1706` was removed and cannot be selected.
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn removed_indirect() {
|
||||||
|
// Selection _including_ a removed rule without matching should not fail
|
||||||
|
// nor should the rule be used
|
||||||
|
let mut cmd = RuffCheck::default().args(["--select", "PLR"]).build();
|
||||||
|
assert_cmd_snapshot!(cmd.pass_stdin(r###"
|
||||||
|
# This would have been a PLR1706 violation
|
||||||
|
x, y = 1, 2
|
||||||
|
maximum = x >= y and x or y
|
||||||
|
"""###), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deprecated_direct() {
|
fn deprecated_direct() {
|
||||||
// Selection of a deprecated rule without preview enabled should still work
|
// Selection of a deprecated rule without preview enabled should still work
|
||||||
|
|
|
@ -45,6 +45,14 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rule.is_removed() {
|
||||||
|
output.push_str(
|
||||||
|
r"**Warning: This rule has been removed and its documentation is only available for historical reasons.**",
|
||||||
|
);
|
||||||
|
output.push('\n');
|
||||||
|
output.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
let fix_availability = rule.fixable();
|
let fix_availability = rule.fixable();
|
||||||
if matches!(
|
if matches!(
|
||||||
fix_availability,
|
fix_availability,
|
||||||
|
|
|
@ -15,6 +15,7 @@ use ruff_workspace::options_base::OptionsMetadata;
|
||||||
|
|
||||||
const FIX_SYMBOL: &str = "🛠️";
|
const FIX_SYMBOL: &str = "🛠️";
|
||||||
const PREVIEW_SYMBOL: &str = "🧪";
|
const PREVIEW_SYMBOL: &str = "🧪";
|
||||||
|
const REMOVED_SYMBOL: &str = "❌";
|
||||||
const WARNING_SYMBOL: &str = "⚠️";
|
const WARNING_SYMBOL: &str = "⚠️";
|
||||||
const STABLE_SYMBOL: &str = "✔️";
|
const STABLE_SYMBOL: &str = "✔️";
|
||||||
const SPACER: &str = " ";
|
const SPACER: &str = " ";
|
||||||
|
@ -26,6 +27,9 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
|
||||||
table_out.push('\n');
|
table_out.push('\n');
|
||||||
for rule in rules {
|
for rule in rules {
|
||||||
let status_token = match rule.group() {
|
let status_token = match rule.group() {
|
||||||
|
RuleGroup::Removed => {
|
||||||
|
format!("<span title='Rule has been removed'>{REMOVED_SYMBOL}</span>")
|
||||||
|
}
|
||||||
RuleGroup::Deprecated => {
|
RuleGroup::Deprecated => {
|
||||||
format!("<span title='Rule has been deprecated'>{WARNING_SYMBOL}</span>")
|
format!("<span title='Rule has been deprecated'>{WARNING_SYMBOL}</span>")
|
||||||
}
|
}
|
||||||
|
@ -62,9 +66,20 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
|
||||||
Cow::Borrowed(message)
|
Cow::Borrowed(message)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Start and end of style spans
|
||||||
|
let mut ss = "";
|
||||||
|
let mut se = "";
|
||||||
|
if rule.is_removed() {
|
||||||
|
ss = "<span style='opacity: 0.5', title='This rule has been removed'>";
|
||||||
|
se = "</span>";
|
||||||
|
} else if rule.is_deprecated() {
|
||||||
|
ss = "<span style='opacity: 0.8', title='This rule has been deprecated'>";
|
||||||
|
se = "</span>";
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::or_fun_call)]
|
#[allow(clippy::or_fun_call)]
|
||||||
table_out.push_str(&format!(
|
table_out.push_str(&format!(
|
||||||
"| {0}{1} {{ #{0}{1} }} | {2} | {3} | {4} |",
|
"| {ss}{0}{1}{se} {{ #{0}{1} }} | {ss}{2}{se} | {ss}{3}{se} | {ss}{4}{se} |",
|
||||||
linter.common_prefix(),
|
linter.common_prefix(),
|
||||||
linter.code_for_rule(rule).unwrap(),
|
linter.code_for_rule(rule).unwrap(),
|
||||||
rule.explanation()
|
rule.explanation()
|
||||||
|
@ -101,6 +116,11 @@ pub(crate) fn generate() -> String {
|
||||||
));
|
));
|
||||||
table_out.push_str("<br />");
|
table_out.push_str("<br />");
|
||||||
|
|
||||||
|
table_out.push_str(&format!(
|
||||||
|
"{SPACER}{REMOVED_SYMBOL}{SPACER} The rule has been removed only the documentation is available."
|
||||||
|
));
|
||||||
|
table_out.push_str("<br />");
|
||||||
|
|
||||||
table_out.push_str(&format!(
|
table_out.push_str(&format!(
|
||||||
"{SPACER}{FIX_SYMBOL}{SPACER} The rule is automatically fixable by the `--fix` command-line option."
|
"{SPACER}{FIX_SYMBOL}{SPACER} The rule is automatically fixable by the `--fix` command-line option."
|
||||||
));
|
));
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
# OK
|
|
||||||
|
|
||||||
1<2 and 'b' and 'c'
|
|
||||||
|
|
||||||
1<2 or 'a' and 'b'
|
|
||||||
|
|
||||||
1<2 and 'a'
|
|
||||||
|
|
||||||
1<2 or 'a'
|
|
||||||
|
|
||||||
2>1
|
|
||||||
|
|
||||||
1<2 and 'a' or 'b' and 'c'
|
|
||||||
|
|
||||||
1<2 and 'a' or 'b' or 'c'
|
|
||||||
|
|
||||||
1<2 and 'a' or 'b' or 'c' or (lambda x: x+1)
|
|
||||||
|
|
||||||
1<2 and 'a' or 'b' or (lambda x: x+1) or 'c'
|
|
||||||
|
|
||||||
default = 'default'
|
|
||||||
if (not isinstance(default, bool) and isinstance(default, int)) \
|
|
||||||
or (isinstance(default, str) and default):
|
|
||||||
pass
|
|
||||||
|
|
||||||
docid, token = None, None
|
|
||||||
(docid is None and token is None) or (docid is not None and token is not None)
|
|
||||||
|
|
||||||
vendor, os_version = 'darwin', '14'
|
|
||||||
vendor == "debian" and os_version in ["12"] or vendor == "ubuntu" and os_version in []
|
|
||||||
|
|
||||||
# Don't emit if the parent is an `if` statement.
|
|
||||||
if (task_id in task_dict and task_dict[task_id] is not task) \
|
|
||||||
or task_id in used_group_ids:
|
|
||||||
pass
|
|
||||||
|
|
||||||
no_target, is_x64, target = True, False, 'target'
|
|
||||||
if (no_target and not is_x64) or target == 'ARM_APPL_RUST_TARGET':
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Don't emit if the parent is a `bool_op` expression.
|
|
||||||
isinstance(val, str) and ((len(val) == 7 and val[0] == "#") or val in enums.NamedColor)
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
|
|
||||||
1<2 and 'a' or 'b'
|
|
||||||
|
|
||||||
(lambda x: x+1) and 'a' or 'b'
|
|
||||||
|
|
||||||
'a' and (lambda x: x+1) or 'orange'
|
|
||||||
|
|
||||||
val = '#0000FF'
|
|
||||||
(len(val) == 7 and val[0] == "#") or val in {'green'}
|
|
||||||
|
|
||||||
marker = 'marker'
|
|
||||||
isinstance(marker, dict) and 'field' in marker or marker in {}
|
|
||||||
|
|
||||||
def has_oranges(oranges, apples=None) -> bool:
|
|
||||||
return apples and False or oranges
|
|
||||||
|
|
||||||
[x for x in l if a and b or c]
|
|
||||||
|
|
||||||
{x: y for x in l if a and b or c}
|
|
||||||
|
|
||||||
{x for x in l if a and b or c}
|
|
||||||
|
|
||||||
new_list = [
|
|
||||||
x
|
|
||||||
for sublist in all_lists
|
|
||||||
if a and b or c
|
|
||||||
for x in sublist
|
|
||||||
if (isinstance(operator, list) and x in operator) or x != operator
|
|
||||||
]
|
|
|
@ -1508,9 +1508,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::RepeatedEqualityComparison) {
|
if checker.enabled(Rule::RepeatedEqualityComparison) {
|
||||||
pylint::rules::repeated_equality_comparison(checker, bool_op);
|
pylint::rules::repeated_equality_comparison(checker, bool_op);
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::AndOrTernary) {
|
|
||||||
pylint::rules::and_or_ternary(checker, bool_op);
|
|
||||||
}
|
|
||||||
if checker.enabled(Rule::UnnecessaryKeyCheck) {
|
if checker.enabled(Rule::UnnecessaryKeyCheck) {
|
||||||
ruff::rules::unnecessary_key_check(checker, expr);
|
ruff::rules::unnecessary_key_check(checker, expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub enum RuleGroup {
|
||||||
/// The rule has been deprecated, warnings will be displayed during selection in stable
|
/// The rule has been deprecated, warnings will be displayed during selection in stable
|
||||||
/// and errors will be raised if used with preview mode enabled.
|
/// and errors will be raised if used with preview mode enabled.
|
||||||
Deprecated,
|
Deprecated,
|
||||||
|
/// The rule has been removed, errors will be displayed on use.
|
||||||
|
Removed,
|
||||||
/// Legacy category for unstable rules, supports backwards compatible selection.
|
/// Legacy category for unstable rules, supports backwards compatible selection.
|
||||||
#[deprecated(note = "Use `RuleGroup::Preview` for new rules instead")]
|
#[deprecated(note = "Use `RuleGroup::Preview` for new rules instead")]
|
||||||
Nursery,
|
Nursery,
|
||||||
|
@ -268,7 +270,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal),
|
(Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal),
|
||||||
(Pylint, "R1711") => (RuleGroup::Stable, rules::pylint::rules::UselessReturn),
|
(Pylint, "R1711") => (RuleGroup::Stable, rules::pylint::rules::UselessReturn),
|
||||||
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
|
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
|
||||||
(Pylint, "R1706") => (RuleGroup::Preview, rules::pylint::rules::AndOrTernary),
|
(Pylint, "R1706") => (RuleGroup::Removed, rules::pylint::rules::AndOrTernary),
|
||||||
(Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias),
|
(Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias),
|
||||||
(Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup),
|
(Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup),
|
||||||
(Pylint, "R1736") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryListIndexLookup),
|
(Pylint, "R1736") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryListIndexLookup),
|
||||||
|
|
|
@ -213,6 +213,8 @@ impl RuleSelector {
|
||||||
|| (preview_enabled && (matches!(self, RuleSelector::Rule { .. }) || !preview_require_explicit))
|
|| (preview_enabled && (matches!(self, RuleSelector::Rule { .. }) || !preview_require_explicit))
|
||||||
// Deprecated rules are excluded in preview mode unless explicitly selected
|
// Deprecated rules are excluded in preview mode unless explicitly selected
|
||||||
|| (rule.is_deprecated() && (!preview_enabled || matches!(self, RuleSelector::Rule { .. })))
|
|| (rule.is_deprecated() && (!preview_enabled || matches!(self, RuleSelector::Rule { .. })))
|
||||||
|
// Removed rules are included if explicitly selected but will error downstream
|
||||||
|
|| (rule.is_removed() && matches!(self, RuleSelector::Rule { .. }))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,6 +249,8 @@ pub struct PreviewOptions {
|
||||||
|
|
||||||
#[cfg(feature = "schemars")]
|
#[cfg(feature = "schemars")]
|
||||||
mod schema {
|
mod schema {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use schemars::_serde_json::Value;
|
use schemars::_serde_json::Value;
|
||||||
|
@ -290,6 +294,16 @@ mod schema {
|
||||||
(!prefix.is_empty()).then(|| prefix.to_string())
|
(!prefix.is_empty()).then(|| prefix.to_string())
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
.filter(|p| {
|
||||||
|
// Exclude any prefixes where all of the rules are removed
|
||||||
|
if let Ok(Self::Rule { prefix, .. } | Self::Prefix { prefix, .. }) =
|
||||||
|
RuleSelector::from_str(p)
|
||||||
|
{
|
||||||
|
!prefix.rules().all(|rule| rule.is_removed())
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
.sorted()
|
.sorted()
|
||||||
.map(Value::String)
|
.map(Value::String)
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
|
@ -20,7 +20,6 @@ mod tests {
|
||||||
use crate::settings::LinterSettings;
|
use crate::settings::LinterSettings;
|
||||||
use crate::test::test_path;
|
use crate::test::test_path;
|
||||||
|
|
||||||
#[test_case(Rule::AndOrTernary, Path::new("and_or_ternary.py"))]
|
|
||||||
#[test_case(Rule::AssertOnStringLiteral, Path::new("assert_on_string_literal.py"))]
|
#[test_case(Rule::AssertOnStringLiteral, Path::new("assert_on_string_literal.py"))]
|
||||||
#[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.py"))]
|
#[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.py"))]
|
||||||
#[test_case(Rule::BadOpenMode, Path::new("bad_open_mode.py"))]
|
#[test_case(Rule::BadOpenMode, Path::new("bad_open_mode.py"))]
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::violation;
|
||||||
use ruff_python_ast::{
|
|
||||||
BoolOp, Expr, ExprBoolOp, ExprDictComp, ExprIfExp, ExprListComp, ExprSetComp,
|
|
||||||
};
|
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
|
||||||
use crate::fix::snippet::SourceCodeSnippet;
|
|
||||||
|
|
||||||
|
/// ## Removal
|
||||||
|
/// This rule was removed from Ruff because it was common for it to introduce behavioral changes.
|
||||||
|
/// See [#9007](https://github.com/astral-sh/ruff/issues/9007) for more information.
|
||||||
|
///
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for uses of the known pre-Python 2.5 ternary syntax.
|
/// Checks for uses of the known pre-Python 2.5 ternary syntax.
|
||||||
///
|
///
|
||||||
|
@ -31,100 +28,15 @@ use crate::fix::snippet::SourceCodeSnippet;
|
||||||
/// maximum = x if x >= y else y
|
/// maximum = x if x >= y else y
|
||||||
/// ```
|
/// ```
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct AndOrTernary {
|
pub struct AndOrTernary;
|
||||||
ternary: SourceCodeSnippet,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Violation for AndOrTernary {
|
|
||||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
|
||||||
|
|
||||||
#[derive_message_formats]
|
|
||||||
fn message(&self) -> String {
|
|
||||||
if let Some(ternary) = self.ternary.full_display() {
|
|
||||||
format!("Consider using if-else expression (`{ternary}`)")
|
|
||||||
} else {
|
|
||||||
format!("Consider using if-else expression")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fix_title(&self) -> Option<String> {
|
|
||||||
Some(format!("Convert to if-else expression"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Some((condition, true_value, false_value))`, if `bool_op` is of the form `condition and true_value or false_value`.
|
|
||||||
fn parse_and_or_ternary(bool_op: &ExprBoolOp) -> Option<(&Expr, &Expr, &Expr)> {
|
|
||||||
if bool_op.op != BoolOp::Or {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let [expr, false_value] = bool_op.values.as_slice() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let Some(and_op) = expr.as_bool_op_expr() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
if and_op.op != BoolOp::And {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let [condition, true_value] = and_op.values.as_slice() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
if false_value.is_bool_op_expr() || true_value.is_bool_op_expr() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some((condition, true_value, false_value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the expression is used within a comprehension.
|
|
||||||
fn is_comprehension_if(parent: Option<&Expr>, expr: &ExprBoolOp) -> bool {
|
|
||||||
let comprehensions = match parent {
|
|
||||||
Some(Expr::ListComp(ExprListComp { generators, .. })) => generators,
|
|
||||||
Some(Expr::SetComp(ExprSetComp { generators, .. })) => generators,
|
|
||||||
Some(Expr::DictComp(ExprDictComp { generators, .. })) => generators,
|
|
||||||
_ => {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
comprehensions
|
|
||||||
.iter()
|
|
||||||
.any(|comp| comp.ifs.iter().any(|ifs| ifs.range() == expr.range()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PLR1706
|
/// PLR1706
|
||||||
pub(crate) fn and_or_ternary(checker: &mut Checker, bool_op: &ExprBoolOp) {
|
impl Violation for AndOrTernary {
|
||||||
if checker.semantic().current_statement().is_if_stmt() {
|
fn message(&self) -> String {
|
||||||
return;
|
unreachable!("PLR1706 has been removed");
|
||||||
}
|
}
|
||||||
let parent_expr = checker.semantic().current_expression_parent();
|
|
||||||
if parent_expr.is_some_and(Expr::is_bool_op_expr) {
|
fn message_formats() -> &'static [&'static str] {
|
||||||
return;
|
&["Consider using if-else expression"]
|
||||||
}
|
}
|
||||||
let Some((condition, true_value, false_value)) = parse_and_or_ternary(bool_op) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let if_expr = Expr::IfExp(ExprIfExp {
|
|
||||||
test: Box::new(condition.clone()),
|
|
||||||
body: Box::new(true_value.clone()),
|
|
||||||
orelse: Box::new(false_value.clone()),
|
|
||||||
range: TextRange::default(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let ternary = if is_comprehension_if(parent_expr, bool_op) {
|
|
||||||
format!("({})", checker.generator().expr(&if_expr))
|
|
||||||
} else {
|
|
||||||
checker.generator().expr(&if_expr)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut diagnostic = Diagnostic::new(
|
|
||||||
AndOrTernary {
|
|
||||||
ternary: SourceCodeSnippet::new(ternary.clone()),
|
|
||||||
},
|
|
||||||
bool_op.range,
|
|
||||||
);
|
|
||||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
|
||||||
ternary,
|
|
||||||
bool_op.range,
|
|
||||||
)));
|
|
||||||
checker.diagnostics.push(diagnostic);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
---
|
|
||||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
|
||||||
---
|
|
||||||
and_or_ternary.py:46:1: PLR1706 [*] Consider using if-else expression (`'a' if 1 < 2 else 'b'`)
|
|
||||||
|
|
|
||||||
44 | # Errors
|
|
||||||
45 |
|
|
||||||
46 | 1<2 and 'a' or 'b'
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
47 |
|
|
||||||
48 | (lambda x: x+1) and 'a' or 'b'
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
43 43 |
|
|
||||||
44 44 | # Errors
|
|
||||||
45 45 |
|
|
||||||
46 |-1<2 and 'a' or 'b'
|
|
||||||
46 |+'a' if 1 < 2 else 'b'
|
|
||||||
47 47 |
|
|
||||||
48 48 | (lambda x: x+1) and 'a' or 'b'
|
|
||||||
49 49 |
|
|
||||||
|
|
||||||
and_or_ternary.py:48:1: PLR1706 [*] Consider using if-else expression (`'a' if (lambda x: x + 1) else 'b'`)
|
|
||||||
|
|
|
||||||
46 | 1<2 and 'a' or 'b'
|
|
||||||
47 |
|
|
||||||
48 | (lambda x: x+1) and 'a' or 'b'
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
49 |
|
|
||||||
50 | 'a' and (lambda x: x+1) or 'orange'
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
45 45 |
|
|
||||||
46 46 | 1<2 and 'a' or 'b'
|
|
||||||
47 47 |
|
|
||||||
48 |-(lambda x: x+1) and 'a' or 'b'
|
|
||||||
48 |+'a' if (lambda x: x + 1) else 'b'
|
|
||||||
49 49 |
|
|
||||||
50 50 | 'a' and (lambda x: x+1) or 'orange'
|
|
||||||
51 51 |
|
|
||||||
|
|
||||||
and_or_ternary.py:50:1: PLR1706 [*] Consider using if-else expression (`(lambda x: x + 1) if 'a' else 'orange'`)
|
|
||||||
|
|
|
||||||
48 | (lambda x: x+1) and 'a' or 'b'
|
|
||||||
49 |
|
|
||||||
50 | 'a' and (lambda x: x+1) or 'orange'
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
51 |
|
|
||||||
52 | val = '#0000FF'
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
47 47 |
|
|
||||||
48 48 | (lambda x: x+1) and 'a' or 'b'
|
|
||||||
49 49 |
|
|
||||||
50 |-'a' and (lambda x: x+1) or 'orange'
|
|
||||||
50 |+(lambda x: x + 1) if 'a' else 'orange'
|
|
||||||
51 51 |
|
|
||||||
52 52 | val = '#0000FF'
|
|
||||||
53 53 | (len(val) == 7 and val[0] == "#") or val in {'green'}
|
|
||||||
|
|
||||||
and_or_ternary.py:53:1: PLR1706 [*] Consider using if-else expression
|
|
||||||
|
|
|
||||||
52 | val = '#0000FF'
|
|
||||||
53 | (len(val) == 7 and val[0] == "#") or val in {'green'}
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
54 |
|
|
||||||
55 | marker = 'marker'
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
50 50 | 'a' and (lambda x: x+1) or 'orange'
|
|
||||||
51 51 |
|
|
||||||
52 52 | val = '#0000FF'
|
|
||||||
53 |-(len(val) == 7 and val[0] == "#") or val in {'green'}
|
|
||||||
53 |+val[0] == '#' if len(val) == 7 else val in {'green'}
|
|
||||||
54 54 |
|
|
||||||
55 55 | marker = 'marker'
|
|
||||||
56 56 | isinstance(marker, dict) and 'field' in marker or marker in {}
|
|
||||||
|
|
||||||
and_or_ternary.py:56:1: PLR1706 [*] Consider using if-else expression
|
|
||||||
|
|
|
||||||
55 | marker = 'marker'
|
|
||||||
56 | isinstance(marker, dict) and 'field' in marker or marker in {}
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
57 |
|
|
||||||
58 | def has_oranges(oranges, apples=None) -> bool:
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
53 53 | (len(val) == 7 and val[0] == "#") or val in {'green'}
|
|
||||||
54 54 |
|
|
||||||
55 55 | marker = 'marker'
|
|
||||||
56 |-isinstance(marker, dict) and 'field' in marker or marker in {}
|
|
||||||
56 |+'field' in marker if isinstance(marker, dict) else marker in {}
|
|
||||||
57 57 |
|
|
||||||
58 58 | def has_oranges(oranges, apples=None) -> bool:
|
|
||||||
59 59 | return apples and False or oranges
|
|
||||||
|
|
||||||
and_or_ternary.py:59:12: PLR1706 [*] Consider using if-else expression (`False if apples else oranges`)
|
|
||||||
|
|
|
||||||
58 | def has_oranges(oranges, apples=None) -> bool:
|
|
||||||
59 | return apples and False or oranges
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
60 |
|
|
||||||
61 | [x for x in l if a and b or c]
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
56 56 | isinstance(marker, dict) and 'field' in marker or marker in {}
|
|
||||||
57 57 |
|
|
||||||
58 58 | def has_oranges(oranges, apples=None) -> bool:
|
|
||||||
59 |- return apples and False or oranges
|
|
||||||
59 |+ return False if apples else oranges
|
|
||||||
60 60 |
|
|
||||||
61 61 | [x for x in l if a and b or c]
|
|
||||||
62 62 |
|
|
||||||
|
|
||||||
and_or_ternary.py:61:18: PLR1706 [*] Consider using if-else expression (`(b if a else c)`)
|
|
||||||
|
|
|
||||||
59 | return apples and False or oranges
|
|
||||||
60 |
|
|
||||||
61 | [x for x in l if a and b or c]
|
|
||||||
| ^^^^^^^^^^^^ PLR1706
|
|
||||||
62 |
|
|
||||||
63 | {x: y for x in l if a and b or c}
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
58 58 | def has_oranges(oranges, apples=None) -> bool:
|
|
||||||
59 59 | return apples and False or oranges
|
|
||||||
60 60 |
|
|
||||||
61 |-[x for x in l if a and b or c]
|
|
||||||
61 |+[x for x in l if (b if a else c)]
|
|
||||||
62 62 |
|
|
||||||
63 63 | {x: y for x in l if a and b or c}
|
|
||||||
64 64 |
|
|
||||||
|
|
||||||
and_or_ternary.py:63:21: PLR1706 [*] Consider using if-else expression (`(b if a else c)`)
|
|
||||||
|
|
|
||||||
61 | [x for x in l if a and b or c]
|
|
||||||
62 |
|
|
||||||
63 | {x: y for x in l if a and b or c}
|
|
||||||
| ^^^^^^^^^^^^ PLR1706
|
|
||||||
64 |
|
|
||||||
65 | {x for x in l if a and b or c}
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
60 60 |
|
|
||||||
61 61 | [x for x in l if a and b or c]
|
|
||||||
62 62 |
|
|
||||||
63 |-{x: y for x in l if a and b or c}
|
|
||||||
63 |+{x: y for x in l if (b if a else c)}
|
|
||||||
64 64 |
|
|
||||||
65 65 | {x for x in l if a and b or c}
|
|
||||||
66 66 |
|
|
||||||
|
|
||||||
and_or_ternary.py:65:18: PLR1706 [*] Consider using if-else expression (`(b if a else c)`)
|
|
||||||
|
|
|
||||||
63 | {x: y for x in l if a and b or c}
|
|
||||||
64 |
|
|
||||||
65 | {x for x in l if a and b or c}
|
|
||||||
| ^^^^^^^^^^^^ PLR1706
|
|
||||||
66 |
|
|
||||||
67 | new_list = [
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
62 62 |
|
|
||||||
63 63 | {x: y for x in l if a and b or c}
|
|
||||||
64 64 |
|
|
||||||
65 |-{x for x in l if a and b or c}
|
|
||||||
65 |+{x for x in l if (b if a else c)}
|
|
||||||
66 66 |
|
|
||||||
67 67 | new_list = [
|
|
||||||
68 68 | x
|
|
||||||
|
|
||||||
and_or_ternary.py:70:8: PLR1706 [*] Consider using if-else expression (`(b if a else c)`)
|
|
||||||
|
|
|
||||||
68 | x
|
|
||||||
69 | for sublist in all_lists
|
|
||||||
70 | if a and b or c
|
|
||||||
| ^^^^^^^^^^^^ PLR1706
|
|
||||||
71 | for x in sublist
|
|
||||||
72 | if (isinstance(operator, list) and x in operator) or x != operator
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
67 67 | new_list = [
|
|
||||||
68 68 | x
|
|
||||||
69 69 | for sublist in all_lists
|
|
||||||
70 |- if a and b or c
|
|
||||||
70 |+ if (b if a else c)
|
|
||||||
71 71 | for x in sublist
|
|
||||||
72 72 | if (isinstance(operator, list) and x in operator) or x != operator
|
|
||||||
73 73 | ]
|
|
||||||
|
|
||||||
and_or_ternary.py:72:8: PLR1706 [*] Consider using if-else expression
|
|
||||||
|
|
|
||||||
70 | if a and b or c
|
|
||||||
71 | for x in sublist
|
|
||||||
72 | if (isinstance(operator, list) and x in operator) or x != operator
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706
|
|
||||||
73 | ]
|
|
||||||
|
|
|
||||||
= help: Convert to if-else expression
|
|
||||||
|
|
||||||
ℹ Unsafe fix
|
|
||||||
69 69 | for sublist in all_lists
|
|
||||||
70 70 | if a and b or c
|
|
||||||
71 71 | for x in sublist
|
|
||||||
72 |- if (isinstance(operator, list) and x in operator) or x != operator
|
|
||||||
72 |+ if (x in operator if isinstance(operator, list) else x != operator)
|
|
||||||
73 73 | ]
|
|
||||||
|
|
||||||
|
|
|
@ -329,6 +329,10 @@ See also https://github.com/astral-sh/ruff/issues/2186.
|
||||||
pub fn is_deprecated(&self) -> bool {
|
pub fn is_deprecated(&self) -> bool {
|
||||||
matches!(self.group(), RuleGroup::Deprecated)
|
matches!(self.group(), RuleGroup::Deprecated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_removed(&self) -> bool {
|
||||||
|
matches!(self.group(), RuleGroup::Removed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Linter {
|
impl Linter {
|
||||||
|
|
|
@ -756,6 +756,7 @@ impl LintConfiguration {
|
||||||
let mut redirects = FxHashMap::default();
|
let mut redirects = FxHashMap::default();
|
||||||
let mut deprecated_nursery_selectors = FxHashSet::default();
|
let mut deprecated_nursery_selectors = FxHashSet::default();
|
||||||
let mut deprecated_selectors = FxHashSet::default();
|
let mut deprecated_selectors = FxHashSet::default();
|
||||||
|
let mut removed_selectors = FxHashSet::default();
|
||||||
let mut ignored_preview_selectors = FxHashSet::default();
|
let mut ignored_preview_selectors = FxHashSet::default();
|
||||||
|
|
||||||
// Track which docstring rules are specifically enabled
|
// Track which docstring rules are specifically enabled
|
||||||
|
@ -922,6 +923,13 @@ impl LintConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removed rules
|
||||||
|
if let RuleSelector::Rule { prefix, .. } = selector {
|
||||||
|
if prefix.rules().any(|rule| rule.is_removed()) {
|
||||||
|
removed_selectors.insert(selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Redirected rules
|
// Redirected rules
|
||||||
if let RuleSelector::Prefix {
|
if let RuleSelector::Prefix {
|
||||||
prefix,
|
prefix,
|
||||||
|
@ -933,6 +941,29 @@ impl LintConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let removed_selectors = removed_selectors.iter().collect::<Vec<_>>();
|
||||||
|
match removed_selectors.as_slice() {
|
||||||
|
[] => (),
|
||||||
|
[selection] => {
|
||||||
|
let (prefix, code) = selection.prefix_and_code();
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Rule `{prefix}{code}` was removed and cannot be selected."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
[..] => {
|
||||||
|
let mut message =
|
||||||
|
"The following rules have been removed and cannot be selected:".to_string();
|
||||||
|
for selection in removed_selectors {
|
||||||
|
let (prefix, code) = selection.prefix_and_code();
|
||||||
|
message.push_str("\n - ");
|
||||||
|
message.push_str(prefix);
|
||||||
|
message.push_str(code);
|
||||||
|
}
|
||||||
|
message.push('\n');
|
||||||
|
return Err(anyhow!(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (from, target) in redirects {
|
for (from, target) in redirects {
|
||||||
// TODO(martin): This belongs into the ruff crate.
|
// TODO(martin): This belongs into the ruff crate.
|
||||||
warn_user_once_by_id!(
|
warn_user_once_by_id!(
|
||||||
|
@ -1423,7 +1454,6 @@ mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
const PREVIEW_RULES: &[Rule] = &[
|
const PREVIEW_RULES: &[Rule] = &[
|
||||||
Rule::AndOrTernary,
|
|
||||||
Rule::AssignmentInAssert,
|
Rule::AssignmentInAssert,
|
||||||
Rule::DirectLoggerInstantiation,
|
Rule::DirectLoggerInstantiation,
|
||||||
Rule::InvalidGetLoggerArgument,
|
Rule::InvalidGetLoggerArgument,
|
||||||
|
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -3267,7 +3267,6 @@
|
||||||
"PLR1701",
|
"PLR1701",
|
||||||
"PLR1702",
|
"PLR1702",
|
||||||
"PLR1704",
|
"PLR1704",
|
||||||
"PLR1706",
|
|
||||||
"PLR171",
|
"PLR171",
|
||||||
"PLR1711",
|
"PLR1711",
|
||||||
"PLR1714",
|
"PLR1714",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue