mirror of https://github.com/astral-sh/ruff
Move pyflakes violations to rule modules (#2488)
This commit is contained in:
parent
e89b4a5de5
commit
40cb905ae5
|
|
@ -28,7 +28,6 @@ use crate::ast::{branch_detection, cast, helpers, operations, visitor};
|
||||||
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable};
|
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable};
|
||||||
use crate::noqa::Directive;
|
use crate::noqa::Directive;
|
||||||
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||||
use crate::python::future::ALL_FEATURE_NAMES;
|
|
||||||
use crate::python::typing;
|
use crate::python::typing;
|
||||||
use crate::python::typing::{Callable, SubscriptKind};
|
use crate::python::typing::{Callable, SubscriptKind};
|
||||||
use crate::registry::{Diagnostic, Rule};
|
use crate::registry::{Diagnostic, Rule};
|
||||||
|
|
@ -44,7 +43,6 @@ use crate::rules::{
|
||||||
use crate::settings::types::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
use crate::settings::{flags, Settings};
|
use crate::settings::{flags, Settings};
|
||||||
use crate::source_code::{Indexer, Locator, Stylist};
|
use crate::source_code::{Indexer, Locator, Stylist};
|
||||||
use crate::violations::DeferralKeyword;
|
|
||||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||||
use crate::{autofix, docstrings, noqa, violations, visibility};
|
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)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Checker<'a> {
|
pub struct Checker<'a> {
|
||||||
// Input data.
|
// Input data.
|
||||||
path: &'a Path,
|
pub(crate) path: &'a Path,
|
||||||
package: Option<&'a Path>,
|
package: Option<&'a Path>,
|
||||||
autofix: flags::Autofix,
|
autofix: flags::Autofix,
|
||||||
noqa: flags::Noqa,
|
noqa: flags::Noqa,
|
||||||
|
|
@ -720,17 +718,7 @@ where
|
||||||
}
|
}
|
||||||
StmtKind::Return { .. } => {
|
StmtKind::Return { .. } => {
|
||||||
if self.settings.rules.enabled(&Rule::ReturnOutsideFunction) {
|
if self.settings.rules.enabled(&Rule::ReturnOutsideFunction) {
|
||||||
if let Some(&index) = self.scope_stack.last() {
|
pyflakes::rules::return_outside_function(self, stmt);
|
||||||
if matches!(
|
|
||||||
self.scopes[index].kind,
|
|
||||||
ScopeKind::Class(_) | ScopeKind::Module
|
|
||||||
) {
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
|
||||||
violations::ReturnOutsideFunction,
|
|
||||||
Range::from_located(stmt),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::ClassDef {
|
StmtKind::ClassDef {
|
||||||
|
|
@ -1170,21 +1158,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.settings.rules.enabled(&Rule::FutureFeatureNotDefined) {
|
if self.settings.rules.enabled(&Rule::FutureFeatureNotDefined) {
|
||||||
if !ALL_FEATURE_NAMES.contains(&&*alias.node.name) {
|
pyflakes::rules::future_feature_not_defined(self, alias);
|
||||||
self.diagnostics.push(Diagnostic::new(
|
|
||||||
violations::FutureFeatureNotDefined {
|
|
||||||
name: alias.node.name.to_string(),
|
|
||||||
},
|
|
||||||
Range::from_located(alias),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.settings.rules.enabled(&Rule::LateFutureImport)
|
if self.settings.rules.enabled(&Rule::LateFutureImport)
|
||||||
&& !self.futures_allowed
|
&& !self.futures_allowed
|
||||||
{
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::LateFutureImport,
|
pyflakes::rules::LateFutureImport,
|
||||||
Range::from_located(stmt),
|
Range::from_located(stmt),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -1207,7 +1188,7 @@ where
|
||||||
[*(self.scope_stack.last().expect("No current scope found"))];
|
[*(self.scope_stack.last().expect("No current scope found"))];
|
||||||
if !matches!(scope.kind, ScopeKind::Module) {
|
if !matches!(scope.kind, ScopeKind::Module) {
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::ImportStarNotPermitted {
|
pyflakes::rules::ImportStarNotPermitted {
|
||||||
name: helpers::format_import_from(
|
name: helpers::format_import_from(
|
||||||
level.as_ref(),
|
level.as_ref(),
|
||||||
module.as_deref(),
|
module.as_deref(),
|
||||||
|
|
@ -1220,7 +1201,7 @@ where
|
||||||
|
|
||||||
if self.settings.rules.enabled(&Rule::ImportStarUsed) {
|
if self.settings.rules.enabled(&Rule::ImportStarUsed) {
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::ImportStarUsed {
|
pyflakes::rules::ImportStarUsed {
|
||||||
name: helpers::format_import_from(
|
name: helpers::format_import_from(
|
||||||
level.as_ref(),
|
level.as_ref(),
|
||||||
module.as_deref(),
|
module.as_deref(),
|
||||||
|
|
@ -2197,7 +2178,7 @@ where
|
||||||
.enabled(&Rule::StringDotFormatInvalidFormat)
|
.enabled(&Rule::StringDotFormatInvalidFormat)
|
||||||
{
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::StringDotFormatInvalidFormat {
|
pyflakes::rules::StringDotFormatInvalidFormat {
|
||||||
message: pyflakes::format::error_to_string(&e),
|
message: pyflakes::format::error_to_string(&e),
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
|
|
@ -2753,41 +2734,17 @@ where
|
||||||
}
|
}
|
||||||
ExprKind::Yield { .. } => {
|
ExprKind::Yield { .. } => {
|
||||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||||
let scope = self.current_scope();
|
pyflakes::rules::yield_outside_function(self, expr);
|
||||||
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
|
||||||
violations::YieldOutsideFunction {
|
|
||||||
keyword: DeferralKeyword::Yield,
|
|
||||||
},
|
|
||||||
Range::from_located(expr),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::YieldFrom { .. } => {
|
ExprKind::YieldFrom { .. } => {
|
||||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||||
let scope = self.current_scope();
|
pyflakes::rules::yield_outside_function(self, expr);
|
||||||
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
|
||||||
violations::YieldOutsideFunction {
|
|
||||||
keyword: DeferralKeyword::YieldFrom,
|
|
||||||
},
|
|
||||||
Range::from_located(expr),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Await { .. } => {
|
ExprKind::Await { .. } => {
|
||||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||||
let scope = self.current_scope();
|
pyflakes::rules::yield_outside_function(self, expr);
|
||||||
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
|
||||||
violations::YieldOutsideFunction {
|
|
||||||
keyword: DeferralKeyword::Await,
|
|
||||||
},
|
|
||||||
Range::from_located(expr),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if self.settings.rules.enabled(&Rule::AwaitOutsideAsync) {
|
if self.settings.rules.enabled(&Rule::AwaitOutsideAsync) {
|
||||||
pylint::rules::await_outside_async(self, expr);
|
pylint::rules::await_outside_async(self, expr);
|
||||||
|
|
@ -2870,7 +2827,7 @@ where
|
||||||
.enabled(&Rule::PercentFormatUnsupportedFormatCharacter)
|
.enabled(&Rule::PercentFormatUnsupportedFormatCharacter)
|
||||||
{
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::PercentFormatUnsupportedFormatCharacter {
|
pyflakes::rules::PercentFormatUnsupportedFormatCharacter {
|
||||||
char: c,
|
char: c,
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
|
|
@ -2884,7 +2841,7 @@ where
|
||||||
.enabled(&Rule::PercentFormatInvalidFormat)
|
.enabled(&Rule::PercentFormatInvalidFormat)
|
||||||
{
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::PercentFormatInvalidFormat {
|
pyflakes::rules::PercentFormatInvalidFormat {
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
|
|
@ -3574,7 +3531,7 @@ where
|
||||||
if !self.bindings[*index].used() {
|
if !self.bindings[*index].used() {
|
||||||
if self.settings.rules.enabled(&Rule::UnusedVariable) {
|
if self.settings.rules.enabled(&Rule::UnusedVariable) {
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::UnusedVariable {
|
pyflakes::rules::UnusedVariable {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
},
|
},
|
||||||
name_range,
|
name_range,
|
||||||
|
|
@ -3894,7 +3851,7 @@ impl<'a> Checker<'a> {
|
||||||
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
|
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
|
||||||
if self.settings.rules.enabled(&Rule::ImportShadowedByLoopVar) {
|
if self.settings.rules.enabled(&Rule::ImportShadowedByLoopVar) {
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::ImportShadowedByLoopVar {
|
pyflakes::rules::ImportShadowedByLoopVar {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
line: existing.range.location.row(),
|
line: existing.range.location.row(),
|
||||||
},
|
},
|
||||||
|
|
@ -3913,7 +3870,7 @@ impl<'a> Checker<'a> {
|
||||||
{
|
{
|
||||||
if self.settings.rules.enabled(&Rule::RedefinedWhileUnused) {
|
if self.settings.rules.enabled(&Rule::RedefinedWhileUnused) {
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::RedefinedWhileUnused {
|
pyflakes::rules::RedefinedWhileUnused {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
line: existing.range.location.row(),
|
line: existing.range.location.row(),
|
||||||
},
|
},
|
||||||
|
|
@ -4080,7 +4037,7 @@ impl<'a> Checker<'a> {
|
||||||
from_list.sort();
|
from_list.sort();
|
||||||
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::ImportStarUsage {
|
pyflakes::rules::ImportStarUsage {
|
||||||
name: id.to_string(),
|
name: id.to_string(),
|
||||||
sources: from_list,
|
sources: from_list,
|
||||||
},
|
},
|
||||||
|
|
@ -4114,7 +4071,7 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::UndefinedName { name: id.clone() },
|
pyflakes::rules::UndefinedName { name: id.clone() },
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -4322,7 +4279,7 @@ impl<'a> Checker<'a> {
|
||||||
&& self.settings.rules.enabled(&Rule::UndefinedName)
|
&& self.settings.rules.enabled(&Rule::UndefinedName)
|
||||||
{
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::UndefinedName {
|
pyflakes::rules::UndefinedName {
|
||||||
name: id.to_string(),
|
name: id.to_string(),
|
||||||
},
|
},
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
|
|
@ -4390,7 +4347,7 @@ impl<'a> Checker<'a> {
|
||||||
.enabled(&Rule::ForwardAnnotationSyntaxError)
|
.enabled(&Rule::ForwardAnnotationSyntaxError)
|
||||||
{
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
violations::ForwardAnnotationSyntaxError {
|
pyflakes::rules::ForwardAnnotationSyntaxError {
|
||||||
body: expression.to_string(),
|
body: expression.to_string(),
|
||||||
},
|
},
|
||||||
range,
|
range,
|
||||||
|
|
@ -4597,7 +4554,7 @@ impl<'a> Checker<'a> {
|
||||||
for &name in names {
|
for &name in names {
|
||||||
if !scope.values.contains_key(name) {
|
if !scope.values.contains_key(name) {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
violations::UndefinedExport {
|
pyflakes::rules::UndefinedExport {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
},
|
},
|
||||||
all_binding.range,
|
all_binding.range,
|
||||||
|
|
@ -4637,7 +4594,7 @@ impl<'a> Checker<'a> {
|
||||||
if let Some(indices) = self.redefinitions.get(index) {
|
if let Some(indices) = self.redefinitions.get(index) {
|
||||||
for index in indices {
|
for index in indices {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
violations::RedefinedWhileUnused {
|
pyflakes::rules::RedefinedWhileUnused {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
line: binding.range.location.row(),
|
line: binding.range.location.row(),
|
||||||
},
|
},
|
||||||
|
|
@ -4668,7 +4625,7 @@ impl<'a> Checker<'a> {
|
||||||
for &name in names {
|
for &name in names {
|
||||||
if !scope.values.contains_key(name) {
|
if !scope.values.contains_key(name) {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
violations::ImportStarUsage {
|
pyflakes::rules::ImportStarUsage {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
sources: from_list.clone(),
|
sources: from_list.clone(),
|
||||||
},
|
},
|
||||||
|
|
@ -4834,7 +4791,7 @@ impl<'a> Checker<'a> {
|
||||||
let multiple = unused_imports.len() > 1;
|
let multiple = unused_imports.len() > 1;
|
||||||
for (full_name, range) in unused_imports {
|
for (full_name, range) in unused_imports {
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::UnusedImport {
|
pyflakes::rules::UnusedImport {
|
||||||
name: full_name.to_string(),
|
name: full_name.to_string(),
|
||||||
ignore_init,
|
ignore_init,
|
||||||
multiple,
|
multiple,
|
||||||
|
|
@ -4860,7 +4817,7 @@ impl<'a> Checker<'a> {
|
||||||
let multiple = unused_imports.len() > 1;
|
let multiple = unused_imports.len() > 1;
|
||||||
for (full_name, range) in unused_imports {
|
for (full_name, range) in unused_imports {
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::UnusedImport {
|
pyflakes::rules::UnusedImport {
|
||||||
name: full_name.to_string(),
|
name: full_name.to_string(),
|
||||||
ignore_init,
|
ignore_init,
|
||||||
multiple,
|
multiple,
|
||||||
|
|
|
||||||
|
|
@ -242,8 +242,8 @@ mod tests {
|
||||||
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
|
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
|
||||||
use crate::registry::Diagnostic;
|
use crate::registry::Diagnostic;
|
||||||
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
|
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
|
||||||
|
use crate::rules::pyflakes;
|
||||||
use crate::source_code::LineEnding;
|
use crate::source_code::LineEnding;
|
||||||
use crate::violations;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn regex() {
|
fn regex() {
|
||||||
|
|
@ -276,7 +276,7 @@ mod tests {
|
||||||
assert_eq!(output, format!("{contents}\n"));
|
assert_eq!(output, format!("{contents}\n"));
|
||||||
|
|
||||||
let diagnostics = vec![Diagnostic::new(
|
let diagnostics = vec![Diagnostic::new(
|
||||||
violations::UnusedVariable {
|
pyflakes::rules::UnusedVariable {
|
||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
},
|
},
|
||||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
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)),
|
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||||
),
|
),
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
violations::UnusedVariable {
|
pyflakes::rules::UnusedVariable {
|
||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
},
|
},
|
||||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
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)),
|
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||||
),
|
),
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
violations::UnusedVariable {
|
pyflakes::rules::UnusedVariable {
|
||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
},
|
},
|
||||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||||
|
|
|
||||||
|
|
@ -33,49 +33,49 @@ ruff_macros::define_rule_mapping!(
|
||||||
W505 => rules::pycodestyle::rules::DocLineTooLong,
|
W505 => rules::pycodestyle::rules::DocLineTooLong,
|
||||||
W605 => rules::pycodestyle::rules::InvalidEscapeSequence,
|
W605 => rules::pycodestyle::rules::InvalidEscapeSequence,
|
||||||
// pyflakes
|
// pyflakes
|
||||||
F401 => violations::UnusedImport,
|
F401 => rules::pyflakes::rules::UnusedImport,
|
||||||
F402 => violations::ImportShadowedByLoopVar,
|
F402 => rules::pyflakes::rules::ImportShadowedByLoopVar,
|
||||||
F403 => violations::ImportStarUsed,
|
F403 => rules::pyflakes::rules::ImportStarUsed,
|
||||||
F404 => violations::LateFutureImport,
|
F404 => rules::pyflakes::rules::LateFutureImport,
|
||||||
F405 => violations::ImportStarUsage,
|
F405 => rules::pyflakes::rules::ImportStarUsage,
|
||||||
F406 => violations::ImportStarNotPermitted,
|
F406 => rules::pyflakes::rules::ImportStarNotPermitted,
|
||||||
F407 => violations::FutureFeatureNotDefined,
|
F407 => rules::pyflakes::rules::FutureFeatureNotDefined,
|
||||||
F501 => violations::PercentFormatInvalidFormat,
|
F501 => rules::pyflakes::rules::PercentFormatInvalidFormat,
|
||||||
F502 => violations::PercentFormatExpectedMapping,
|
F502 => rules::pyflakes::rules::PercentFormatExpectedMapping,
|
||||||
F503 => violations::PercentFormatExpectedSequence,
|
F503 => rules::pyflakes::rules::PercentFormatExpectedSequence,
|
||||||
F504 => violations::PercentFormatExtraNamedArguments,
|
F504 => rules::pyflakes::rules::PercentFormatExtraNamedArguments,
|
||||||
F505 => violations::PercentFormatMissingArgument,
|
F505 => rules::pyflakes::rules::PercentFormatMissingArgument,
|
||||||
F506 => violations::PercentFormatMixedPositionalAndNamed,
|
F506 => rules::pyflakes::rules::PercentFormatMixedPositionalAndNamed,
|
||||||
F507 => violations::PercentFormatPositionalCountMismatch,
|
F507 => rules::pyflakes::rules::PercentFormatPositionalCountMismatch,
|
||||||
F508 => violations::PercentFormatStarRequiresSequence,
|
F508 => rules::pyflakes::rules::PercentFormatStarRequiresSequence,
|
||||||
F509 => violations::PercentFormatUnsupportedFormatCharacter,
|
F509 => rules::pyflakes::rules::PercentFormatUnsupportedFormatCharacter,
|
||||||
F521 => violations::StringDotFormatInvalidFormat,
|
F521 => rules::pyflakes::rules::StringDotFormatInvalidFormat,
|
||||||
F522 => violations::StringDotFormatExtraNamedArguments,
|
F522 => rules::pyflakes::rules::StringDotFormatExtraNamedArguments,
|
||||||
F523 => violations::StringDotFormatExtraPositionalArguments,
|
F523 => rules::pyflakes::rules::StringDotFormatExtraPositionalArguments,
|
||||||
F524 => violations::StringDotFormatMissingArguments,
|
F524 => rules::pyflakes::rules::StringDotFormatMissingArguments,
|
||||||
F525 => violations::StringDotFormatMixingAutomatic,
|
F525 => rules::pyflakes::rules::StringDotFormatMixingAutomatic,
|
||||||
F541 => violations::FStringMissingPlaceholders,
|
F541 => rules::pyflakes::rules::FStringMissingPlaceholders,
|
||||||
F601 => violations::MultiValueRepeatedKeyLiteral,
|
F601 => rules::pyflakes::rules::MultiValueRepeatedKeyLiteral,
|
||||||
F602 => violations::MultiValueRepeatedKeyVariable,
|
F602 => rules::pyflakes::rules::MultiValueRepeatedKeyVariable,
|
||||||
F621 => violations::ExpressionsInStarAssignment,
|
F621 => rules::pyflakes::rules::ExpressionsInStarAssignment,
|
||||||
F622 => violations::TwoStarredExpressions,
|
F622 => rules::pyflakes::rules::TwoStarredExpressions,
|
||||||
F631 => violations::AssertTuple,
|
F631 => rules::pyflakes::rules::AssertTuple,
|
||||||
F632 => violations::IsLiteral,
|
F632 => rules::pyflakes::rules::IsLiteral,
|
||||||
F633 => violations::InvalidPrintSyntax,
|
F633 => rules::pyflakes::rules::InvalidPrintSyntax,
|
||||||
F634 => violations::IfTuple,
|
F634 => rules::pyflakes::rules::IfTuple,
|
||||||
F701 => violations::BreakOutsideLoop,
|
F701 => rules::pyflakes::rules::BreakOutsideLoop,
|
||||||
F702 => violations::ContinueOutsideLoop,
|
F702 => rules::pyflakes::rules::ContinueOutsideLoop,
|
||||||
F704 => violations::YieldOutsideFunction,
|
F704 => rules::pyflakes::rules::YieldOutsideFunction,
|
||||||
F706 => violations::ReturnOutsideFunction,
|
F706 => rules::pyflakes::rules::ReturnOutsideFunction,
|
||||||
F707 => violations::DefaultExceptNotLast,
|
F707 => rules::pyflakes::rules::DefaultExceptNotLast,
|
||||||
F722 => violations::ForwardAnnotationSyntaxError,
|
F722 => rules::pyflakes::rules::ForwardAnnotationSyntaxError,
|
||||||
F811 => violations::RedefinedWhileUnused,
|
F811 => rules::pyflakes::rules::RedefinedWhileUnused,
|
||||||
F821 => violations::UndefinedName,
|
F821 => rules::pyflakes::rules::UndefinedName,
|
||||||
F822 => violations::UndefinedExport,
|
F822 => rules::pyflakes::rules::UndefinedExport,
|
||||||
F823 => violations::UndefinedLocal,
|
F823 => rules::pyflakes::rules::UndefinedLocal,
|
||||||
F841 => violations::UnusedVariable,
|
F841 => rules::pyflakes::rules::UnusedVariable,
|
||||||
F842 => violations::UnusedAnnotation,
|
F842 => rules::pyflakes::rules::UnusedAnnotation,
|
||||||
F901 => violations::RaiseNotImplemented,
|
F901 => rules::pyflakes::rules::RaiseNotImplemented,
|
||||||
// pylint
|
// pylint
|
||||||
PLE0604 => rules::pylint::rules::InvalidAllObject,
|
PLE0604 => rules::pylint::rules::InvalidAllObject,
|
||||||
PLE0605 => rules::pylint::rules::InvalidAllFormat,
|
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 rustpython_ast::{Expr, ExprKind, Stmt};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::Diagnostic;
|
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
|
/// F631
|
||||||
pub fn assert_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
pub fn assert_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||||
if let ExprKind::Tuple { elts, .. } = &test.node {
|
if let ExprKind::Tuple { elts, .. } = &test.node {
|
||||||
if !elts.is_empty() {
|
if !elts.is_empty() {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker
|
||||||
violations::AssertTuple,
|
.diagnostics
|
||||||
Range::from_located(stmt),
|
.push(Diagnostic::new(AssertTuple, Range::from_located(stmt)));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 rustpython_ast::{Expr, ExprKind};
|
||||||
|
|
||||||
use crate::ast::helpers::find_useless_f_strings;
|
use crate::ast::helpers::find_useless_f_strings;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::fix::Fix;
|
use crate::fix::Fix;
|
||||||
use crate::registry::Diagnostic;
|
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
|
/// F541
|
||||||
pub fn f_string_missing_placeholders(expr: &Expr, values: &[Expr], checker: &mut Checker) {
|
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 { .. }))
|
.any(|value| matches!(value.node, ExprKind::FormattedValue { .. }))
|
||||||
{
|
{
|
||||||
for (prefix_range, tok_range) in find_useless_f_strings(expr, checker.locator) {
|
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()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
diagnostic.amend(Fix::deletion(
|
diagnostic.amend(Fix::deletion(
|
||||||
prefix_range.location,
|
prefix_range.location,
|
||||||
|
|
|
||||||
|
|
@ -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 rustpython_ast::{Expr, ExprKind, Stmt};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::Diagnostic;
|
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
|
/// F634
|
||||||
pub fn if_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
pub fn if_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||||
if let ExprKind::Tuple { elts, .. } = &test.node {
|
if let ExprKind::Tuple { elts, .. } = &test.node {
|
||||||
if !elts.is_empty() {
|
if !elts.is_empty() {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker
|
||||||
violations::IfTuple,
|
.diagnostics
|
||||||
Range::from_located(stmt),
|
.push(Diagnostic::new(IfTuple, Range::from_located(stmt)));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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::helpers;
|
||||||
use crate::ast::operations::locate_cmpops;
|
use crate::ast::operations::locate_cmpops;
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::define_violation;
|
||||||
use crate::fix::Fix;
|
use crate::fix::Fix;
|
||||||
use crate::registry::Diagnostic;
|
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
|
/// F632
|
||||||
pub fn invalid_literal_comparison(
|
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(left)
|
||||||
|| helpers::is_constant_non_singleton(right))
|
|| helpers::is_constant_non_singleton(right))
|
||||||
{
|
{
|
||||||
let mut diagnostic =
|
let mut diagnostic = Diagnostic::new(IsLiteral { cmpop: op.into() }, location);
|
||||||
Diagnostic::new(violations::IsLiteral { cmpop: op.into() }, location);
|
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
if let Some(located_op) = &located.get(index) {
|
if let Some(located_op) = &located.get(index) {
|
||||||
assert_eq!(&located_op.node, op);
|
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 rustpython_ast::{Expr, ExprKind};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::Diagnostic;
|
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
|
/// F633
|
||||||
pub fn invalid_print_syntax(checker: &mut Checker, left: &Expr) {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::InvalidPrintSyntax,
|
InvalidPrintSyntax,
|
||||||
Range::from_located(left),
|
Range::from_located(left),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,187 +1,67 @@
|
||||||
pub use assert_tuple::assert_tuple;
|
pub use assert_tuple::{assert_tuple, AssertTuple};
|
||||||
pub use f_string_missing_placeholders::f_string_missing_placeholders;
|
pub use break_outside_loop::{break_outside_loop, BreakOutsideLoop};
|
||||||
pub use if_tuple::if_tuple;
|
pub use continue_outside_loop::{continue_outside_loop, ContinueOutsideLoop};
|
||||||
pub use invalid_literal_comparisons::invalid_literal_comparison;
|
pub use default_except_not_last::{default_except_not_last, DefaultExceptNotLast};
|
||||||
pub use invalid_print_syntax::invalid_print_syntax;
|
pub use f_string_missing_placeholders::{
|
||||||
pub use raise_not_implemented::raise_not_implemented;
|
f_string_missing_placeholders, FStringMissingPlaceholders,
|
||||||
pub use repeated_keys::repeated_keys;
|
};
|
||||||
|
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::{
|
pub(crate) use strings::{
|
||||||
percent_format_expected_mapping, percent_format_expected_sequence,
|
percent_format_expected_mapping, percent_format_expected_sequence,
|
||||||
percent_format_extra_named_arguments, percent_format_missing_arguments,
|
percent_format_extra_named_arguments, percent_format_missing_arguments,
|
||||||
percent_format_mixed_positional_and_named, percent_format_positional_count_mismatch,
|
percent_format_mixed_positional_and_named, percent_format_positional_count_mismatch,
|
||||||
percent_format_star_requires_sequence, string_dot_format_extra_named_arguments,
|
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_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 undefined_export::UndefinedExport;
|
||||||
pub use unused_variable::unused_variable;
|
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 assert_tuple;
|
||||||
|
mod break_outside_loop;
|
||||||
|
mod continue_outside_loop;
|
||||||
|
mod default_except_not_last;
|
||||||
mod f_string_missing_placeholders;
|
mod f_string_missing_placeholders;
|
||||||
|
mod forward_annotation_syntax_error;
|
||||||
mod if_tuple;
|
mod if_tuple;
|
||||||
|
mod imports;
|
||||||
mod invalid_literal_comparisons;
|
mod invalid_literal_comparisons;
|
||||||
mod invalid_print_syntax;
|
mod invalid_print_syntax;
|
||||||
mod raise_not_implemented;
|
mod raise_not_implemented;
|
||||||
|
mod redefined_while_unused;
|
||||||
mod repeated_keys;
|
mod repeated_keys;
|
||||||
|
mod return_outside_function;
|
||||||
|
mod starred_expressions;
|
||||||
mod strings;
|
mod strings;
|
||||||
|
mod undefined_export;
|
||||||
|
mod undefined_local;
|
||||||
|
mod undefined_name;
|
||||||
mod unused_annotation;
|
mod unused_annotation;
|
||||||
mod unused_variable;
|
mod unused_variable;
|
||||||
|
mod yield_outside_function;
|
||||||
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),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,26 @@
|
||||||
|
use crate::define_violation;
|
||||||
|
use ruff_macros::derive_message_formats;
|
||||||
use rustpython_ast::{Expr, ExprKind};
|
use rustpython_ast::{Expr, ExprKind};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::fix::Fix;
|
use crate::fix::Fix;
|
||||||
use crate::registry::Diagnostic;
|
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> {
|
fn match_not_implemented(expr: &Expr) -> Option<&Expr> {
|
||||||
match &expr.node {
|
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 {
|
let Some(expr) = match_not_implemented(expr) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut diagnostic =
|
let mut diagnostic = Diagnostic::new(RaiseNotImplemented, Range::from_located(expr));
|
||||||
Diagnostic::new(violations::RaiseNotImplemented, Range::from_located(expr));
|
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
diagnostic.amend(Fix::replacement(
|
diagnostic.amend(Fix::replacement(
|
||||||
"NotImplementedError".to_string(),
|
"NotImplementedError".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -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::checkers::ast::Checker;
|
||||||
use crate::fix::Fix;
|
use crate::fix::Fix;
|
||||||
use crate::registry::{Diagnostic, Rule};
|
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)]
|
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||||
enum DictionaryKey<'a> {
|
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 comparable_value: ComparableExpr = (&values[i]).into();
|
||||||
let is_duplicate_value = seen_values.contains(&comparable_value);
|
let is_duplicate_value = seen_values.contains(&comparable_value);
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::MultiValueRepeatedKeyLiteral {
|
MultiValueRepeatedKeyLiteral {
|
||||||
name: unparse_expr(key, checker.stylist),
|
name: unparse_expr(key, checker.stylist),
|
||||||
repeated_value: is_duplicate_value,
|
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 comparable_value: ComparableExpr = (&values[i]).into();
|
||||||
let is_duplicate_value = seen_values.contains(&comparable_value);
|
let is_duplicate_value = seen_values.contains(&comparable_value);
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::MultiValueRepeatedKeyVariable {
|
MultiValueRepeatedKeyVariable {
|
||||||
name: dict_key.to_string(),
|
name: dict_key.to_string(),
|
||||||
repeated_value: is_duplicate_value,
|
repeated_value: is_duplicate_value,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 std::string::ToString;
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
@ -13,7 +15,192 @@ use super::super::format::FormatSummary;
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::Diagnostic;
|
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 {
|
fn has_star_star_kwargs(keywords: &[Keyword]) -> bool {
|
||||||
keywords.iter().any(|k| {
|
keywords.iter().any(|k| {
|
||||||
|
|
@ -42,10 +229,9 @@ pub(crate) fn percent_format_expected_mapping(
|
||||||
| ExprKind::Set { .. }
|
| ExprKind::Set { .. }
|
||||||
| ExprKind::ListComp { .. }
|
| ExprKind::ListComp { .. }
|
||||||
| ExprKind::SetComp { .. }
|
| ExprKind::SetComp { .. }
|
||||||
| ExprKind::GeneratorExp { .. } => checker.diagnostics.push(Diagnostic::new(
|
| ExprKind::GeneratorExp { .. } => checker
|
||||||
violations::PercentFormatExpectedMapping,
|
.diagnostics
|
||||||
location,
|
.push(Diagnostic::new(PercentFormatExpectedMapping, location)),
|
||||||
)),
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,10 +250,9 @@ pub(crate) fn percent_format_expected_sequence(
|
||||||
ExprKind::Dict { .. } | ExprKind::DictComp { .. }
|
ExprKind::Dict { .. } | ExprKind::DictComp { .. }
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker
|
||||||
violations::PercentFormatExpectedSequence,
|
.diagnostics
|
||||||
location,
|
.push(Diagnostic::new(PercentFormatExpectedSequence, location));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +299,7 @@ pub(crate) fn percent_format_extra_named_arguments(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::PercentFormatExtraNamedArguments {
|
PercentFormatExtraNamedArguments {
|
||||||
missing: missing.iter().map(|&arg| arg.to_string()).collect(),
|
missing: missing.iter().map(|&arg| arg.to_string()).collect(),
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
|
|
@ -174,7 +359,7 @@ pub(crate) fn percent_format_missing_arguments(
|
||||||
|
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::PercentFormatMissingArgument {
|
PercentFormatMissingArgument {
|
||||||
missing: missing.iter().map(|&s| s.clone()).collect(),
|
missing: missing.iter().map(|&s| s.clone()).collect(),
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
|
|
@ -191,7 +376,7 @@ pub(crate) fn percent_format_mixed_positional_and_named(
|
||||||
) {
|
) {
|
||||||
if !(summary.num_positional == 0 || summary.keywords.is_empty()) {
|
if !(summary.num_positional == 0 || summary.keywords.is_empty()) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::PercentFormatMixedPositionalAndNamed,
|
PercentFormatMixedPositionalAndNamed,
|
||||||
location,
|
location,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +405,7 @@ pub(crate) fn percent_format_positional_count_mismatch(
|
||||||
|
|
||||||
if found != summary.num_positional {
|
if found != summary.num_positional {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::PercentFormatPositionalCountMismatch {
|
PercentFormatPositionalCountMismatch {
|
||||||
wanted: summary.num_positional,
|
wanted: summary.num_positional,
|
||||||
got: found,
|
got: found,
|
||||||
},
|
},
|
||||||
|
|
@ -241,9 +426,9 @@ pub(crate) fn percent_format_star_requires_sequence(
|
||||||
) {
|
) {
|
||||||
if summary.starred {
|
if summary.starred {
|
||||||
match &right.node {
|
match &right.node {
|
||||||
ExprKind::Dict { .. } | ExprKind::DictComp { .. } => checker.diagnostics.push(
|
ExprKind::Dict { .. } | ExprKind::DictComp { .. } => checker
|
||||||
Diagnostic::new(violations::PercentFormatStarRequiresSequence, location),
|
.diagnostics
|
||||||
),
|
.push(Diagnostic::new(PercentFormatStarRequiresSequence, location)),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +465,7 @@ pub(crate) fn string_dot_format_extra_named_arguments(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::StringDotFormatExtraNamedArguments {
|
StringDotFormatExtraNamedArguments {
|
||||||
missing: missing.iter().map(|&arg| arg.to_string()).collect(),
|
missing: missing.iter().map(|&arg| arg.to_string()).collect(),
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
|
|
@ -321,7 +506,7 @@ pub(crate) fn string_dot_format_extra_positional_arguments(
|
||||||
}
|
}
|
||||||
|
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::StringDotFormatExtraPositionalArguments {
|
StringDotFormatExtraPositionalArguments {
|
||||||
missing: missing
|
missing: missing
|
||||||
.iter()
|
.iter()
|
||||||
.map(std::string::ToString::to_string)
|
.map(std::string::ToString::to_string)
|
||||||
|
|
@ -368,7 +553,7 @@ pub(crate) fn string_dot_format_missing_argument(
|
||||||
|
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::StringDotFormatMissingArguments { missing },
|
StringDotFormatMissingArguments { missing },
|
||||||
location,
|
location,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -381,9 +566,8 @@ pub(crate) fn string_dot_format_mixing_automatic(
|
||||||
location: Range,
|
location: Range,
|
||||||
) {
|
) {
|
||||||
if !(summary.autos.is_empty() || summary.indexes.is_empty()) {
|
if !(summary.autos.is_empty() || summary.indexes.is_empty()) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker
|
||||||
violations::StringDotFormatMixingAutomatic,
|
.diagnostics
|
||||||
location,
|
.push(Diagnostic::new(StringDotFormatMixingAutomatic, location));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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__`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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::ast::types::BindingKind;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::define_violation;
|
||||||
use crate::registry::Diagnostic;
|
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
|
/// F842
|
||||||
pub fn unused_annotation(checker: &mut Checker, scope: usize) {
|
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.settings.dummy_variable_rgx.is_match(name)
|
||||||
{
|
{
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
violations::UnusedAnnotation {
|
UnusedAnnotation {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.range,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
use crate::define_violation;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
use ruff_macros::derive_message_formats;
|
||||||
use rustpython_ast::{ExprKind, Location, Stmt, StmtKind};
|
use rustpython_ast::{ExprKind, Location, Stmt, StmtKind};
|
||||||
use rustpython_parser::lexer;
|
use rustpython_parser::lexer;
|
||||||
use rustpython_parser::lexer::Tok;
|
use rustpython_parser::lexer::Tok;
|
||||||
|
|
@ -11,7 +13,25 @@ use crate::checkers::ast::Checker;
|
||||||
use crate::fix::Fix;
|
use crate::fix::Fix;
|
||||||
use crate::registry::Diagnostic;
|
use crate::registry::Diagnostic;
|
||||||
use crate::source_code::Locator;
|
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
|
fn match_token_after<F>(stmt: &Stmt, locator: &Locator, f: F) -> Location
|
||||||
where
|
where
|
||||||
|
|
@ -174,7 +194,7 @@ pub fn unused_variable(checker: &mut Checker, scope: usize) {
|
||||||
&& name != &"__traceback_supplement__"
|
&& name != &"__traceback_supplement__"
|
||||||
{
|
{
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
violations::UnusedVariable {
|
UnusedVariable {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.range,
|
||||||
|
|
|
||||||
|
|
@ -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
|
// pylint
|
||||||
|
|
||||||
define_violation!(
|
define_violation!(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue