mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Move pyflakes violations to rule modules (#2488)
This commit is contained in:
parent
e89b4a5de5
commit
40cb905ae5
27 changed files with 1097 additions and 987 deletions
|
@ -28,7 +28,6 @@ use crate::ast::{branch_detection, cast, helpers, operations, visitor};
|
|||
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable};
|
||||
use crate::noqa::Directive;
|
||||
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use crate::python::future::ALL_FEATURE_NAMES;
|
||||
use crate::python::typing;
|
||||
use crate::python::typing::{Callable, SubscriptKind};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
|
@ -44,7 +43,6 @@ use crate::rules::{
|
|||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
use crate::violations::DeferralKeyword;
|
||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||
use crate::{autofix, docstrings, noqa, violations, visibility};
|
||||
|
||||
|
@ -55,7 +53,7 @@ type DeferralContext<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
|
|||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Checker<'a> {
|
||||
// Input data.
|
||||
path: &'a Path,
|
||||
pub(crate) path: &'a Path,
|
||||
package: Option<&'a Path>,
|
||||
autofix: flags::Autofix,
|
||||
noqa: flags::Noqa,
|
||||
|
@ -720,17 +718,7 @@ where
|
|||
}
|
||||
StmtKind::Return { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::ReturnOutsideFunction) {
|
||||
if let Some(&index) = self.scope_stack.last() {
|
||||
if matches!(
|
||||
self.scopes[index].kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ReturnOutsideFunction,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
pyflakes::rules::return_outside_function(self, stmt);
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef {
|
||||
|
@ -1170,21 +1158,14 @@ where
|
|||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::FutureFeatureNotDefined) {
|
||||
if !ALL_FEATURE_NAMES.contains(&&*alias.node.name) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::FutureFeatureNotDefined {
|
||||
name: alias.node.name.to_string(),
|
||||
},
|
||||
Range::from_located(alias),
|
||||
));
|
||||
}
|
||||
pyflakes::rules::future_feature_not_defined(self, alias);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::LateFutureImport)
|
||||
&& !self.futures_allowed
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::LateFutureImport,
|
||||
pyflakes::rules::LateFutureImport,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
|
@ -1207,7 +1188,7 @@ where
|
|||
[*(self.scope_stack.last().expect("No current scope found"))];
|
||||
if !matches!(scope.kind, ScopeKind::Module) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ImportStarNotPermitted {
|
||||
pyflakes::rules::ImportStarNotPermitted {
|
||||
name: helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
|
@ -1220,7 +1201,7 @@ where
|
|||
|
||||
if self.settings.rules.enabled(&Rule::ImportStarUsed) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ImportStarUsed {
|
||||
pyflakes::rules::ImportStarUsed {
|
||||
name: helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
|
@ -2197,7 +2178,7 @@ where
|
|||
.enabled(&Rule::StringDotFormatInvalidFormat)
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::StringDotFormatInvalidFormat {
|
||||
pyflakes::rules::StringDotFormatInvalidFormat {
|
||||
message: pyflakes::format::error_to_string(&e),
|
||||
},
|
||||
location,
|
||||
|
@ -2753,41 +2734,17 @@ where
|
|||
}
|
||||
ExprKind::Yield { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
let scope = self.current_scope();
|
||||
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::YieldOutsideFunction {
|
||||
keyword: DeferralKeyword::Yield,
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
pyflakes::rules::yield_outside_function(self, expr);
|
||||
}
|
||||
}
|
||||
ExprKind::YieldFrom { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
let scope = self.current_scope();
|
||||
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::YieldOutsideFunction {
|
||||
keyword: DeferralKeyword::YieldFrom,
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
pyflakes::rules::yield_outside_function(self, expr);
|
||||
}
|
||||
}
|
||||
ExprKind::Await { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
let scope = self.current_scope();
|
||||
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::YieldOutsideFunction {
|
||||
keyword: DeferralKeyword::Await,
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
pyflakes::rules::yield_outside_function(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AwaitOutsideAsync) {
|
||||
pylint::rules::await_outside_async(self, expr);
|
||||
|
@ -2870,7 +2827,7 @@ where
|
|||
.enabled(&Rule::PercentFormatUnsupportedFormatCharacter)
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatUnsupportedFormatCharacter {
|
||||
pyflakes::rules::PercentFormatUnsupportedFormatCharacter {
|
||||
char: c,
|
||||
},
|
||||
location,
|
||||
|
@ -2884,7 +2841,7 @@ where
|
|||
.enabled(&Rule::PercentFormatInvalidFormat)
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatInvalidFormat {
|
||||
pyflakes::rules::PercentFormatInvalidFormat {
|
||||
message: e.to_string(),
|
||||
},
|
||||
location,
|
||||
|
@ -3574,7 +3531,7 @@ where
|
|||
if !self.bindings[*index].used() {
|
||||
if self.settings.rules.enabled(&Rule::UnusedVariable) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedVariable {
|
||||
pyflakes::rules::UnusedVariable {
|
||||
name: name.to_string(),
|
||||
},
|
||||
name_range,
|
||||
|
@ -3894,7 +3851,7 @@ impl<'a> Checker<'a> {
|
|||
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
|
||||
if self.settings.rules.enabled(&Rule::ImportShadowedByLoopVar) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ImportShadowedByLoopVar {
|
||||
pyflakes::rules::ImportShadowedByLoopVar {
|
||||
name: name.to_string(),
|
||||
line: existing.range.location.row(),
|
||||
},
|
||||
|
@ -3913,7 +3870,7 @@ impl<'a> Checker<'a> {
|
|||
{
|
||||
if self.settings.rules.enabled(&Rule::RedefinedWhileUnused) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::RedefinedWhileUnused {
|
||||
pyflakes::rules::RedefinedWhileUnused {
|
||||
name: name.to_string(),
|
||||
line: existing.range.location.row(),
|
||||
},
|
||||
|
@ -4080,7 +4037,7 @@ impl<'a> Checker<'a> {
|
|||
from_list.sort();
|
||||
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ImportStarUsage {
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: id.to_string(),
|
||||
sources: from_list,
|
||||
},
|
||||
|
@ -4114,7 +4071,7 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::UndefinedName { name: id.clone() },
|
||||
pyflakes::rules::UndefinedName { name: id.clone() },
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
|
@ -4322,7 +4279,7 @@ impl<'a> Checker<'a> {
|
|||
&& self.settings.rules.enabled(&Rule::UndefinedName)
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::UndefinedName {
|
||||
pyflakes::rules::UndefinedName {
|
||||
name: id.to_string(),
|
||||
},
|
||||
Range::from_located(expr),
|
||||
|
@ -4390,7 +4347,7 @@ impl<'a> Checker<'a> {
|
|||
.enabled(&Rule::ForwardAnnotationSyntaxError)
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ForwardAnnotationSyntaxError {
|
||||
pyflakes::rules::ForwardAnnotationSyntaxError {
|
||||
body: expression.to_string(),
|
||||
},
|
||||
range,
|
||||
|
@ -4597,7 +4554,7 @@ impl<'a> Checker<'a> {
|
|||
for &name in names {
|
||||
if !scope.values.contains_key(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::UndefinedExport {
|
||||
pyflakes::rules::UndefinedExport {
|
||||
name: name.to_string(),
|
||||
},
|
||||
all_binding.range,
|
||||
|
@ -4637,7 +4594,7 @@ impl<'a> Checker<'a> {
|
|||
if let Some(indices) = self.redefinitions.get(index) {
|
||||
for index in indices {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::RedefinedWhileUnused {
|
||||
pyflakes::rules::RedefinedWhileUnused {
|
||||
name: (*name).to_string(),
|
||||
line: binding.range.location.row(),
|
||||
},
|
||||
|
@ -4668,7 +4625,7 @@ impl<'a> Checker<'a> {
|
|||
for &name in names {
|
||||
if !scope.values.contains_key(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::ImportStarUsage {
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: name.to_string(),
|
||||
sources: from_list.clone(),
|
||||
},
|
||||
|
@ -4834,7 +4791,7 @@ impl<'a> Checker<'a> {
|
|||
let multiple = unused_imports.len() > 1;
|
||||
for (full_name, range) in unused_imports {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedImport {
|
||||
pyflakes::rules::UnusedImport {
|
||||
name: full_name.to_string(),
|
||||
ignore_init,
|
||||
multiple,
|
||||
|
@ -4860,7 +4817,7 @@ impl<'a> Checker<'a> {
|
|||
let multiple = unused_imports.len() > 1;
|
||||
for (full_name, range) in unused_imports {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedImport {
|
||||
pyflakes::rules::UnusedImport {
|
||||
name: full_name.to_string(),
|
||||
ignore_init,
|
||||
multiple,
|
||||
|
|
|
@ -242,8 +242,8 @@ mod tests {
|
|||
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
|
||||
use crate::rules::pyflakes;
|
||||
use crate::source_code::LineEnding;
|
||||
use crate::violations;
|
||||
|
||||
#[test]
|
||||
fn regex() {
|
||||
|
@ -276,7 +276,7 @@ mod tests {
|
|||
assert_eq!(output, format!("{contents}\n"));
|
||||
|
||||
let diagnostics = vec![Diagnostic::new(
|
||||
violations::UnusedVariable {
|
||||
pyflakes::rules::UnusedVariable {
|
||||
name: "x".to_string(),
|
||||
},
|
||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||
|
@ -300,7 +300,7 @@ mod tests {
|
|||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||
),
|
||||
Diagnostic::new(
|
||||
violations::UnusedVariable {
|
||||
pyflakes::rules::UnusedVariable {
|
||||
name: "x".to_string(),
|
||||
},
|
||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||
|
@ -325,7 +325,7 @@ mod tests {
|
|||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||
),
|
||||
Diagnostic::new(
|
||||
violations::UnusedVariable {
|
||||
pyflakes::rules::UnusedVariable {
|
||||
name: "x".to_string(),
|
||||
},
|
||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||
|
|
|
@ -33,49 +33,49 @@ ruff_macros::define_rule_mapping!(
|
|||
W505 => rules::pycodestyle::rules::DocLineTooLong,
|
||||
W605 => rules::pycodestyle::rules::InvalidEscapeSequence,
|
||||
// pyflakes
|
||||
F401 => violations::UnusedImport,
|
||||
F402 => violations::ImportShadowedByLoopVar,
|
||||
F403 => violations::ImportStarUsed,
|
||||
F404 => violations::LateFutureImport,
|
||||
F405 => violations::ImportStarUsage,
|
||||
F406 => violations::ImportStarNotPermitted,
|
||||
F407 => violations::FutureFeatureNotDefined,
|
||||
F501 => violations::PercentFormatInvalidFormat,
|
||||
F502 => violations::PercentFormatExpectedMapping,
|
||||
F503 => violations::PercentFormatExpectedSequence,
|
||||
F504 => violations::PercentFormatExtraNamedArguments,
|
||||
F505 => violations::PercentFormatMissingArgument,
|
||||
F506 => violations::PercentFormatMixedPositionalAndNamed,
|
||||
F507 => violations::PercentFormatPositionalCountMismatch,
|
||||
F508 => violations::PercentFormatStarRequiresSequence,
|
||||
F509 => violations::PercentFormatUnsupportedFormatCharacter,
|
||||
F521 => violations::StringDotFormatInvalidFormat,
|
||||
F522 => violations::StringDotFormatExtraNamedArguments,
|
||||
F523 => violations::StringDotFormatExtraPositionalArguments,
|
||||
F524 => violations::StringDotFormatMissingArguments,
|
||||
F525 => violations::StringDotFormatMixingAutomatic,
|
||||
F541 => violations::FStringMissingPlaceholders,
|
||||
F601 => violations::MultiValueRepeatedKeyLiteral,
|
||||
F602 => violations::MultiValueRepeatedKeyVariable,
|
||||
F621 => violations::ExpressionsInStarAssignment,
|
||||
F622 => violations::TwoStarredExpressions,
|
||||
F631 => violations::AssertTuple,
|
||||
F632 => violations::IsLiteral,
|
||||
F633 => violations::InvalidPrintSyntax,
|
||||
F634 => violations::IfTuple,
|
||||
F701 => violations::BreakOutsideLoop,
|
||||
F702 => violations::ContinueOutsideLoop,
|
||||
F704 => violations::YieldOutsideFunction,
|
||||
F706 => violations::ReturnOutsideFunction,
|
||||
F707 => violations::DefaultExceptNotLast,
|
||||
F722 => violations::ForwardAnnotationSyntaxError,
|
||||
F811 => violations::RedefinedWhileUnused,
|
||||
F821 => violations::UndefinedName,
|
||||
F822 => violations::UndefinedExport,
|
||||
F823 => violations::UndefinedLocal,
|
||||
F841 => violations::UnusedVariable,
|
||||
F842 => violations::UnusedAnnotation,
|
||||
F901 => violations::RaiseNotImplemented,
|
||||
F401 => rules::pyflakes::rules::UnusedImport,
|
||||
F402 => rules::pyflakes::rules::ImportShadowedByLoopVar,
|
||||
F403 => rules::pyflakes::rules::ImportStarUsed,
|
||||
F404 => rules::pyflakes::rules::LateFutureImport,
|
||||
F405 => rules::pyflakes::rules::ImportStarUsage,
|
||||
F406 => rules::pyflakes::rules::ImportStarNotPermitted,
|
||||
F407 => rules::pyflakes::rules::FutureFeatureNotDefined,
|
||||
F501 => rules::pyflakes::rules::PercentFormatInvalidFormat,
|
||||
F502 => rules::pyflakes::rules::PercentFormatExpectedMapping,
|
||||
F503 => rules::pyflakes::rules::PercentFormatExpectedSequence,
|
||||
F504 => rules::pyflakes::rules::PercentFormatExtraNamedArguments,
|
||||
F505 => rules::pyflakes::rules::PercentFormatMissingArgument,
|
||||
F506 => rules::pyflakes::rules::PercentFormatMixedPositionalAndNamed,
|
||||
F507 => rules::pyflakes::rules::PercentFormatPositionalCountMismatch,
|
||||
F508 => rules::pyflakes::rules::PercentFormatStarRequiresSequence,
|
||||
F509 => rules::pyflakes::rules::PercentFormatUnsupportedFormatCharacter,
|
||||
F521 => rules::pyflakes::rules::StringDotFormatInvalidFormat,
|
||||
F522 => rules::pyflakes::rules::StringDotFormatExtraNamedArguments,
|
||||
F523 => rules::pyflakes::rules::StringDotFormatExtraPositionalArguments,
|
||||
F524 => rules::pyflakes::rules::StringDotFormatMissingArguments,
|
||||
F525 => rules::pyflakes::rules::StringDotFormatMixingAutomatic,
|
||||
F541 => rules::pyflakes::rules::FStringMissingPlaceholders,
|
||||
F601 => rules::pyflakes::rules::MultiValueRepeatedKeyLiteral,
|
||||
F602 => rules::pyflakes::rules::MultiValueRepeatedKeyVariable,
|
||||
F621 => rules::pyflakes::rules::ExpressionsInStarAssignment,
|
||||
F622 => rules::pyflakes::rules::TwoStarredExpressions,
|
||||
F631 => rules::pyflakes::rules::AssertTuple,
|
||||
F632 => rules::pyflakes::rules::IsLiteral,
|
||||
F633 => rules::pyflakes::rules::InvalidPrintSyntax,
|
||||
F634 => rules::pyflakes::rules::IfTuple,
|
||||
F701 => rules::pyflakes::rules::BreakOutsideLoop,
|
||||
F702 => rules::pyflakes::rules::ContinueOutsideLoop,
|
||||
F704 => rules::pyflakes::rules::YieldOutsideFunction,
|
||||
F706 => rules::pyflakes::rules::ReturnOutsideFunction,
|
||||
F707 => rules::pyflakes::rules::DefaultExceptNotLast,
|
||||
F722 => rules::pyflakes::rules::ForwardAnnotationSyntaxError,
|
||||
F811 => rules::pyflakes::rules::RedefinedWhileUnused,
|
||||
F821 => rules::pyflakes::rules::UndefinedName,
|
||||
F822 => rules::pyflakes::rules::UndefinedExport,
|
||||
F823 => rules::pyflakes::rules::UndefinedLocal,
|
||||
F841 => rules::pyflakes::rules::UnusedVariable,
|
||||
F842 => rules::pyflakes::rules::UnusedAnnotation,
|
||||
F901 => rules::pyflakes::rules::RaiseNotImplemented,
|
||||
// pylint
|
||||
PLE0604 => rules::pylint::rules::InvalidAllObject,
|
||||
PLE0605 => rules::pylint::rules::InvalidAllFormat,
|
||||
|
|
|
@ -1,18 +1,29 @@
|
|||
use crate::define_violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct AssertTuple;
|
||||
);
|
||||
impl Violation for AssertTuple {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Assert test is a non-empty tuple, which is always `True`")
|
||||
}
|
||||
}
|
||||
|
||||
/// F631
|
||||
pub fn assert_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||
if let ExprKind::Tuple { elts, .. } = &test.node {
|
||||
if !elts.is_empty() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::AssertTuple,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(AssertTuple, Range::from_located(stmt)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
51
src/rules/pyflakes/rules/break_outside_loop.rs
Normal file
51
src/rules/pyflakes/rules/break_outside_loop.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use crate::ast::types::Range;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct BreakOutsideLoop;
|
||||
);
|
||||
impl Violation for BreakOutsideLoop {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`break` outside loop")
|
||||
}
|
||||
}
|
||||
|
||||
/// F701
|
||||
pub fn break_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Diagnostic> {
|
||||
let mut allowed: bool = false;
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
| StmtKind::While { orelse, .. } => {
|
||||
if !orelse.contains(child) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
None
|
||||
} else {
|
||||
Some(Diagnostic::new(BreakOutsideLoop, Range::from_located(stmt)))
|
||||
}
|
||||
}
|
54
src/rules/pyflakes/rules/continue_outside_loop.rs
Normal file
54
src/rules/pyflakes/rules/continue_outside_loop.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::ast::types::Range;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct ContinueOutsideLoop;
|
||||
);
|
||||
impl Violation for ContinueOutsideLoop {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`continue` not properly in loop")
|
||||
}
|
||||
}
|
||||
|
||||
/// F702
|
||||
pub fn continue_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Diagnostic> {
|
||||
let mut allowed: bool = false;
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
| StmtKind::While { orelse, .. } => {
|
||||
if !orelse.contains(child) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
None
|
||||
} else {
|
||||
Some(Diagnostic::new(
|
||||
ContinueOutsideLoop,
|
||||
Range::from_located(stmt),
|
||||
))
|
||||
}
|
||||
}
|
36
src/rules/pyflakes/rules/default_except_not_last.rs
Normal file
36
src/rules/pyflakes/rules/default_except_not_last.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use crate::ast::helpers::except_range;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::Locator;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct DefaultExceptNotLast;
|
||||
);
|
||||
impl Violation for DefaultExceptNotLast {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("An `except` block as not the last exception handler")
|
||||
}
|
||||
}
|
||||
|
||||
/// F707
|
||||
pub fn default_except_not_last(
|
||||
handlers: &[Excepthandler],
|
||||
locator: &Locator,
|
||||
) -> Option<Diagnostic> {
|
||||
for (idx, handler) in handlers.iter().enumerate() {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
|
||||
if type_.is_none() && idx < handlers.len() - 1 {
|
||||
return Some(Diagnostic::new(
|
||||
DefaultExceptNotLast,
|
||||
except_range(handler, locator),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
|
@ -1,10 +1,26 @@
|
|||
use crate::define_violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::helpers::find_useless_f_strings;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
|
||||
define_violation!(
|
||||
pub struct FStringMissingPlaceholders;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for FStringMissingPlaceholders {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("f-string without any placeholders")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove extraneous `f` prefix".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// F541
|
||||
pub fn f_string_missing_placeholders(expr: &Expr, values: &[Expr], checker: &mut Checker) {
|
||||
|
@ -13,7 +29,7 @@ pub fn f_string_missing_placeholders(expr: &Expr, values: &[Expr], checker: &mut
|
|||
.any(|value| matches!(value.node, ExprKind::FormattedValue { .. }))
|
||||
{
|
||||
for (prefix_range, tok_range) in find_useless_f_strings(expr, checker.locator) {
|
||||
let mut diagnostic = Diagnostic::new(violations::FStringMissingPlaceholders, tok_range);
|
||||
let mut diagnostic = Diagnostic::new(FStringMissingPlaceholders, tok_range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
prefix_range.location,
|
||||
|
|
16
src/rules/pyflakes/rules/forward_annotation_syntax_error.rs
Normal file
16
src/rules/pyflakes/rules/forward_annotation_syntax_error.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::define_violation;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
define_violation!(
|
||||
pub struct ForwardAnnotationSyntaxError {
|
||||
pub body: String,
|
||||
}
|
||||
);
|
||||
impl Violation for ForwardAnnotationSyntaxError {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ForwardAnnotationSyntaxError { body } = self;
|
||||
format!("Syntax error in forward annotation: `{body}`")
|
||||
}
|
||||
}
|
|
@ -1,18 +1,29 @@
|
|||
use crate::define_violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct IfTuple;
|
||||
);
|
||||
impl Violation for IfTuple {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("If test is a tuple, which is always `True`")
|
||||
}
|
||||
}
|
||||
|
||||
/// F634
|
||||
pub fn if_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||
if let ExprKind::Tuple { elts, .. } = &test.node {
|
||||
if !elts.is_empty() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::IfTuple,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(IfTuple, Range::from_located(stmt)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
143
src/rules/pyflakes/rules/imports.rs
Normal file
143
src/rules/pyflakes/rules/imports.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::python::future::ALL_FEATURE_NAMES;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::{Availability, Violation};
|
||||
use crate::{define_violation, AutofixKind};
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::Alias;
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedImport {
|
||||
pub name: String,
|
||||
pub ignore_init: bool,
|
||||
pub multiple: bool,
|
||||
}
|
||||
);
|
||||
fn fmt_unused_import_autofix_msg(unused_import: &UnusedImport) -> String {
|
||||
let UnusedImport { name, multiple, .. } = unused_import;
|
||||
if *multiple {
|
||||
"Remove unused import".to_string()
|
||||
} else {
|
||||
format!("Remove unused import: `{name}`")
|
||||
}
|
||||
}
|
||||
impl Violation for UnusedImport {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedImport {
|
||||
name, ignore_init, ..
|
||||
} = self;
|
||||
if *ignore_init {
|
||||
format!(
|
||||
"`{name}` imported but unused; consider adding to `__all__` or using a redundant \
|
||||
alias"
|
||||
)
|
||||
} else {
|
||||
format!("`{name}` imported but unused")
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let UnusedImport { ignore_init, .. } = self;
|
||||
if *ignore_init {
|
||||
None
|
||||
} else {
|
||||
Some(fmt_unused_import_autofix_msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
define_violation!(
|
||||
pub struct ImportShadowedByLoopVar {
|
||||
pub name: String,
|
||||
pub line: usize,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportShadowedByLoopVar {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportShadowedByLoopVar { name, line } = self;
|
||||
format!("Import `{name}` from line {line} shadowed by loop variable")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportStarUsed {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportStarUsed {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportStarUsed { name } = self;
|
||||
format!("`from {name} import *` used; unable to detect undefined names")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LateFutureImport;
|
||||
);
|
||||
impl Violation for LateFutureImport {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`from __future__` imports must occur at the beginning of the file")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportStarUsage {
|
||||
pub name: String,
|
||||
pub sources: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportStarUsage {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportStarUsage { name, sources } = self;
|
||||
let sources = sources
|
||||
.iter()
|
||||
.map(|source| format!("`{source}`"))
|
||||
.join(", ");
|
||||
format!("`{name}` may be undefined, or defined from star imports: {sources}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportStarNotPermitted {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportStarNotPermitted {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportStarNotPermitted { name } = self;
|
||||
format!("`from {name} import *` only allowed at module level")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FutureFeatureNotDefined {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FutureFeatureNotDefined {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FutureFeatureNotDefined { name } = self;
|
||||
format!("Future feature `{name}` is not defined")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn future_feature_not_defined(checker: &mut Checker, alias: &Alias) {
|
||||
if !ALL_FEATURE_NAMES.contains(&&*alias.node.name) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
FutureFeatureNotDefined {
|
||||
name: alias.node.name.to_string(),
|
||||
},
|
||||
Range::from_located(alias),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,14 +1,56 @@
|
|||
use itertools::izip;
|
||||
use once_cell::unsync::Lazy;
|
||||
use rustpython_ast::{Cmpop, Expr};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::operations::locate_cmpops;
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
use itertools::izip;
|
||||
use once_cell::unsync::Lazy;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Cmpop, Expr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum IsCmpop {
|
||||
Is,
|
||||
IsNot,
|
||||
}
|
||||
|
||||
impl From<&Cmpop> for IsCmpop {
|
||||
fn from(cmpop: &Cmpop) -> Self {
|
||||
match cmpop {
|
||||
Cmpop::Is => IsCmpop::Is,
|
||||
Cmpop::IsNot => IsCmpop::IsNot,
|
||||
_ => unreachable!("Expected Cmpop::Is | Cmpop::IsNot"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct IsLiteral {
|
||||
pub cmpop: IsCmpop,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for IsLiteral {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IsLiteral { cmpop } = self;
|
||||
match cmpop {
|
||||
IsCmpop::Is => format!("Use `==` to compare constant literals"),
|
||||
IsCmpop::IsNot => format!("Use `!=` to compare constant literals"),
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let IsLiteral { cmpop } = self;
|
||||
match cmpop {
|
||||
IsCmpop::Is => "Replace `is` with `==`".to_string(),
|
||||
IsCmpop::IsNot => "Replace `is not` with `!=`".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// F632
|
||||
pub fn invalid_literal_comparison(
|
||||
|
@ -25,8 +67,7 @@ pub fn invalid_literal_comparison(
|
|||
&& (helpers::is_constant_non_singleton(left)
|
||||
|| helpers::is_constant_non_singleton(right))
|
||||
{
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::IsLiteral { cmpop: op.into() }, location);
|
||||
let mut diagnostic = Diagnostic::new(IsLiteral { cmpop: op.into() }, location);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(located_op) = &located.get(index) {
|
||||
assert_eq!(&located_op.node, op);
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
use crate::define_violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct InvalidPrintSyntax;
|
||||
);
|
||||
impl Violation for InvalidPrintSyntax {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Use of `>>` is invalid with `print` function")
|
||||
}
|
||||
}
|
||||
|
||||
/// F633
|
||||
pub fn invalid_print_syntax(checker: &mut Checker, left: &Expr) {
|
||||
|
@ -17,7 +29,7 @@ pub fn invalid_print_syntax(checker: &mut Checker, left: &Expr) {
|
|||
return;
|
||||
};
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::InvalidPrintSyntax,
|
||||
InvalidPrintSyntax,
|
||||
Range::from_located(left),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1,187 +1,67 @@
|
|||
pub use assert_tuple::assert_tuple;
|
||||
pub use f_string_missing_placeholders::f_string_missing_placeholders;
|
||||
pub use if_tuple::if_tuple;
|
||||
pub use invalid_literal_comparisons::invalid_literal_comparison;
|
||||
pub use invalid_print_syntax::invalid_print_syntax;
|
||||
pub use raise_not_implemented::raise_not_implemented;
|
||||
pub use repeated_keys::repeated_keys;
|
||||
pub use assert_tuple::{assert_tuple, AssertTuple};
|
||||
pub use break_outside_loop::{break_outside_loop, BreakOutsideLoop};
|
||||
pub use continue_outside_loop::{continue_outside_loop, ContinueOutsideLoop};
|
||||
pub use default_except_not_last::{default_except_not_last, DefaultExceptNotLast};
|
||||
pub use f_string_missing_placeholders::{
|
||||
f_string_missing_placeholders, FStringMissingPlaceholders,
|
||||
};
|
||||
pub use forward_annotation_syntax_error::ForwardAnnotationSyntaxError;
|
||||
pub use if_tuple::{if_tuple, IfTuple};
|
||||
pub use imports::{
|
||||
future_feature_not_defined, FutureFeatureNotDefined, ImportShadowedByLoopVar,
|
||||
ImportStarNotPermitted, ImportStarUsage, ImportStarUsed, LateFutureImport, UnusedImport,
|
||||
};
|
||||
pub use invalid_literal_comparisons::{invalid_literal_comparison, IsLiteral};
|
||||
pub use invalid_print_syntax::{invalid_print_syntax, InvalidPrintSyntax};
|
||||
pub use raise_not_implemented::{raise_not_implemented, RaiseNotImplemented};
|
||||
pub use redefined_while_unused::RedefinedWhileUnused;
|
||||
pub use repeated_keys::{
|
||||
repeated_keys, MultiValueRepeatedKeyLiteral, MultiValueRepeatedKeyVariable,
|
||||
};
|
||||
pub use return_outside_function::{return_outside_function, ReturnOutsideFunction};
|
||||
pub use starred_expressions::{
|
||||
starred_expressions, ExpressionsInStarAssignment, TwoStarredExpressions,
|
||||
};
|
||||
pub(crate) use strings::{
|
||||
percent_format_expected_mapping, percent_format_expected_sequence,
|
||||
percent_format_extra_named_arguments, percent_format_missing_arguments,
|
||||
percent_format_mixed_positional_and_named, percent_format_positional_count_mismatch,
|
||||
percent_format_star_requires_sequence, string_dot_format_extra_named_arguments,
|
||||
string_dot_format_extra_positional_arguments, string_dot_format_missing_argument,
|
||||
string_dot_format_mixing_automatic,
|
||||
string_dot_format_mixing_automatic, PercentFormatExpectedMapping,
|
||||
PercentFormatExpectedSequence, PercentFormatExtraNamedArguments, PercentFormatInvalidFormat,
|
||||
PercentFormatMissingArgument, PercentFormatMixedPositionalAndNamed,
|
||||
PercentFormatPositionalCountMismatch, PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter, StringDotFormatExtraNamedArguments,
|
||||
StringDotFormatExtraPositionalArguments, StringDotFormatInvalidFormat,
|
||||
StringDotFormatMissingArguments, StringDotFormatMixingAutomatic,
|
||||
};
|
||||
pub use unused_annotation::unused_annotation;
|
||||
pub use unused_variable::unused_variable;
|
||||
pub use undefined_export::UndefinedExport;
|
||||
pub use undefined_local::{undefined_local, UndefinedLocal};
|
||||
pub use undefined_name::UndefinedName;
|
||||
pub use unused_annotation::{unused_annotation, UnusedAnnotation};
|
||||
pub use unused_variable::{unused_variable, UnusedVariable};
|
||||
pub use yield_outside_function::{yield_outside_function, YieldOutsideFunction};
|
||||
|
||||
mod assert_tuple;
|
||||
mod break_outside_loop;
|
||||
mod continue_outside_loop;
|
||||
mod default_except_not_last;
|
||||
mod f_string_missing_placeholders;
|
||||
mod forward_annotation_syntax_error;
|
||||
mod if_tuple;
|
||||
mod imports;
|
||||
mod invalid_literal_comparisons;
|
||||
mod invalid_print_syntax;
|
||||
mod raise_not_implemented;
|
||||
mod redefined_while_unused;
|
||||
mod repeated_keys;
|
||||
mod return_outside_function;
|
||||
mod starred_expressions;
|
||||
mod strings;
|
||||
mod undefined_export;
|
||||
mod undefined_local;
|
||||
mod undefined_name;
|
||||
mod unused_annotation;
|
||||
mod unused_variable;
|
||||
|
||||
use std::string::ToString;
|
||||
|
||||
use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::except_range;
|
||||
use crate::ast::types::{Binding, Range, Scope, ScopeKind};
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::Locator;
|
||||
use crate::violations;
|
||||
|
||||
/// F821
|
||||
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &[Binding]) -> Option<Diagnostic> {
|
||||
let current = &scopes.last().expect("No current scope found");
|
||||
if matches!(current.kind, ScopeKind::Function(_)) && !current.values.contains_key(name) {
|
||||
for scope in scopes.iter().rev().skip(1) {
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if let Some(binding) = scope.values.get(name).map(|index| &bindings[*index]) {
|
||||
if let Some((scope_id, location)) = binding.runtime_usage {
|
||||
if scope_id == current.id {
|
||||
return Some(Diagnostic::new(
|
||||
violations::UndefinedLocal {
|
||||
name: name.to_string(),
|
||||
},
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// F707
|
||||
pub fn default_except_not_last(
|
||||
handlers: &[Excepthandler],
|
||||
locator: &Locator,
|
||||
) -> Option<Diagnostic> {
|
||||
for (idx, handler) in handlers.iter().enumerate() {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
|
||||
if type_.is_none() && idx < handlers.len() - 1 {
|
||||
return Some(Diagnostic::new(
|
||||
violations::DefaultExceptNotLast,
|
||||
except_range(handler, locator),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// F621, F622
|
||||
pub fn starred_expressions(
|
||||
elts: &[Expr],
|
||||
check_too_many_expressions: bool,
|
||||
check_two_starred_expressions: bool,
|
||||
location: Range,
|
||||
) -> Option<Diagnostic> {
|
||||
let mut has_starred: bool = false;
|
||||
let mut starred_index: Option<usize> = None;
|
||||
for (index, elt) in elts.iter().enumerate() {
|
||||
if matches!(elt.node, ExprKind::Starred { .. }) {
|
||||
if has_starred && check_two_starred_expressions {
|
||||
return Some(Diagnostic::new(violations::TwoStarredExpressions, location));
|
||||
}
|
||||
has_starred = true;
|
||||
starred_index = Some(index);
|
||||
}
|
||||
}
|
||||
|
||||
if check_too_many_expressions {
|
||||
if let Some(starred_index) = starred_index {
|
||||
if starred_index >= 1 << 8 || elts.len() - starred_index > 1 << 24 {
|
||||
return Some(Diagnostic::new(
|
||||
violations::ExpressionsInStarAssignment,
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// F701
|
||||
pub fn break_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Diagnostic> {
|
||||
let mut allowed: bool = false;
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
| StmtKind::While { orelse, .. } => {
|
||||
if !orelse.contains(child) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
None
|
||||
} else {
|
||||
Some(Diagnostic::new(
|
||||
violations::BreakOutsideLoop,
|
||||
Range::from_located(stmt),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// F702
|
||||
pub fn continue_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Diagnostic> {
|
||||
let mut allowed: bool = false;
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
| StmtKind::While { orelse, .. } => {
|
||||
if !orelse.contains(child) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
None
|
||||
} else {
|
||||
Some(Diagnostic::new(
|
||||
violations::ContinueOutsideLoop,
|
||||
Range::from_located(stmt),
|
||||
))
|
||||
}
|
||||
}
|
||||
mod yield_outside_function;
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
use crate::define_violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
|
||||
define_violation!(
|
||||
pub struct RaiseNotImplemented;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for RaiseNotImplemented {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`raise NotImplemented` should be `raise NotImplementedError`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Use `raise NotImplementedError`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn match_not_implemented(expr: &Expr) -> Option<&Expr> {
|
||||
match &expr.node {
|
||||
|
@ -30,8 +46,7 @@ pub fn raise_not_implemented(checker: &mut Checker, expr: &Expr) {
|
|||
let Some(expr) = match_not_implemented(expr) else {
|
||||
return;
|
||||
};
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::RaiseNotImplemented, Range::from_located(expr));
|
||||
let mut diagnostic = Diagnostic::new(RaiseNotImplemented, Range::from_located(expr));
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
"NotImplementedError".to_string(),
|
||||
|
|
18
src/rules/pyflakes/rules/redefined_while_unused.rs
Normal file
18
src/rules/pyflakes/rules/redefined_while_unused.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use crate::define_violation;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
define_violation!(
|
||||
pub struct RedefinedWhileUnused {
|
||||
pub name: String,
|
||||
pub line: usize,
|
||||
}
|
||||
);
|
||||
impl Violation for RedefinedWhileUnused {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let RedefinedWhileUnused { name, line } = self;
|
||||
format!("Redefinition of unused `{name}` from line {line}")
|
||||
}
|
||||
}
|
|
@ -9,7 +9,63 @@ use crate::ast::types::Range;
|
|||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
use crate::{define_violation, AutofixKind};
|
||||
|
||||
use crate::violation::{Availability, Violation};
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
define_violation!(
|
||||
pub struct MultiValueRepeatedKeyLiteral {
|
||||
pub name: String,
|
||||
pub repeated_value: bool,
|
||||
}
|
||||
);
|
||||
impl Violation for MultiValueRepeatedKeyLiteral {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MultiValueRepeatedKeyLiteral { name, .. } = self;
|
||||
format!("Dictionary key literal `{name}` repeated")
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let MultiValueRepeatedKeyLiteral { repeated_value, .. } = self;
|
||||
if *repeated_value {
|
||||
Some(|MultiValueRepeatedKeyLiteral { name, .. }| {
|
||||
format!("Remove repeated key literal `{name}`")
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
define_violation!(
|
||||
pub struct MultiValueRepeatedKeyVariable {
|
||||
pub name: String,
|
||||
pub repeated_value: bool,
|
||||
}
|
||||
);
|
||||
impl Violation for MultiValueRepeatedKeyVariable {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MultiValueRepeatedKeyVariable { name, .. } = self;
|
||||
format!("Dictionary key `{name}` repeated")
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let MultiValueRepeatedKeyVariable { repeated_value, .. } = self;
|
||||
if *repeated_value {
|
||||
Some(|MultiValueRepeatedKeyVariable { name, .. }| {
|
||||
format!("Remove repeated key `{name}`")
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
enum DictionaryKey<'a> {
|
||||
|
@ -48,7 +104,7 @@ pub fn repeated_keys(checker: &mut Checker, keys: &[Option<Expr>], values: &[Exp
|
|||
let comparable_value: ComparableExpr = (&values[i]).into();
|
||||
let is_duplicate_value = seen_values.contains(&comparable_value);
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::MultiValueRepeatedKeyLiteral {
|
||||
MultiValueRepeatedKeyLiteral {
|
||||
name: unparse_expr(key, checker.stylist),
|
||||
repeated_value: is_duplicate_value,
|
||||
},
|
||||
|
@ -76,7 +132,7 @@ pub fn repeated_keys(checker: &mut Checker, keys: &[Option<Expr>], values: &[Exp
|
|||
let comparable_value: ComparableExpr = (&values[i]).into();
|
||||
let is_duplicate_value = seen_values.contains(&comparable_value);
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::MultiValueRepeatedKeyVariable {
|
||||
MultiValueRepeatedKeyVariable {
|
||||
name: dict_key.to_string(),
|
||||
repeated_value: is_duplicate_value,
|
||||
},
|
||||
|
|
31
src/rules/pyflakes/rules/return_outside_function.rs
Normal file
31
src/rules/pyflakes/rules/return_outside_function.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use crate::ast::types::{Range, ScopeKind};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
define_violation!(
|
||||
pub struct ReturnOutsideFunction;
|
||||
);
|
||||
impl Violation for ReturnOutsideFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`return` statement outside of a function/method")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_outside_function(checker: &mut Checker, stmt: &Stmt) {
|
||||
if let Some(&index) = checker.scope_stack.last() {
|
||||
if matches!(
|
||||
checker.scopes[index].kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
ReturnOutsideFunction,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
56
src/rules/pyflakes/rules/starred_expressions.rs
Normal file
56
src/rules/pyflakes/rules/starred_expressions.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::ast::types::Range;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_parser::ast::{Expr, ExprKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct ExpressionsInStarAssignment;
|
||||
);
|
||||
impl Violation for ExpressionsInStarAssignment {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Too many expressions in star-unpacking assignment")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct TwoStarredExpressions;
|
||||
);
|
||||
impl Violation for TwoStarredExpressions {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Two starred expressions in assignment")
|
||||
}
|
||||
}
|
||||
|
||||
/// F621, F622
|
||||
pub fn starred_expressions(
|
||||
elts: &[Expr],
|
||||
check_too_many_expressions: bool,
|
||||
check_two_starred_expressions: bool,
|
||||
location: Range,
|
||||
) -> Option<Diagnostic> {
|
||||
let mut has_starred: bool = false;
|
||||
let mut starred_index: Option<usize> = None;
|
||||
for (index, elt) in elts.iter().enumerate() {
|
||||
if matches!(elt.node, ExprKind::Starred { .. }) {
|
||||
if has_starred && check_two_starred_expressions {
|
||||
return Some(Diagnostic::new(TwoStarredExpressions, location));
|
||||
}
|
||||
has_starred = true;
|
||||
starred_index = Some(index);
|
||||
}
|
||||
}
|
||||
|
||||
if check_too_many_expressions {
|
||||
if let Some(starred_index) = starred_index {
|
||||
if starred_index >= 1 << 8 || elts.len() - starred_index > 1 << 24 {
|
||||
return Some(Diagnostic::new(ExpressionsInStarAssignment, location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use crate::define_violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use std::string::ToString;
|
||||
|
||||
use log::error;
|
||||
|
@ -13,7 +15,192 @@ use super::super::format::FormatSummary;
|
|||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::{AlwaysAutofixableViolation, Violation};
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatInvalidFormat {
|
||||
pub message: String,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatInvalidFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatInvalidFormat { message } = self;
|
||||
format!("`%`-format string has invalid format string: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatExpectedMapping;
|
||||
);
|
||||
impl Violation for PercentFormatExpectedMapping {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string expected mapping but got sequence")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatExpectedSequence;
|
||||
);
|
||||
impl Violation for PercentFormatExpectedSequence {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string expected sequence but got mapping")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatExtraNamedArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for PercentFormatExtraNamedArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`%`-format string has unused named argument(s): {message}")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let PercentFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("Remove extra named arguments: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatMissingArgument {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatMissingArgument {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatMissingArgument { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`%`-format string is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatMixedPositionalAndNamed;
|
||||
);
|
||||
impl Violation for PercentFormatMixedPositionalAndNamed {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string has mixed positional and named placeholders")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatPositionalCountMismatch {
|
||||
pub wanted: usize,
|
||||
pub got: usize,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatPositionalCountMismatch {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatPositionalCountMismatch { wanted, got } = self;
|
||||
format!("`%`-format string has {wanted} placeholder(s) but {got} substitution(s)")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatStarRequiresSequence;
|
||||
);
|
||||
impl Violation for PercentFormatStarRequiresSequence {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string `*` specifier requires sequence")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatUnsupportedFormatCharacter {
|
||||
pub char: char,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatUnsupportedFormatCharacter {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatUnsupportedFormatCharacter { char } = self;
|
||||
format!("`%`-format string has unsupported format character '{char}'")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatInvalidFormat {
|
||||
pub message: String,
|
||||
}
|
||||
);
|
||||
impl Violation for StringDotFormatInvalidFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatInvalidFormat { message } = self;
|
||||
format!("`.format` call has invalid format string: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatExtraNamedArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for StringDotFormatExtraNamedArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`.format` call has unused named argument(s): {message}")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let StringDotFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("Remove extra named arguments: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatExtraPositionalArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for StringDotFormatExtraPositionalArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatExtraPositionalArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`.format` call has unused arguments at position(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatMissingArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for StringDotFormatMissingArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatMissingArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`.format` call is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatMixingAutomatic;
|
||||
);
|
||||
impl Violation for StringDotFormatMixingAutomatic {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`.format` string mixes automatic and manual numbering")
|
||||
}
|
||||
}
|
||||
|
||||
fn has_star_star_kwargs(keywords: &[Keyword]) -> bool {
|
||||
keywords.iter().any(|k| {
|
||||
|
@ -42,10 +229,9 @@ pub(crate) fn percent_format_expected_mapping(
|
|||
| ExprKind::Set { .. }
|
||||
| ExprKind::ListComp { .. }
|
||||
| ExprKind::SetComp { .. }
|
||||
| ExprKind::GeneratorExp { .. } => checker.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatExpectedMapping,
|
||||
location,
|
||||
)),
|
||||
| ExprKind::GeneratorExp { .. } => checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(PercentFormatExpectedMapping, location)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -64,10 +250,9 @@ pub(crate) fn percent_format_expected_sequence(
|
|||
ExprKind::Dict { .. } | ExprKind::DictComp { .. }
|
||||
)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatExpectedSequence,
|
||||
location,
|
||||
));
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(PercentFormatExpectedSequence, location));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +299,7 @@ pub(crate) fn percent_format_extra_named_arguments(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::PercentFormatExtraNamedArguments {
|
||||
PercentFormatExtraNamedArguments {
|
||||
missing: missing.iter().map(|&arg| arg.to_string()).collect(),
|
||||
},
|
||||
location,
|
||||
|
@ -174,7 +359,7 @@ pub(crate) fn percent_format_missing_arguments(
|
|||
|
||||
if !missing.is_empty() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatMissingArgument {
|
||||
PercentFormatMissingArgument {
|
||||
missing: missing.iter().map(|&s| s.clone()).collect(),
|
||||
},
|
||||
location,
|
||||
|
@ -191,7 +376,7 @@ pub(crate) fn percent_format_mixed_positional_and_named(
|
|||
) {
|
||||
if !(summary.num_positional == 0 || summary.keywords.is_empty()) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatMixedPositionalAndNamed,
|
||||
PercentFormatMixedPositionalAndNamed,
|
||||
location,
|
||||
));
|
||||
}
|
||||
|
@ -220,7 +405,7 @@ pub(crate) fn percent_format_positional_count_mismatch(
|
|||
|
||||
if found != summary.num_positional {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::PercentFormatPositionalCountMismatch {
|
||||
PercentFormatPositionalCountMismatch {
|
||||
wanted: summary.num_positional,
|
||||
got: found,
|
||||
},
|
||||
|
@ -241,9 +426,9 @@ pub(crate) fn percent_format_star_requires_sequence(
|
|||
) {
|
||||
if summary.starred {
|
||||
match &right.node {
|
||||
ExprKind::Dict { .. } | ExprKind::DictComp { .. } => checker.diagnostics.push(
|
||||
Diagnostic::new(violations::PercentFormatStarRequiresSequence, location),
|
||||
),
|
||||
ExprKind::Dict { .. } | ExprKind::DictComp { .. } => checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(PercentFormatStarRequiresSequence, location)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +465,7 @@ pub(crate) fn string_dot_format_extra_named_arguments(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::StringDotFormatExtraNamedArguments {
|
||||
StringDotFormatExtraNamedArguments {
|
||||
missing: missing.iter().map(|&arg| arg.to_string()).collect(),
|
||||
},
|
||||
location,
|
||||
|
@ -321,7 +506,7 @@ pub(crate) fn string_dot_format_extra_positional_arguments(
|
|||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::StringDotFormatExtraPositionalArguments {
|
||||
StringDotFormatExtraPositionalArguments {
|
||||
missing: missing
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
|
@ -368,7 +553,7 @@ pub(crate) fn string_dot_format_missing_argument(
|
|||
|
||||
if !missing.is_empty() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::StringDotFormatMissingArguments { missing },
|
||||
StringDotFormatMissingArguments { missing },
|
||||
location,
|
||||
));
|
||||
}
|
||||
|
@ -381,9 +566,8 @@ pub(crate) fn string_dot_format_mixing_automatic(
|
|||
location: Range,
|
||||
) {
|
||||
if !(summary.autos.is_empty() || summary.indexes.is_empty()) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::StringDotFormatMixingAutomatic,
|
||||
location,
|
||||
));
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(StringDotFormatMixingAutomatic, location));
|
||||
}
|
||||
}
|
||||
|
|
17
src/rules/pyflakes/rules/undefined_export.rs
Normal file
17
src/rules/pyflakes/rules/undefined_export.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::define_violation;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
define_violation!(
|
||||
pub struct UndefinedExport {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UndefinedExport {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UndefinedExport { name } = self;
|
||||
format!("Undefined name `{name}` in `__all__`")
|
||||
}
|
||||
}
|
46
src/rules/pyflakes/rules/undefined_local.rs
Normal file
46
src/rules/pyflakes/rules/undefined_local.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use crate::ast::types::{Binding, Scope, ScopeKind};
|
||||
use crate::registry::Diagnostic;
|
||||
|
||||
use crate::define_violation;
|
||||
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use std::string::ToString;
|
||||
|
||||
define_violation!(
|
||||
pub struct UndefinedLocal {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UndefinedLocal {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UndefinedLocal { name } = self;
|
||||
format!("Local variable `{name}` referenced before assignment")
|
||||
}
|
||||
}
|
||||
|
||||
/// F821
|
||||
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &[Binding]) -> Option<Diagnostic> {
|
||||
let current = &scopes.last().expect("No current scope found");
|
||||
if matches!(current.kind, ScopeKind::Function(_)) && !current.values.contains_key(name) {
|
||||
for scope in scopes.iter().rev().skip(1) {
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if let Some(binding) = scope.values.get(name).map(|index| &bindings[*index]) {
|
||||
if let Some((scope_id, location)) = binding.runtime_usage {
|
||||
if scope_id == current.id {
|
||||
return Some(Diagnostic::new(
|
||||
UndefinedLocal {
|
||||
name: name.to_string(),
|
||||
},
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
16
src/rules/pyflakes/rules/undefined_name.rs
Normal file
16
src/rules/pyflakes/rules/undefined_name.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::define_violation;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
define_violation!(
|
||||
pub struct UndefinedName {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UndefinedName {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UndefinedName { name } = self;
|
||||
format!("Undefined name `{name}`")
|
||||
}
|
||||
}
|
|
@ -1,7 +1,23 @@
|
|||
use crate::ast::types::BindingKind;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedAnnotation {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UnusedAnnotation {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedAnnotation { name } = self;
|
||||
format!("Local variable `{name}` is annotated but never used")
|
||||
}
|
||||
}
|
||||
|
||||
/// F842
|
||||
pub fn unused_annotation(checker: &mut Checker, scope: usize) {
|
||||
|
@ -16,7 +32,7 @@ pub fn unused_annotation(checker: &mut Checker, scope: usize) {
|
|||
&& !checker.settings.dummy_variable_rgx.is_match(name)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::UnusedAnnotation {
|
||||
UnusedAnnotation {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::define_violation;
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{ExprKind, Location, Stmt, StmtKind};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
@ -11,7 +13,25 @@ use crate::checkers::ast::Checker;
|
|||
use crate::fix::Fix;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::Locator;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedVariable {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UnusedVariable {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedVariable { name } = self;
|
||||
format!("Local variable `{name}` is assigned to but never used")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let UnusedVariable { name } = self;
|
||||
format!("Remove assignment to unused variable `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
fn match_token_after<F>(stmt: &Stmt, locator: &Locator, f: F) -> Location
|
||||
where
|
||||
|
@ -174,7 +194,7 @@ pub fn unused_variable(checker: &mut Checker, scope: usize) {
|
|||
&& name != &"__traceback_supplement__"
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedVariable {
|
||||
UnusedVariable {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range,
|
||||
|
|
57
src/rules/pyflakes/rules/yield_outside_function.rs
Normal file
57
src/rules/pyflakes/rules/yield_outside_function.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::ast::types::{Range, ScopeKind};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DeferralKeyword {
|
||||
Yield,
|
||||
YieldFrom,
|
||||
Await,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeferralKeyword {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DeferralKeyword::Yield => fmt.write_str("yield"),
|
||||
DeferralKeyword::YieldFrom => fmt.write_str("yield from"),
|
||||
DeferralKeyword::Await => fmt.write_str("await"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct YieldOutsideFunction {
|
||||
pub keyword: DeferralKeyword,
|
||||
}
|
||||
);
|
||||
impl Violation for YieldOutsideFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let YieldOutsideFunction { keyword } = self;
|
||||
format!("`{keyword}` statement outside of a function")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_outside_function(checker: &mut Checker, expr: &Expr) {
|
||||
if matches!(
|
||||
checker.current_scope().kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
) {
|
||||
let keyword = match expr.node {
|
||||
ExprKind::Yield { .. } => DeferralKeyword::Yield,
|
||||
ExprKind::YieldFrom { .. } => DeferralKeyword::YieldFrom,
|
||||
ExprKind::Await { .. } => DeferralKeyword::Await,
|
||||
_ => unreachable!("Expected ExprKind::Yield | ExprKind::YieldFrom | ExprKind::Await"),
|
||||
};
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
YieldOutsideFunction { keyword },
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -59,656 +59,6 @@ impl Violation for SyntaxError {
|
|||
}
|
||||
}
|
||||
|
||||
// pyflakes
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedImport {
|
||||
pub name: String,
|
||||
pub ignore_init: bool,
|
||||
pub multiple: bool,
|
||||
}
|
||||
);
|
||||
fn fmt_unused_import_autofix_msg(unused_import: &UnusedImport) -> String {
|
||||
let UnusedImport { name, multiple, .. } = unused_import;
|
||||
if *multiple {
|
||||
"Remove unused import".to_string()
|
||||
} else {
|
||||
format!("Remove unused import: `{name}`")
|
||||
}
|
||||
}
|
||||
impl Violation for UnusedImport {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedImport {
|
||||
name, ignore_init, ..
|
||||
} = self;
|
||||
if *ignore_init {
|
||||
format!(
|
||||
"`{name}` imported but unused; consider adding to `__all__` or using a redundant \
|
||||
alias"
|
||||
)
|
||||
} else {
|
||||
format!("`{name}` imported but unused")
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let UnusedImport { ignore_init, .. } = self;
|
||||
if *ignore_init {
|
||||
None
|
||||
} else {
|
||||
Some(fmt_unused_import_autofix_msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportShadowedByLoopVar {
|
||||
pub name: String,
|
||||
pub line: usize,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportShadowedByLoopVar {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportShadowedByLoopVar { name, line } = self;
|
||||
format!("Import `{name}` from line {line} shadowed by loop variable")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportStarUsed {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportStarUsed {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportStarUsed { name } = self;
|
||||
format!("`from {name} import *` used; unable to detect undefined names")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct LateFutureImport;
|
||||
);
|
||||
impl Violation for LateFutureImport {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`from __future__` imports must occur at the beginning of the file")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportStarUsage {
|
||||
pub name: String,
|
||||
pub sources: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportStarUsage {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportStarUsage { name, sources } = self;
|
||||
let sources = sources
|
||||
.iter()
|
||||
.map(|source| format!("`{source}`"))
|
||||
.join(", ");
|
||||
format!("`{name}` may be undefined, or defined from star imports: {sources}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ImportStarNotPermitted {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for ImportStarNotPermitted {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ImportStarNotPermitted { name } = self;
|
||||
format!("`from {name} import *` only allowed at module level")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FutureFeatureNotDefined {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FutureFeatureNotDefined {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FutureFeatureNotDefined { name } = self;
|
||||
format!("Future feature `{name}` is not defined")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatInvalidFormat {
|
||||
pub message: String,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatInvalidFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatInvalidFormat { message } = self;
|
||||
format!("`%`-format string has invalid format string: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatExpectedMapping;
|
||||
);
|
||||
impl Violation for PercentFormatExpectedMapping {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string expected mapping but got sequence")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatExpectedSequence;
|
||||
);
|
||||
impl Violation for PercentFormatExpectedSequence {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string expected sequence but got mapping")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatExtraNamedArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for PercentFormatExtraNamedArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`%`-format string has unused named argument(s): {message}")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let PercentFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("Remove extra named arguments: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatMissingArgument {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatMissingArgument {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatMissingArgument { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`%`-format string is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatMixedPositionalAndNamed;
|
||||
);
|
||||
impl Violation for PercentFormatMixedPositionalAndNamed {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string has mixed positional and named placeholders")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatPositionalCountMismatch {
|
||||
pub wanted: usize,
|
||||
pub got: usize,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatPositionalCountMismatch {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatPositionalCountMismatch { wanted, got } = self;
|
||||
format!("`%`-format string has {wanted} placeholder(s) but {got} substitution(s)")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatStarRequiresSequence;
|
||||
);
|
||||
impl Violation for PercentFormatStarRequiresSequence {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`%`-format string `*` specifier requires sequence")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct PercentFormatUnsupportedFormatCharacter {
|
||||
pub char: char,
|
||||
}
|
||||
);
|
||||
impl Violation for PercentFormatUnsupportedFormatCharacter {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatUnsupportedFormatCharacter { char } = self;
|
||||
format!("`%`-format string has unsupported format character '{char}'")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatInvalidFormat {
|
||||
pub message: String,
|
||||
}
|
||||
);
|
||||
impl Violation for StringDotFormatInvalidFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatInvalidFormat { message } = self;
|
||||
format!("`.format` call has invalid format string: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatExtraNamedArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for StringDotFormatExtraNamedArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`.format` call has unused named argument(s): {message}")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let StringDotFormatExtraNamedArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("Remove extra named arguments: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatExtraPositionalArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for StringDotFormatExtraPositionalArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatExtraPositionalArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`.format` call has unused arguments at position(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatMissingArguments {
|
||||
pub missing: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for StringDotFormatMissingArguments {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatMissingArguments { missing } = self;
|
||||
let message = missing.join(", ");
|
||||
format!("`.format` call is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct StringDotFormatMixingAutomatic;
|
||||
);
|
||||
impl Violation for StringDotFormatMixingAutomatic {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`.format` string mixes automatic and manual numbering")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FStringMissingPlaceholders;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for FStringMissingPlaceholders {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("f-string without any placeholders")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove extraneous `f` prefix".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct MultiValueRepeatedKeyLiteral {
|
||||
pub name: String,
|
||||
pub repeated_value: bool,
|
||||
}
|
||||
);
|
||||
impl Violation for MultiValueRepeatedKeyLiteral {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MultiValueRepeatedKeyLiteral { name, .. } = self;
|
||||
format!("Dictionary key literal `{name}` repeated")
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let MultiValueRepeatedKeyLiteral { repeated_value, .. } = self;
|
||||
if *repeated_value {
|
||||
Some(|MultiValueRepeatedKeyLiteral { name, .. }| {
|
||||
format!("Remove repeated key literal `{name}`")
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct MultiValueRepeatedKeyVariable {
|
||||
pub name: String,
|
||||
pub repeated_value: bool,
|
||||
}
|
||||
);
|
||||
impl Violation for MultiValueRepeatedKeyVariable {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MultiValueRepeatedKeyVariable { name, .. } = self;
|
||||
format!("Dictionary key `{name}` repeated")
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let MultiValueRepeatedKeyVariable { repeated_value, .. } = self;
|
||||
if *repeated_value {
|
||||
Some(|MultiValueRepeatedKeyVariable { name, .. }| {
|
||||
format!("Remove repeated key `{name}`")
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ExpressionsInStarAssignment;
|
||||
);
|
||||
impl Violation for ExpressionsInStarAssignment {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Too many expressions in star-unpacking assignment")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct TwoStarredExpressions;
|
||||
);
|
||||
impl Violation for TwoStarredExpressions {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Two starred expressions in assignment")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct AssertTuple;
|
||||
);
|
||||
impl Violation for AssertTuple {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Assert test is a non-empty tuple, which is always `True`")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum IsCmpop {
|
||||
Is,
|
||||
IsNot,
|
||||
}
|
||||
|
||||
impl From<&Cmpop> for IsCmpop {
|
||||
fn from(cmpop: &Cmpop) -> Self {
|
||||
match cmpop {
|
||||
Cmpop::Is => IsCmpop::Is,
|
||||
Cmpop::IsNot => IsCmpop::IsNot,
|
||||
_ => unreachable!("Expected Cmpop::Is | Cmpop::IsNot"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct IsLiteral {
|
||||
pub cmpop: IsCmpop,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for IsLiteral {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IsLiteral { cmpop } = self;
|
||||
match cmpop {
|
||||
IsCmpop::Is => format!("Use `==` to compare constant literals"),
|
||||
IsCmpop::IsNot => format!("Use `!=` to compare constant literals"),
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let IsLiteral { cmpop } = self;
|
||||
match cmpop {
|
||||
IsCmpop::Is => "Replace `is` with `==`".to_string(),
|
||||
IsCmpop::IsNot => "Replace `is not` with `!=`".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct InvalidPrintSyntax;
|
||||
);
|
||||
impl Violation for InvalidPrintSyntax {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Use of `>>` is invalid with `print` function")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct IfTuple;
|
||||
);
|
||||
impl Violation for IfTuple {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("If test is a tuple, which is always `True`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct BreakOutsideLoop;
|
||||
);
|
||||
impl Violation for BreakOutsideLoop {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`break` outside loop")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ContinueOutsideLoop;
|
||||
);
|
||||
impl Violation for ContinueOutsideLoop {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`continue` not properly in loop")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DeferralKeyword {
|
||||
Yield,
|
||||
YieldFrom,
|
||||
Await,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeferralKeyword {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DeferralKeyword::Yield => fmt.write_str("yield"),
|
||||
DeferralKeyword::YieldFrom => fmt.write_str("yield from"),
|
||||
DeferralKeyword::Await => fmt.write_str("await"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct YieldOutsideFunction {
|
||||
pub keyword: DeferralKeyword,
|
||||
}
|
||||
);
|
||||
impl Violation for YieldOutsideFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let YieldOutsideFunction { keyword } = self;
|
||||
format!("`{keyword}` statement outside of a function")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ReturnOutsideFunction;
|
||||
);
|
||||
impl Violation for ReturnOutsideFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`return` statement outside of a function/method")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct DefaultExceptNotLast;
|
||||
);
|
||||
impl Violation for DefaultExceptNotLast {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("An `except` block as not the last exception handler")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ForwardAnnotationSyntaxError {
|
||||
pub body: String,
|
||||
}
|
||||
);
|
||||
impl Violation for ForwardAnnotationSyntaxError {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let ForwardAnnotationSyntaxError { body } = self;
|
||||
format!("Syntax error in forward annotation: `{body}`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct RedefinedWhileUnused {
|
||||
pub name: String,
|
||||
pub line: usize,
|
||||
}
|
||||
);
|
||||
impl Violation for RedefinedWhileUnused {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let RedefinedWhileUnused { name, line } = self;
|
||||
format!("Redefinition of unused `{name}` from line {line}")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UndefinedName {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UndefinedName {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UndefinedName { name } = self;
|
||||
format!("Undefined name `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UndefinedExport {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UndefinedExport {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UndefinedExport { name } = self;
|
||||
format!("Undefined name `{name}` in `__all__`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UndefinedLocal {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UndefinedLocal {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UndefinedLocal { name } = self;
|
||||
format!("Local variable `{name}` referenced before assignment")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedVariable {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UnusedVariable {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedVariable { name } = self;
|
||||
format!("Local variable `{name}` is assigned to but never used")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let UnusedVariable { name } = self;
|
||||
format!("Remove assignment to unused variable `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedAnnotation {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for UnusedAnnotation {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedAnnotation { name } = self;
|
||||
format!("Local variable `{name}` is annotated but never used")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct RaiseNotImplemented;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for RaiseNotImplemented {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`raise NotImplemented` should be `raise NotImplementedError`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Use `raise NotImplementedError`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// pylint
|
||||
|
||||
define_violation!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue