[`pylint`] Add `named_expr_without_context` (`W0131`) (#4531)

This commit is contained in:
Hoël Bagard 2023-05-20 03:00:01 +09:00 committed by GitHub
parent a9ed8d5391
commit fe8e2bb237
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 100 additions and 0 deletions

View File

@ -0,0 +1,19 @@
# Errors
(a := 42)
if True:
(b := 1)
class Foo:
(c := 1)
# OK
if a := 42:
print("Success")
a = 0
while (a := a + 1) < 10:
print("Correct")
a = (b := 1)

View File

@ -1895,6 +1895,9 @@ where
if self.settings.rules.enabled(Rule::InvalidMockAccess) { if self.settings.rules.enabled(Rule::InvalidMockAccess) {
pygrep_hooks::rules::uncalled_mock_method(self, value); pygrep_hooks::rules::uncalled_mock_method(self, value);
} }
if self.settings.rules.enabled(Rule::NamedExprWithoutContext) {
pylint::rules::named_expr_without_context(self, value);
}
if self.settings.rules.enabled(Rule::AsyncioDanglingTask) { if self.settings.rules.enabled(Rule::AsyncioDanglingTask) {
if let Some(diagnostic) = ruff::rules::asyncio_dangling_task(value, |expr| { if let Some(diagnostic) = ruff::rules::asyncio_dangling_task(value, |expr| {
self.ctx.resolve_call_path(expr) self.ctx.resolve_call_path(expr)

View File

@ -185,6 +185,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "R5501") => (RuleGroup::Unspecified, Rule::CollapsibleElseIf), (Pylint, "R5501") => (RuleGroup::Unspecified, Rule::CollapsibleElseIf),
(Pylint, "W0120") => (RuleGroup::Unspecified, Rule::UselessElseOnLoop), (Pylint, "W0120") => (RuleGroup::Unspecified, Rule::UselessElseOnLoop),
(Pylint, "W0129") => (RuleGroup::Unspecified, Rule::AssertOnStringLiteral), (Pylint, "W0129") => (RuleGroup::Unspecified, Rule::AssertOnStringLiteral),
(Pylint, "W0131") => (RuleGroup::Unspecified, Rule::NamedExprWithoutContext),
(Pylint, "W0406") => (RuleGroup::Unspecified, Rule::ImportSelf), (Pylint, "W0406") => (RuleGroup::Unspecified, Rule::ImportSelf),
(Pylint, "W0602") => (RuleGroup::Unspecified, Rule::GlobalVariableNotAssigned), (Pylint, "W0602") => (RuleGroup::Unspecified, Rule::GlobalVariableNotAssigned),
(Pylint, "W0603") => (RuleGroup::Unspecified, Rule::GlobalStatement), (Pylint, "W0603") => (RuleGroup::Unspecified, Rule::GlobalStatement),

View File

@ -161,6 +161,7 @@ ruff_macros::register_rules!(
rules::pylint::rules::NestedMinMax, rules::pylint::rules::NestedMinMax,
rules::pylint::rules::DuplicateValue, rules::pylint::rules::DuplicateValue,
rules::pylint::rules::DuplicateBases, rules::pylint::rules::DuplicateBases,
rules::pylint::rules::NamedExprWithoutContext,
// flake8-async // flake8-async
rules::flake8_async::rules::BlockingHttpCallInAsyncFunction, rules::flake8_async::rules::BlockingHttpCallInAsyncFunction,
rules::flake8_async::rules::OpenSleepOrSubprocessInAsyncFunction, rules::flake8_async::rules::OpenSleepOrSubprocessInAsyncFunction,

View File

@ -58,6 +58,7 @@ mod tests {
#[test_case(Rule::LoggingTooFewArgs, Path::new("logging_too_few_args.py"); "PLE1206")] #[test_case(Rule::LoggingTooFewArgs, Path::new("logging_too_few_args.py"); "PLE1206")]
#[test_case(Rule::LoggingTooManyArgs, Path::new("logging_too_many_args.py"); "PLE1205")] #[test_case(Rule::LoggingTooManyArgs, Path::new("logging_too_many_args.py"); "PLE1205")]
#[test_case(Rule::MagicValueComparison, Path::new("magic_value_comparison.py"); "PLR2004")] #[test_case(Rule::MagicValueComparison, Path::new("magic_value_comparison.py"); "PLR2004")]
#[test_case(Rule::NamedExprWithoutContext, Path::new("named_expr_without_context.py"); "PLW0131")]
#[test_case(Rule::NonlocalWithoutBinding, Path::new("nonlocal_without_binding.py"); "PLE0117")] #[test_case(Rule::NonlocalWithoutBinding, Path::new("nonlocal_without_binding.py"); "PLE0117")]
#[test_case(Rule::PropertyWithParameters, Path::new("property_with_parameters.py"); "PLR0206")] #[test_case(Rule::PropertyWithParameters, Path::new("property_with_parameters.py"); "PLR0206")]
#[test_case(Rule::RedefinedLoopName, Path::new("redefined_loop_name.py"); "PLW2901")] #[test_case(Rule::RedefinedLoopName, Path::new("redefined_loop_name.py"); "PLW2901")]

View File

@ -27,6 +27,7 @@ pub(crate) use load_before_global_declaration::{
pub(crate) use logging::{logging_call, LoggingTooFewArgs, LoggingTooManyArgs}; pub(crate) use logging::{logging_call, LoggingTooFewArgs, LoggingTooManyArgs};
pub(crate) use magic_value_comparison::{magic_value_comparison, MagicValueComparison}; pub(crate) use magic_value_comparison::{magic_value_comparison, MagicValueComparison};
pub(crate) use manual_import_from::{manual_from_import, ManualFromImport}; pub(crate) use manual_import_from::{manual_from_import, ManualFromImport};
pub(crate) use named_expr_without_context::{named_expr_without_context, NamedExprWithoutContext};
pub(crate) use nested_min_max::{nested_min_max, NestedMinMax}; pub(crate) use nested_min_max::{nested_min_max, NestedMinMax};
pub(crate) use nonlocal_without_binding::NonlocalWithoutBinding; pub(crate) use nonlocal_without_binding::NonlocalWithoutBinding;
pub(crate) use property_with_parameters::{property_with_parameters, PropertyWithParameters}; pub(crate) use property_with_parameters::{property_with_parameters, PropertyWithParameters};
@ -73,6 +74,7 @@ mod load_before_global_declaration;
mod logging; mod logging;
mod magic_value_comparison; mod magic_value_comparison;
mod manual_import_from; mod manual_import_from;
mod named_expr_without_context;
mod nested_min_max; mod nested_min_max;
mod nonlocal_without_binding; mod nonlocal_without_binding;
mod property_with_parameters; mod property_with_parameters;

View File

@ -0,0 +1,44 @@
use rustpython_parser::ast::{self, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for usages of named expressions (e.g., `a := 42`) that can be
/// replaced by regular assignment statements (e.g., `a = 42`).
///
/// ## Why is this bad?
/// While a top-level named expression is syntactically and semantically valid,
/// it's less clear than a regular assignment statement. Named expressions are
/// intended to be used in comprehensions and generator expressions, where
/// assignment statements are not allowed.
///
/// ## Example
/// ```python
/// (a := 42)
/// ```
///
/// Use instead:
/// ```python
/// a = 42
/// ```
#[violation]
pub struct NamedExprWithoutContext;
impl Violation for NamedExprWithoutContext {
#[derive_message_formats]
fn message(&self) -> String {
format!("Named expression used without context")
}
}
/// PLW0131
pub(crate) fn named_expr_without_context(checker: &mut Checker, value: &Expr) {
if let Expr::NamedExpr(ast::ExprNamedExpr { range, .. }) = value {
checker
.diagnostics
.push(Diagnostic::new(NamedExprWithoutContext, *range));
}
}

View File

@ -0,0 +1,28 @@
---
source: crates/ruff/src/rules/pylint/mod.rs
---
named_expr_without_context.py:2:2: PLW0131 Named expression used without context
|
2 | # Errors
3 | (a := 42)
| ^^^^^^^ PLW0131
4 | if True:
5 | (b := 1)
|
named_expr_without_context.py:4:6: PLW0131 Named expression used without context
|
4 | (a := 42)
5 | if True:
6 | (b := 1)
| ^^^^^^ PLW0131
|
named_expr_without_context.py:8:6: PLW0131 Named expression used without context
|
8 | class Foo:
9 | (c := 1)
| ^^^^^^ PLW0131
|

1
ruff.schema.json generated
View File

@ -2078,6 +2078,7 @@
"PLW0129", "PLW0129",
"PLW013", "PLW013",
"PLW0130", "PLW0130",
"PLW0131",
"PLW04", "PLW04",
"PLW040", "PLW040",
"PLW0406", "PLW0406",