Move pyflakes violations to rule modules (#2488)

This commit is contained in:
Aarni Koskela 2023-02-02 21:00:59 +02:00 committed by GitHub
parent e89b4a5de5
commit 40cb905ae5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1097 additions and 987 deletions

View file

@ -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,

View file

@ -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)),

View file

@ -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,

View file

@ -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)));
}
}
}

View 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)))
}
}

View 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),
))
}
}

View 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
}

View file

@ -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,

View 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}`")
}
}

View file

@ -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)));
}
}
}

View 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),
));
}
}

View file

@ -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);

View file

@ -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),
));
}

View file

@ -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;

View file

@ -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(),

View 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}")
}
}

View file

@ -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,
},

View 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),
));
}
}
}

View 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
}

View file

@ -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));
}
}

View 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__`")
}
}

View 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
}

View 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}`")
}
}

View file

@ -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,

View file

@ -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,

View 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),
));
}
}

View file

@ -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!(