Add a "fix message" to every autofix-able check (#1489)

This commit is contained in:
Charlie Marsh 2022-12-30 23:16:03 -05:00 committed by GitHub
parent 1e3cf87f67
commit 01c74e0629
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 594 additions and 166 deletions

View file

@ -545,7 +545,7 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
| F621 | ExpressionsInStarAssignment | Too many expressions in star-unpacking assignment | |
| F622 | TwoStarredExpressions | Two starred expressions in assignment | |
| F631 | AssertTuple | Assert test is a non-empty tuple, which is always `True` | |
| F632 | IsLiteral | Use `==` and `!=` to compare constant literals | 🛠 |
| F632 | IsLiteral | Use `==` to compare constant literals | 🛠 |
| F633 | InvalidPrintSyntax | Use of `>>` is invalid with `print` function | |
| F634 | IfTuple | If test is a tuple, which is always `True` | |
| F701 | BreakOutsideLoop | `break` outside loop | |
@ -578,7 +578,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
| E722 | DoNotUseBareExcept | Do not use bare `except` | |
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | 🛠 |
| E731 | DoNotAssignLambda | Do not assign a `lambda` expression, use a `def` | 🛠 |
| E741 | AmbiguousVariableName | Ambiguous variable name: `...` | |
| E742 | AmbiguousClassName | Ambiguous class name: `...` | |
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
@ -663,7 +663,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| ---- | ---- | ------- | --- |
| UP001 | UselessMetaclassType | `__metaclass__ = type` is implied | 🛠 |
| UP003 | TypeOfPrimitive | Use `str` instead of `type(...)` | 🛠 |
| UP004 | UselessObjectInheritance | Class `...` inherits from object | 🛠 |
| UP004 | UselessObjectInheritance | Class `...` inherits from `object` | 🛠 |
| UP005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` | 🛠 |
| UP006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | 🛠 |
| UP007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
@ -677,7 +677,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
| UP016 | RemoveSixCompat | Unnecessary `six` compatibility usage | 🛠 |
| UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 |
| UP018 | NativeLiterals | Unnecessary call to `str` and `bytes` | 🛠 |
| UP018 | NativeLiterals | Unnecessary call to `str` | 🛠 |
| UP019 | TypingTextStrAlias | `typing.Text` is deprecated, use `str` | 🛠 |
| UP020 | OpenAlias | Use builtin `open` | 🛠 |
| UP021 | ReplaceUniversalNewlines | `universal_newlines` is deprecated, use `text` | 🛠 |
@ -1018,7 +1018,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
| ---- | ---- | ------- | --- |
| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | |
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF004 | KeywordArgumentBeforeStarArgument | Keyword argument `...` must come after starred arguments | |
| RUF100 | UnusedNOQA | Unused blanket `noqa` directive | 🛠 |

View file

@ -56,7 +56,9 @@ export default function SourceEditor({
.filter((check) => position.startLineNumber === check.location.row)
.filter((check) => check.fix)
.map((check) => ({
title: `Fix ${check.code}`,
title: check.fix
? check.fix.message ?? `Fix ${check.code}`
: "Autofix",
id: `fix-${check.code}`,
kind: "quickfix",
edit: check.fix

View file

@ -3735,9 +3735,10 @@ impl<'a> Checker<'a> {
None
};
let multiple = unused_imports.len() > 1;
for (full_name, range) in unused_imports {
let mut check = Check::new(
CheckKind::UnusedImport(full_name.clone(), ignore_init),
CheckKind::UnusedImport(full_name.clone(), ignore_init, multiple),
*range,
);
if matches!(child.node, StmtKind::ImportFrom { .. })
@ -3756,9 +3757,10 @@ impl<'a> Checker<'a> {
.sorted_by_key(|((defined_by, _), _)| defined_by.0.location)
{
let child = defined_by.0;
let multiple = unused_imports.len() > 1;
for (full_name, range) in unused_imports {
let mut check = Check::new(
CheckKind::UnusedImport(full_name.clone(), ignore_init),
CheckKind::UnusedImport(full_name.clone(), ignore_init, multiple),
*range,
);
if matches!(child.node, StmtKind::ImportFrom { .. })

View file

@ -3,6 +3,7 @@ use std::fmt;
use itertools::Itertools;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use rustpython_ast::Cmpop;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
@ -618,11 +619,37 @@ pub enum LintSource {
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum RejectedCmpop {
pub enum EqCmpop {
Eq,
NotEq,
}
impl From<&Cmpop> for EqCmpop {
fn from(cmpop: &Cmpop) -> Self {
match cmpop {
Cmpop::Eq => EqCmpop::Eq,
Cmpop::NotEq => EqCmpop::NotEq,
_ => unreachable!("Expected Cmpop::Eq | Cmpop::NotEq"),
}
}
}
#[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"),
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeferralKeyword {
Yield,
@ -655,6 +682,21 @@ impl fmt::Display for Branch {
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum LiteralType {
Str,
Bytes,
}
impl fmt::Display for LiteralType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
LiteralType::Str => fmt.write_str("str"),
LiteralType::Bytes => fmt.write_str("bytes"),
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnusedCodes {
pub unknown: Vec<String>,
@ -668,17 +710,17 @@ pub enum CheckKind {
AmbiguousClassName(String),
AmbiguousFunctionName(String),
AmbiguousVariableName(String),
DoNotAssignLambda,
DoNotAssignLambda(String),
DoNotUseBareExcept,
IOError(String),
LineTooLong(usize, usize),
ModuleImportNotAtTopOfFile,
MultipleImportsOnOneLine,
NoneComparison(RejectedCmpop),
NoneComparison(EqCmpop),
NotInTest,
NotIsTest,
SyntaxError(String),
TrueFalseComparison(bool, RejectedCmpop),
TrueFalseComparison(bool, EqCmpop),
TypeComparison,
// pycodestyle warnings
NoNewLineAtEndOfFile,
@ -699,7 +741,7 @@ pub enum CheckKind {
ImportStarUsage(String, Vec<String>),
ImportStarUsed(String),
InvalidPrintSyntax,
IsLiteral,
IsLiteral(IsCmpop),
LateFutureImport,
MultiValueRepeatedKeyLiteral,
MultiValueRepeatedKeyVariable(String),
@ -725,7 +767,7 @@ pub enum CheckKind {
UndefinedLocal(String),
UnusedAnnotation(String),
UndefinedName(String),
UnusedImport(String, bool),
UnusedImport(String, bool, bool),
UnusedVariable(String),
YieldOutsideFunction(DeferralKeyword),
// pylint
@ -859,10 +901,10 @@ pub enum CheckKind {
UnnecessaryEncodeUTF8,
ConvertTypedDictFunctionalToClass(String),
ConvertNamedTupleFunctionalToClass(String),
RedundantOpenModes,
RedundantOpenModes(Option<String>),
RemoveSixCompat,
DatetimeTimezoneUTC,
NativeLiterals,
NativeLiterals(LiteralType),
OpenAlias,
ReplaceUniversalNewlines,
ReplaceStdoutStderr,
@ -1030,13 +1072,13 @@ impl CheckCode {
CheckCode::E401 => CheckKind::MultipleImportsOnOneLine,
CheckCode::E402 => CheckKind::ModuleImportNotAtTopOfFile,
CheckCode::E501 => CheckKind::LineTooLong(89, 88),
CheckCode::E711 => CheckKind::NoneComparison(RejectedCmpop::Eq),
CheckCode::E712 => CheckKind::TrueFalseComparison(true, RejectedCmpop::Eq),
CheckCode::E711 => CheckKind::NoneComparison(EqCmpop::Eq),
CheckCode::E712 => CheckKind::TrueFalseComparison(true, EqCmpop::Eq),
CheckCode::E713 => CheckKind::NotInTest,
CheckCode::E714 => CheckKind::NotIsTest,
CheckCode::E721 => CheckKind::TypeComparison,
CheckCode::E722 => CheckKind::DoNotUseBareExcept,
CheckCode::E731 => CheckKind::DoNotAssignLambda,
CheckCode::E731 => CheckKind::DoNotAssignLambda("...".to_string()),
CheckCode::E741 => CheckKind::AmbiguousVariableName("...".to_string()),
CheckCode::E742 => CheckKind::AmbiguousClassName("...".to_string()),
CheckCode::E743 => CheckKind::AmbiguousFunctionName("...".to_string()),
@ -1046,7 +1088,7 @@ impl CheckCode {
CheckCode::W292 => CheckKind::NoNewLineAtEndOfFile,
CheckCode::W605 => CheckKind::InvalidEscapeSequence('c'),
// pyflakes
CheckCode::F401 => CheckKind::UnusedImport("...".to_string(), false),
CheckCode::F401 => CheckKind::UnusedImport("...".to_string(), false, false),
CheckCode::F402 => CheckKind::ImportShadowedByLoopVar("...".to_string(), 1),
CheckCode::F403 => CheckKind::ImportStarUsed("...".to_string()),
CheckCode::F404 => CheckKind::LateFutureImport,
@ -1079,7 +1121,7 @@ impl CheckCode {
CheckCode::F621 => CheckKind::ExpressionsInStarAssignment,
CheckCode::F622 => CheckKind::TwoStarredExpressions,
CheckCode::F631 => CheckKind::AssertTuple,
CheckCode::F632 => CheckKind::IsLiteral,
CheckCode::F632 => CheckKind::IsLiteral(IsCmpop::Is),
CheckCode::F633 => CheckKind::InvalidPrintSyntax,
CheckCode::F634 => CheckKind::IfTuple,
CheckCode::F701 => CheckKind::BreakOutsideLoop,
@ -1253,10 +1295,10 @@ impl CheckCode {
CheckCode::UP012 => CheckKind::UnnecessaryEncodeUTF8,
CheckCode::UP013 => CheckKind::ConvertTypedDictFunctionalToClass("...".to_string()),
CheckCode::UP014 => CheckKind::ConvertNamedTupleFunctionalToClass("...".to_string()),
CheckCode::UP015 => CheckKind::RedundantOpenModes,
CheckCode::UP015 => CheckKind::RedundantOpenModes(None),
CheckCode::UP016 => CheckKind::RemoveSixCompat,
CheckCode::UP017 => CheckKind::DatetimeTimezoneUTC,
CheckCode::UP018 => CheckKind::NativeLiterals,
CheckCode::UP018 => CheckKind::NativeLiterals(LiteralType::Str),
CheckCode::UP019 => CheckKind::TypingTextStrAlias,
CheckCode::UP020 => CheckKind::OpenAlias,
CheckCode::UP021 => CheckKind::ReplaceUniversalNewlines,
@ -1724,7 +1766,7 @@ impl CheckKind {
CheckKind::BreakOutsideLoop => &CheckCode::F701,
CheckKind::ContinueOutsideLoop => &CheckCode::F702,
CheckKind::DefaultExceptNotLast => &CheckCode::F707,
CheckKind::DoNotAssignLambda => &CheckCode::E731,
CheckKind::DoNotAssignLambda(..) => &CheckCode::E731,
CheckKind::DoNotUseBareExcept => &CheckCode::E722,
CheckKind::DuplicateArgumentName => &CheckCode::F831,
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
@ -1737,7 +1779,7 @@ impl CheckKind {
CheckKind::ImportStarUsage(..) => &CheckCode::F405,
CheckKind::ImportStarUsed(..) => &CheckCode::F403,
CheckKind::InvalidPrintSyntax => &CheckCode::F633,
CheckKind::IsLiteral => &CheckCode::F632,
CheckKind::IsLiteral(..) => &CheckCode::F632,
CheckKind::LateFutureImport => &CheckCode::F404,
CheckKind::LineTooLong(..) => &CheckCode::E501,
CheckKind::MultipleImportsOnOneLine => &CheckCode::E401,
@ -1909,10 +1951,10 @@ impl CheckKind {
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::UP012,
CheckKind::ConvertTypedDictFunctionalToClass(..) => &CheckCode::UP013,
CheckKind::ConvertNamedTupleFunctionalToClass(..) => &CheckCode::UP014,
CheckKind::RedundantOpenModes => &CheckCode::UP015,
CheckKind::RedundantOpenModes(..) => &CheckCode::UP015,
CheckKind::RemoveSixCompat => &CheckCode::UP016,
CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017,
CheckKind::NativeLiterals => &CheckCode::UP018,
CheckKind::NativeLiterals(..) => &CheckCode::UP018,
CheckKind::TypingTextStrAlias => &CheckCode::UP019,
CheckKind::OpenAlias => &CheckCode::UP020,
CheckKind::ReplaceUniversalNewlines => &CheckCode::UP021,
@ -2068,8 +2110,8 @@ impl CheckKind {
CheckKind::DefaultExceptNotLast => {
"An `except` block as not the last exception handler".to_string()
}
CheckKind::DoNotAssignLambda => {
"Do not assign a lambda expression, use a def".to_string()
CheckKind::DoNotAssignLambda(..) => {
"Do not assign a `lambda` expression, use a `def`".to_string()
}
CheckKind::DoNotUseBareExcept => "Do not use bare `except`".to_string(),
CheckKind::DuplicateArgumentName => {
@ -2105,7 +2147,10 @@ impl CheckKind {
.join(", ");
format!("`{name}` may be undefined, or defined from star imports: {sources}")
}
CheckKind::IsLiteral => "Use `==` and `!=` to compare constant literals".to_string(),
CheckKind::IsLiteral(cmpop) => match cmpop {
IsCmpop::Is => "Use `==` to compare constant literals".to_string(),
IsCmpop::IsNot => "Use `!=` to compare constant literals".to_string(),
},
CheckKind::LateFutureImport => {
"`from __future__` imports must occur at the beginning of the file".to_string()
}
@ -2123,10 +2168,8 @@ impl CheckKind {
format!("Dictionary key `{name}` repeated")
}
CheckKind::NoneComparison(op) => match op {
RejectedCmpop::Eq => "Comparison to `None` should be `cond is None`".to_string(),
RejectedCmpop::NotEq => {
"Comparison to `None` should be `cond is not None`".to_string()
}
EqCmpop::Eq => "Comparison to `None` should be `cond is None`".to_string(),
EqCmpop::NotEq => "Comparison to `None` should be `cond is not None`".to_string(),
},
CheckKind::NotInTest => "Test for membership should be `not in`".to_string(),
CheckKind::NotIsTest => "Test for object identity should be `is not`".to_string(),
@ -2190,16 +2233,16 @@ impl CheckKind {
CheckKind::ExpressionsInStarAssignment => {
"Too many expressions in star-unpacking assignment".to_string()
}
CheckKind::TrueFalseComparison(true, RejectedCmpop::Eq) => {
CheckKind::TrueFalseComparison(true, EqCmpop::Eq) => {
"Comparison to `True` should be `cond is True`".to_string()
}
CheckKind::TrueFalseComparison(true, RejectedCmpop::NotEq) => {
CheckKind::TrueFalseComparison(true, EqCmpop::NotEq) => {
"Comparison to `True` should be `cond is not True`".to_string()
}
CheckKind::TrueFalseComparison(false, RejectedCmpop::Eq) => {
CheckKind::TrueFalseComparison(false, EqCmpop::Eq) => {
"Comparison to `False` should be `cond is False`".to_string()
}
CheckKind::TrueFalseComparison(false, RejectedCmpop::NotEq) => {
CheckKind::TrueFalseComparison(false, EqCmpop::NotEq) => {
"Comparison to `False` should be `cond is not False`".to_string()
}
CheckKind::TwoStarredExpressions => "Two starred expressions in assignment".to_string(),
@ -2216,7 +2259,7 @@ impl CheckKind {
CheckKind::UnusedAnnotation(name) => {
format!("Local variable `{name}` is annotated but never used")
}
CheckKind::UnusedImport(name, ignore_init) => {
CheckKind::UnusedImport(name, ignore_init, ..) => {
if *ignore_init {
format!(
"`{name}` imported but unused; consider adding to `__all__` or using a \
@ -2627,7 +2670,7 @@ impl CheckKind {
format!("`{alias}` is deprecated, use `{target}`")
}
CheckKind::UselessObjectInheritance(name) => {
format!("Class `{name}` inherits from object")
format!("Class `{name}` inherits from `object`")
}
CheckKind::UsePEP585Annotation(name) => {
format!(
@ -2653,14 +2696,24 @@ impl CheckKind {
"Unnecessary parameters to `functools.lru_cache`".to_string()
}
CheckKind::UnnecessaryEncodeUTF8 => "Unnecessary call to `encode` as UTF-8".to_string(),
CheckKind::RedundantOpenModes => "Unnecessary open mode parameters".to_string(),
CheckKind::RedundantOpenModes(replacement) => match replacement {
None => "Unnecessary open mode parameters".to_string(),
Some(replacement) => {
format!("Unnecessary open mode parameters, use \"{replacement}\"")
}
},
CheckKind::RemoveSixCompat => "Unnecessary `six` compatibility usage".to_string(),
CheckKind::DatetimeTimezoneUTC => "Use `datetime.UTC` alias".to_string(),
CheckKind::NativeLiterals => "Unnecessary call to `str` and `bytes`".to_string(),
CheckKind::NativeLiterals(literal_type) => {
format!("Unnecessary call to `{literal_type}`")
}
CheckKind::OpenAlias => "Use builtin `open`".to_string(),
CheckKind::ConvertTypedDictFunctionalToClass(name) => {
format!("Convert `{name}` from `TypedDict` functional to class syntax")
}
CheckKind::ConvertNamedTupleFunctionalToClass(name) => {
format!("Convert `{name}` from `NamedTuple` functional to class syntax")
}
CheckKind::ReplaceUniversalNewlines => {
"`universal_newlines` is deprecated, use `text`".to_string()
}
@ -2671,9 +2724,6 @@ impl CheckKind {
"`cElementTree` is deprecated, use `ElementTree`".to_string()
}
CheckKind::RewriteUnicodeLiteral => "Remove unicode literals from strings".to_string(),
CheckKind::ConvertNamedTupleFunctionalToClass(name) => {
format!("Convert `{name}` from `NamedTuple` functional to class syntax")
}
// pydocstyle
CheckKind::FitsOnOneLine => "One-line docstring should fit on one line".to_string(),
CheckKind::BlankLineAfterSummary => {
@ -3088,6 +3138,7 @@ impl CheckKind {
self,
CheckKind::AmbiguousUnicodeCharacterString(..)
| CheckKind::AmbiguousUnicodeCharacterDocstring(..)
| CheckKind::AmbiguousUnicodeCharacterComment(..)
| CheckKind::BlankLineAfterLastSection(..)
| CheckKind::BlankLineAfterSection(..)
| CheckKind::BlankLineAfterSummary
@ -3100,7 +3151,7 @@ impl CheckKind {
| CheckKind::DatetimeTimezoneUTC
| CheckKind::DeprecatedUnittestAlias(..)
| CheckKind::DoNotAssertFalse
| CheckKind::DoNotAssignLambda
| CheckKind::DoNotAssignLambda(..)
| CheckKind::DuplicateHandlerException(..)
| CheckKind::EndsInPeriod
| CheckKind::EndsInPunctuation
@ -3108,11 +3159,11 @@ impl CheckKind {
| CheckKind::ImplicitReturn
| CheckKind::ImplicitReturnValue
| CheckKind::InvalidEscapeSequence(..)
| CheckKind::IsLiteral
| CheckKind::IsLiteral(..)
| CheckKind::KeyInDict(..)
| CheckKind::MisplacedComparisonConstant(..)
| CheckKind::MissingReturnTypeSpecialMethod(..)
| CheckKind::NativeLiterals
| CheckKind::NativeLiterals(..)
| CheckKind::OpenAlias
| CheckKind::NewLineAfterLastParagraph
| CheckKind::ReplaceUniversalNewlines
@ -3135,10 +3186,10 @@ impl CheckKind {
| CheckKind::OneBlankLineBeforeClass(..)
| CheckKind::PEP3120UnnecessaryCodingComment
| CheckKind::PPrintFound
| CheckKind::PercentFormatExtraNamedArguments(..)
| CheckKind::PrintFound
| CheckKind::PercentFormatExtraNamedArguments(..)
| CheckKind::RaiseNotImplemented
| CheckKind::RedundantOpenModes
| CheckKind::RedundantOpenModes(..)
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::RemoveSixCompat
| CheckKind::SectionNameEndsInColon(..)
@ -3170,7 +3221,7 @@ impl CheckKind {
| CheckKind::UnnecessaryLiteralWithinTupleCall(..)
| CheckKind::UnnecessaryReturnNone
| CheckKind::UnsortedImports
| CheckKind::UnusedImport(_, false)
| CheckKind::UnusedImport(_, false, _)
| CheckKind::UnusedLoopControlVariable(..)
| CheckKind::UnusedNOQA(..)
| CheckKind::UsePEP585Annotation(..)
@ -3181,6 +3232,235 @@ impl CheckKind {
| CheckKind::UselessObjectInheritance(..)
)
}
/// The message used to describe the fix action for a given `CheckKind`.
pub fn commit(&self) -> Option<String> {
match self {
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant)
| CheckKind::AmbiguousUnicodeCharacterDocstring(confusable, representant)
| CheckKind::AmbiguousUnicodeCharacterComment(confusable, representant) => {
Some(format!("Replace '{confusable}' with '{representant}'"))
}
CheckKind::BlankLineAfterLastSection(name) => {
Some(format!("Add blank line after \"{name}\""))
}
CheckKind::BlankLineAfterSection(name) => {
Some(format!("Add blank line after \"{name}\""))
}
CheckKind::BlankLineAfterSummary => Some("Insert single blank line".to_string()),
CheckKind::BlankLineBeforeSection(name) => {
Some(format!("Add blank line before \"{name}\""))
}
CheckKind::CapitalizeSectionName(name) => Some(format!("Capitalize \"{name}\"")),
CheckKind::CommentedOutCode => Some("Remove commented-out code".to_string()),
CheckKind::ConvertTypedDictFunctionalToClass(name)
| CheckKind::ConvertNamedTupleFunctionalToClass(name) => {
Some(format!("Convert `{name}` to class syntax"))
}
CheckKind::DashedUnderlineAfterSection(name) => {
Some(format!("Add dashed line under \"{name}\""))
}
CheckKind::DatetimeTimezoneUTC => Some("Convert to `datetime.UTC` alias".to_string()),
CheckKind::DeprecatedUnittestAlias(alias, target) => {
Some(format!("Replace `{target}` with `{alias}`"))
}
CheckKind::DoNotAssertFalse => Some("Replace `assert False`".to_string()),
CheckKind::DoNotAssignLambda(name) => Some(format!("Rewrite `{name}` as a `def`")),
CheckKind::DuplicateHandlerException(..) => Some("De-duplicate exceptions".to_string()),
CheckKind::EndsInPeriod => Some("Add period".to_string()),
CheckKind::EndsInPunctuation => Some("Add closing punctuation".to_string()),
CheckKind::GetAttrWithConstant => {
Some("Replace `getattr` with attribute access".to_string())
}
CheckKind::ImplicitReturnValue => Some("Add explicit `None` return value".to_string()),
CheckKind::ImplicitReturn => Some("Add explicit `return` statement".to_string()),
CheckKind::InvalidEscapeSequence(..) => {
Some("Add backslash to escape sequence".to_string())
}
CheckKind::IsLiteral(cmpop) => Some(match cmpop {
IsCmpop::Is => "Replace `is` with `==`".to_string(),
IsCmpop::IsNot => "Replace `is not` with `!=`".to_string(),
}),
CheckKind::KeyInDict(key, dict) => Some(format!("Convert to `{key} in {dict}`")),
CheckKind::MisplacedComparisonConstant(comparison) => {
Some(format!("Replace with {comparison}"))
}
CheckKind::MissingReturnTypeSpecialMethod(..) => {
Some("Add `None` return type".to_string())
}
CheckKind::NativeLiterals(literal_type) => {
Some(format!("Replace with `{literal_type}`"))
}
CheckKind::OpenAlias => Some("Replace with builtin `open`".to_string()),
CheckKind::NewLineAfterLastParagraph => {
Some("Move closing quotes to new line".to_string())
}
CheckKind::ReplaceUniversalNewlines => {
Some("Replace with `text` keyword argument".to_string())
}
CheckKind::ReplaceStdoutStderr => {
Some("Replace with `capture_output` keyword argument".to_string())
}
CheckKind::RewriteCElementTree => Some("Replace with `ElementTree`".to_string()),
CheckKind::RewriteUnicodeLiteral => Some("Remove unicode prefix".to_string()),
CheckKind::NewLineAfterSectionName(name) => {
Some(format!("Add newline after \"{name}\""))
}
CheckKind::NoBlankLineBeforeFunction(..) => {
Some("Remove blank line(s) before function docstring".to_string())
}
CheckKind::NoBlankLineAfterFunction(..) => {
Some("Remove blank line(s) after function docstring".to_string())
}
CheckKind::NoBlankLineBeforeClass(..) => {
Some("Remove blank line(s) before class docstring".to_string())
}
CheckKind::OneBlankLineBeforeClass(..) => {
Some("Insert 1 blank line before class docstring".to_string())
}
CheckKind::OneBlankLineAfterClass(..) => {
Some("Insert 1 blank line after class docstring".to_string())
}
CheckKind::NoBlankLinesBetweenHeaderAndContent(..) => {
Some("Remove blank line(s)".to_string())
}
CheckKind::NoNewLineAtEndOfFile => Some("Add trailing newline".to_string()),
CheckKind::NoOverIndentation => Some("Remove over-indentation".to_string()),
CheckKind::NoSurroundingWhitespace => Some("Trim surrounding whitespace".to_string()),
CheckKind::NoUnderIndentation => Some("Increase indentation".to_string()),
CheckKind::NoneComparison(op) => Some(match op {
EqCmpop::Eq => "Replace with `cond is None`".to_string(),
EqCmpop::NotEq => "Replace with `cond is not None`".to_string(),
}),
CheckKind::NotInTest => Some("Convert to `not in`".to_string()),
CheckKind::NotIsTest => Some("Convert to `is not`".to_string()),
CheckKind::PEP3120UnnecessaryCodingComment => {
Some("Remove unnecessary coding comment".to_string())
}
CheckKind::PPrintFound => Some("Remove `pprint`".to_string()),
CheckKind::PercentFormatExtraNamedArguments(missing)
| CheckKind::StringDotFormatExtraNamedArguments(missing) => {
let message = missing.join(", ");
Some(format!("Remove extra named arguments: {message}"))
}
CheckKind::PrintFound => Some("Remove `print`".to_string()),
CheckKind::RaiseNotImplemented => Some("Use `raise NotImplementedError`".to_string()),
CheckKind::RedundantOpenModes(replacement) => Some(match replacement {
None => "Remove open mode parameters".to_string(),
Some(replacement) => {
format!("Replace with \"{replacement}\"")
}
}),
CheckKind::RedundantTupleInExceptionHandler(name) => {
Some(format!("Replace with `except {name}`"))
}
CheckKind::RemoveSixCompat => Some("Remove `six` usage".to_string()),
CheckKind::SectionNameEndsInColon(name) => Some(format!("Add colon to \"{name}\"")),
CheckKind::SectionNotOverIndented(name) => {
Some(format!("Remove over-indentation from \"{name}\""))
}
CheckKind::SectionUnderlineAfterName(name) => {
Some(format!("Add underline to \"{name}\""))
}
CheckKind::SectionUnderlineMatchesSectionLength(name) => {
Some(format!("Adjust underline length to match \"{name}\""))
}
CheckKind::SectionUnderlineNotOverIndented(name) => {
Some(format!("Remove over-indentation from \"{name}\" underline"))
}
CheckKind::SetAttrWithConstant => Some("Replace `setattr` with assignment".to_string()),
CheckKind::SuperCallWithParameters => Some("Remove `__super__` parameters".to_string()),
CheckKind::TrueFalseComparison(true, EqCmpop::Eq) => {
Some("Replace with `cond is True`".to_string())
}
CheckKind::TrueFalseComparison(true, EqCmpop::NotEq) => {
Some("Replace with `cond is not True`".to_string())
}
CheckKind::TrueFalseComparison(false, EqCmpop::Eq) => {
Some("Replace with `cond is False`".to_string())
}
CheckKind::TrueFalseComparison(false, EqCmpop::NotEq) => {
Some("Replace with `cond is not False`".to_string())
}
CheckKind::TypeOfPrimitive(primitive) => Some(format!(
"Replace `type(...)` with `{}`",
primitive.builtin()
)),
CheckKind::TypingTextStrAlias => Some("Replace with `str`".to_string()),
CheckKind::UnnecessaryCallAroundSorted(func) => {
Some(format!("Remove unnecessary `{func}` call"))
}
CheckKind::UnnecessaryCollectionCall(..) => Some("Rewrite as a literal".to_string()),
CheckKind::UnnecessaryComprehension(obj_type) => {
Some(format!("Rewrite using `{obj_type}()`"))
}
CheckKind::UnnecessaryEncodeUTF8 => Some("Remove unnecessary `encode`".to_string()),
CheckKind::UnnecessaryFutureImport(..) => {
Some("Remove unnecessary `__future__` import".to_string())
}
CheckKind::UnnecessaryGeneratorDict => {
Some("Rewrite as a `dict` comprehension".to_string())
}
CheckKind::UnnecessaryGeneratorList => {
Some("Rewrite as a `list` comprehension".to_string())
}
CheckKind::UnnecessaryGeneratorSet => {
Some("Rewrite as a `set` comprehension".to_string())
}
CheckKind::UnnecessaryLRUCacheParams => {
Some("Remove unnecessary parameters".to_string())
}
CheckKind::UnnecessaryListCall => Some("Remove outer `list` call".to_string()),
CheckKind::UnnecessaryListComprehensionDict => {
Some("Rewrite as a `dict` comprehension".to_string())
}
CheckKind::UnnecessaryListComprehensionSet => {
Some("Rewrite as a `set` comprehension".to_string())
}
CheckKind::UnnecessaryLiteralDict(..) => {
Some("Rewrite as a `dict` literal".to_string())
}
CheckKind::UnnecessaryLiteralSet(..) => Some("Rewrite as a `set` literal".to_string()),
CheckKind::UnnecessaryLiteralWithinTupleCall(literal) => Some({
if literal == "list" {
"Rewrite as a `tuple` literal".to_string()
} else {
"Remove outer `tuple` call".to_string()
}
}),
CheckKind::UnnecessaryLiteralWithinListCall(literal) => Some({
if literal == "list" {
"Remove outer `list` call".to_string()
} else {
"Rewrite as a `list` literal".to_string()
}
}),
CheckKind::UnnecessaryReturnNone => Some("Remove explicit `return None`".to_string()),
CheckKind::UnsortedImports => Some("Organize imports".to_string()),
CheckKind::UnusedImport(name, false, multiple) => {
if *multiple {
Some("Remove unused import".to_string())
} else {
Some(format!("Remove unused import: `{name}`"))
}
}
CheckKind::UnusedLoopControlVariable(name) => {
Some(format!("Rename unused `{name}` to `_{name}`"))
}
CheckKind::UnusedNOQA(..) => Some("Remove unused `noqa` directive".to_string()),
CheckKind::UsePEP585Annotation(name) => {
Some(format!("Replace `{name}` with `{}`", name.to_lowercase(),))
}
CheckKind::UsePEP604Annotation => Some("Convert to `X | Y`".to_string()),
CheckKind::UseSysExit(name) => Some(format!("Replace `{name}` with `sys.exit()`")),
CheckKind::UselessImportAlias => Some("Remove import alias".to_string()),
CheckKind::UselessMetaclassType => Some("Remove `__metaclass__ = type`".to_string()),
CheckKind::UselessObjectInheritance(..) => {
Some("Remove `object` inheritance".to_string())
}
_ => None,
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
@ -3361,4 +3641,17 @@ mod tests {
);
}
}
#[test]
fn fixable_codes() {
for check_code in CheckCode::iter() {
let kind = check_code.kind();
if kind.fixable() {
assert!(
kind.commit().is_some(),
"{check_code:?} is fixable but has no commit message."
);
}
}
}
}

View file

@ -2,10 +2,9 @@ use std::path::Path;
use rustpython_ast::Location;
use rustpython_parser::lexer::LexResult;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use wasm_bindgen::prelude::*;
use crate::autofix::Fix;
use crate::checks::CheckCode;
use crate::checks_gen::CheckCodePrefix;
use crate::linter::check_path;
@ -39,6 +38,7 @@ export interface Check {
};
fix: {
content: string;
message: string | null;
location: {
row: number;
column: number;
@ -51,13 +51,21 @@ export interface Check {
};
"#;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Message {
#[derive(Serialize)]
struct ExpandedMessage {
code: CheckCode,
message: String,
location: Location,
end_location: Location,
fix: Option<Fix>,
fix: Option<ExpandedFix>,
}
#[derive(Serialize)]
struct ExpandedFix {
content: String,
message: Option<String>,
location: Location,
end_location: Location,
}
#[wasm_bindgen(start)]
@ -161,14 +169,19 @@ pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
)
.map_err(|e| e.to_string())?;
let messages: Vec<Message> = checks
let messages: Vec<ExpandedMessage> = checks
.into_iter()
.map(|check| Message {
.map(|check| ExpandedMessage {
code: check.kind.code().clone(),
message: check.kind.body(),
location: check.location,
end_location: check.end_location,
fix: check.fix,
fix: check.fix.map(|fix| ExpandedFix {
content: fix.content,
message: check.kind.commit(),
location: fix.location,
end_location: fix.end_location,
}),
})
.collect();
@ -200,7 +213,7 @@ mod test {
check!(
"if (1, 2): pass",
r#"{}"#,
[Message {
[ExpandedMessage {
code: CheckCode::F634,
message: "If test is a tuple, which is always `True`".to_string(),
location: Location::new(1, 0),

View file

@ -10,7 +10,7 @@ use rustpython_parser::ast::Location;
use serde::Serialize;
use serde_json::json;
use crate::autofix::{fixer, Fix};
use crate::autofix::fixer;
use crate::checks::CheckCode;
use crate::fs::relativize_path;
use crate::linter::Diagnostics;
@ -25,11 +25,19 @@ pub enum Violations {
Hide,
}
#[derive(Serialize)]
struct ExpandedFix<'a> {
content: &'a str,
message: Option<String>,
location: &'a Location,
end_location: &'a Location,
}
#[derive(Serialize)]
struct ExpandedMessage<'a> {
code: &'a CheckCode,
message: String,
fix: Option<&'a Fix>,
fix: Option<ExpandedFix<'a>>,
location: Location,
end_location: Location,
filename: &'a str,
@ -127,7 +135,12 @@ impl<'a> Printer<'a> {
.map(|message| ExpandedMessage {
code: message.kind.code(),
message: message.kind.body(),
fix: message.fix.as_ref(),
fix: message.fix.as_ref().map(|fix| ExpandedFix {
content: &fix.content,
location: &fix.location,
end_location: &fix.end_location,
message: message.kind.commit(),
}),
location: message.location,
end_location: message.end_location,
filename: &message.filename,

View file

@ -11,7 +11,7 @@ use crate::ast::types::Range;
use crate::ast::whitespace::leading_space;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckKind, RejectedCmpop};
use crate::checks::{Check, CheckKind};
use crate::source_code_generator::SourceCodeGenerator;
use crate::source_code_style::SourceCodeStyleDetector;
@ -68,7 +68,7 @@ pub fn literal_comparisons(
{
if matches!(op, Cmpop::Eq) {
let check = Check::new(
CheckKind::NoneComparison(RejectedCmpop::Eq),
CheckKind::NoneComparison(op.into()),
Range::from_located(comparator),
);
if checker.patch(check.kind.code()) && !helpers::is_constant_non_singleton(next) {
@ -78,7 +78,7 @@ pub fn literal_comparisons(
}
if matches!(op, Cmpop::NotEq) {
let check = Check::new(
CheckKind::NoneComparison(RejectedCmpop::NotEq),
CheckKind::NoneComparison(op.into()),
Range::from_located(comparator),
);
if checker.patch(check.kind.code()) && !helpers::is_constant_non_singleton(next) {
@ -96,7 +96,7 @@ pub fn literal_comparisons(
{
if matches!(op, Cmpop::Eq) {
let check = Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
CheckKind::TrueFalseComparison(value, op.into()),
Range::from_located(comparator),
);
if checker.patch(check.kind.code()) && !helpers::is_constant_non_singleton(next) {
@ -106,7 +106,7 @@ pub fn literal_comparisons(
}
if matches!(op, Cmpop::NotEq) {
let check = Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
CheckKind::TrueFalseComparison(value, op.into()),
Range::from_located(comparator),
);
if checker.patch(check.kind.code()) && !helpers::is_constant_non_singleton(next) {
@ -130,7 +130,7 @@ pub fn literal_comparisons(
{
if matches!(op, Cmpop::Eq) {
let check = Check::new(
CheckKind::NoneComparison(RejectedCmpop::Eq),
CheckKind::NoneComparison(op.into()),
Range::from_located(next),
);
if checker.patch(check.kind.code())
@ -142,7 +142,7 @@ pub fn literal_comparisons(
}
if matches!(op, Cmpop::NotEq) {
let check = Check::new(
CheckKind::NoneComparison(RejectedCmpop::NotEq),
CheckKind::NoneComparison(op.into()),
Range::from_located(next),
);
if checker.patch(check.kind.code())
@ -162,7 +162,7 @@ pub fn literal_comparisons(
{
if matches!(op, Cmpop::Eq) {
let check = Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
CheckKind::TrueFalseComparison(value, op.into()),
Range::from_located(next),
);
if checker.patch(check.kind.code())
@ -174,7 +174,7 @@ pub fn literal_comparisons(
}
if matches!(op, Cmpop::NotEq) {
let check = Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
CheckKind::TrueFalseComparison(value, op.into()),
Range::from_located(next),
);
if checker.patch(check.kind.code())
@ -311,7 +311,10 @@ fn function(
pub fn do_not_assign_lambda(checker: &mut Checker, target: &Expr, value: &Expr, stmt: &Stmt) {
if let ExprKind::Name { id, .. } = &target.node {
if let ExprKind::Lambda { args, body } = &value.node {
let mut check = Check::new(CheckKind::DoNotAssignLambda, Range::from_located(stmt));
let mut check = Check::new(
CheckKind::DoNotAssignLambda(id.to_string()),
Range::from_located(stmt),
);
if checker.patch(check.kind.code()) {
if !match_leading_content(stmt, checker.locator)
&& !match_trailing_content(stmt, checker.locator)

View file

@ -2,7 +2,8 @@
source: src/pycodestyle/mod.rs
expression: checks
---
- kind: DoNotAssignLambda
- kind:
DoNotAssignLambda: f
location:
row: 2
column: 0
@ -18,7 +19,8 @@ expression: checks
row: 2
column: 19
parent: ~
- kind: DoNotAssignLambda
- kind:
DoNotAssignLambda: f
location:
row: 4
column: 0
@ -34,7 +36,8 @@ expression: checks
row: 4
column: 19
parent: ~
- kind: DoNotAssignLambda
- kind:
DoNotAssignLambda: this
location:
row: 7
column: 4

View file

@ -2,7 +2,8 @@
source: src/pycodestyle/mod.rs
expression: checks
---
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 4
column: 3
@ -18,7 +19,8 @@ expression: checks
row: 4
column: 11
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 6
column: 3
@ -34,7 +36,8 @@ expression: checks
row: 6
column: 11
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 8
column: 3
@ -50,7 +53,8 @@ expression: checks
row: 8
column: 10
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 10
column: 3
@ -66,7 +70,8 @@ expression: checks
row: 10
column: 11
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 12
column: 3

View file

@ -24,7 +24,7 @@ pub fn invalid_literal_comparison(
&& (helpers::is_constant_non_singleton(left)
|| helpers::is_constant_non_singleton(right))
{
let mut check = Check::new(CheckKind::IsLiteral, location);
let mut check = Check::new(CheckKind::IsLiteral(op.into()), location);
if checker.patch(check.kind.code()) {
if let Some(located_op) = &located.get(index) {
assert_eq!(&located_op.node, op);

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- functools
- false
- false
location:
row: 2
column: 7
@ -25,6 +26,7 @@ expression: checks
UnusedImport:
- collections.OrderedDict
- false
- false
location:
row: 6
column: 4
@ -46,6 +48,7 @@ expression: checks
UnusedImport:
- logging.handlers
- false
- false
location:
row: 12
column: 7
@ -65,6 +68,7 @@ expression: checks
UnusedImport:
- shelve
- false
- false
location:
row: 32
column: 11
@ -84,6 +88,7 @@ expression: checks
UnusedImport:
- importlib
- false
- false
location:
row: 33
column: 11
@ -103,6 +108,7 @@ expression: checks
UnusedImport:
- pathlib
- false
- false
location:
row: 37
column: 11
@ -122,6 +128,7 @@ expression: checks
UnusedImport:
- pickle
- false
- false
location:
row: 52
column: 15

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- a.b.c
- false
- false
location:
row: 2
column: 16
@ -25,6 +26,7 @@ expression: checks
UnusedImport:
- d.e.f
- false
- false
location:
row: 3
column: 16
@ -44,6 +46,7 @@ expression: checks
UnusedImport:
- h.i
- false
- false
location:
row: 4
column: 7
@ -63,6 +66,7 @@ expression: checks
UnusedImport:
- j.k
- false
- false
location:
row: 5
column: 7

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- background.BackgroundTasks
- false
- false
location:
row: 7
column: 24
@ -25,6 +26,7 @@ expression: checks
UnusedImport:
- datastructures.UploadFile
- false
- false
location:
row: 10
column: 28
@ -44,6 +46,7 @@ expression: checks
UnusedImport:
- background
- false
- false
location:
row: 17
column: 7
@ -63,6 +66,7 @@ expression: checks
UnusedImport:
- datastructures
- false
- false
location:
row: 20
column: 7

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- typing.Union
- false
- false
location:
row: 30
column: 4
@ -27,6 +28,7 @@ expression: checks
UnusedImport:
- typing.Awaitable
- false
- true
location:
row: 66
column: 19
@ -46,6 +48,7 @@ expression: checks
UnusedImport:
- typing.AwaitableGenerator
- false
- true
location:
row: 66
column: 30

View file

@ -2,7 +2,8 @@
source: src/pyflakes/mod.rs
expression: checks
---
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 1
column: 3
@ -18,7 +19,8 @@ expression: checks
row: 1
column: 7
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: IsNot
location:
row: 4
column: 3
@ -34,7 +36,8 @@ expression: checks
row: 4
column: 13
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: IsNot
location:
row: 7
column: 3
@ -50,7 +53,8 @@ expression: checks
row: 8
column: 11
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 11
column: 3
@ -66,7 +70,8 @@ expression: checks
row: 11
column: 11
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 14
column: 3
@ -82,7 +87,8 @@ expression: checks
row: 14
column: 16
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 17
column: 3
@ -98,7 +104,8 @@ expression: checks
row: 17
column: 18
parent: ~
- kind: IsLiteral
- kind:
IsLiteral: Is
location:
row: 20
column: 13

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- models.Nut
- false
- false
location:
row: 8
column: 4

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 3
column: 11
@ -25,6 +26,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 4
column: 11
@ -44,6 +46,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 7
column: 11
@ -63,6 +66,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 11
column: 11
@ -82,6 +86,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 16
column: 18
@ -101,6 +106,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 21
column: 16
@ -120,6 +126,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 26
column: 17
@ -139,6 +146,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 30
column: 18
@ -158,6 +166,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 31
column: 22
@ -177,6 +186,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 35
column: 15
@ -196,6 +206,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 40
column: 16
@ -215,6 +226,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 46
column: 7
@ -234,6 +246,7 @@ expression: checks
UnusedImport:
- foo
- false
- false
location:
row: 51
column: 7

View file

@ -5,7 +5,7 @@ use rustpython_parser::lexer::Tok;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
use crate::checks::{Check, CheckCode, CheckKind, LiteralType};
/// UP018
pub fn native_literals(
@ -23,7 +23,12 @@ pub fn native_literals(
&& checker.is_builtin(id)
{
let Some(arg) = args.get(0) else {
let mut check = Check::new(CheckKind::NativeLiterals, Range::from_located(expr));
let literal_type = if id == "str" {
LiteralType::Str
} else {
LiteralType::Bytes
};
let mut check = Check::new(CheckKind::NativeLiterals(literal_type), Range::from_located(expr));
if checker.patch(&CheckCode::UP018) {
check.amend(Fix::replacement(
format!("{}\"\"", if id == "bytes" { "b" } else { "" }),
@ -35,15 +40,14 @@ pub fn native_literals(
return;
};
if !matches!(
&arg.node,
ExprKind::Constant {
value: Constant::Str(_) | Constant::Bytes(_),
..
}
) {
let ExprKind::Constant { value, ..} = &arg.node else {
return;
}
};
let literal_type = match value {
Constant::Str { .. } => LiteralType::Str,
Constant::Bytes { .. } => LiteralType::Bytes,
_ => return,
};
// rust-python merges adjacent string/bytes literals into one node, but we can't
// safely remove the outer call in this situation. We're following pyupgrade
@ -60,7 +64,10 @@ pub fn native_literals(
return;
}
let mut check = Check::new(CheckKind::NativeLiterals, Range::from_located(expr));
let mut check = Check::new(
CheckKind::NativeLiterals(literal_type),
Range::from_located(expr),
);
if checker.patch(&CheckCode::UP018) {
check.amend(Fix::replacement(
arg_code.to_string(),

View file

@ -2,10 +2,11 @@ use std::str::FromStr;
use anyhow::{anyhow, Result};
use log::error;
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, KeywordData, Location};
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Location};
use rustpython_parser::lexer;
use rustpython_parser::token::Tok;
use crate::ast::helpers::find_keyword;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
@ -78,7 +79,10 @@ fn create_check(
locator: &SourceCodeLocator,
patch: bool,
) -> Check {
let mut check = Check::new(CheckKind::RedundantOpenModes, Range::from_located(expr));
let mut check = Check::new(
CheckKind::RedundantOpenModes(replacement_value.clone()),
Range::from_located(expr),
);
if patch {
if let Some(content) = replacement_value {
check.amend(Fix::replacement(
@ -153,27 +157,16 @@ pub fn redundant_open_modes(checker: &mut Checker, expr: &Expr) {
}
let (mode_param, keywords): (Option<&Expr>, Vec<Keyword>) = match_open(expr);
if mode_param.is_none() && !keywords.is_empty() {
if let Some(value) = keywords.iter().find_map(|keyword| {
let KeywordData { arg, value } = &keyword.node;
if arg
.as_ref()
.map(|arg| arg == MODE_KEYWORD_ARGUMENT)
.unwrap_or_default()
{
Some(value)
} else {
None
}
}) {
if let Some(keyword) = find_keyword(&keywords, MODE_KEYWORD_ARGUMENT) {
if let ExprKind::Constant {
value: Constant::Str(mode_param_value),
..
} = &value.node
} = &keyword.node.value.node
{
if let Ok(mode) = OpenMode::from_str(mode_param_value.as_str()) {
checker.add_check(create_check(
expr,
value,
&keyword.node.value,
mode.replacement_value(),
checker.locator,
checker.patch(&CheckCode::UP015),

View file

@ -2,7 +2,8 @@
source: src/pyupgrade/mod.rs
expression: checks
---
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 1
column: 0
@ -18,7 +19,8 @@ expression: checks
row: 1
column: 15
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 2
column: 0
@ -34,7 +36,8 @@ expression: checks
row: 2
column: 16
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 3
column: 0
@ -50,7 +53,8 @@ expression: checks
row: 3
column: 16
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 4
column: 0
@ -66,7 +70,8 @@ expression: checks
row: 4
column: 17
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 5
column: 0
@ -82,7 +87,8 @@ expression: checks
row: 5
column: 15
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 6
column: 0
@ -98,7 +104,8 @@ expression: checks
row: 6
column: 16
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 7
column: 0
@ -114,7 +121,8 @@ expression: checks
row: 7
column: 13
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"w\""
location:
row: 8
column: 0
@ -130,7 +138,8 @@ expression: checks
row: 8
column: 14
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 10
column: 5
@ -146,7 +155,8 @@ expression: checks
row: 10
column: 20
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 12
column: 5
@ -162,7 +172,8 @@ expression: checks
row: 12
column: 21
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 14
column: 5
@ -178,7 +189,8 @@ expression: checks
row: 14
column: 21
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 16
column: 5
@ -194,7 +206,8 @@ expression: checks
row: 16
column: 22
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 18
column: 5
@ -210,7 +223,8 @@ expression: checks
row: 18
column: 20
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 20
column: 5
@ -226,7 +240,8 @@ expression: checks
row: 20
column: 21
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 22
column: 5
@ -242,7 +257,8 @@ expression: checks
row: 22
column: 20
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"w\""
location:
row: 24
column: 5
@ -258,7 +274,8 @@ expression: checks
row: 24
column: 21
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 27
column: 0
@ -274,7 +291,8 @@ expression: checks
row: 27
column: 26
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 28
column: 0
@ -290,7 +308,8 @@ expression: checks
row: 28
column: 27
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 30
column: 5
@ -306,7 +325,8 @@ expression: checks
row: 30
column: 31
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 32
column: 5
@ -322,7 +342,8 @@ expression: checks
row: 32
column: 32
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 35
column: 5
@ -338,7 +359,8 @@ expression: checks
row: 35
column: 20
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 35
column: 29
@ -354,7 +376,8 @@ expression: checks
row: 35
column: 44
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 37
column: 5
@ -370,7 +393,8 @@ expression: checks
row: 37
column: 21
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 37
column: 30
@ -386,7 +410,8 @@ expression: checks
row: 37
column: 46
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 40
column: 0
@ -402,7 +427,8 @@ expression: checks
row: 40
column: 20
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 41
column: 0
@ -418,7 +444,8 @@ expression: checks
row: 41
column: 25
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 42
column: 0
@ -434,7 +461,8 @@ expression: checks
row: 42
column: 15
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 44
column: 5
@ -450,7 +478,8 @@ expression: checks
row: 44
column: 25
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 46
column: 5
@ -466,7 +495,8 @@ expression: checks
row: 46
column: 30
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 48
column: 5
@ -482,7 +512,8 @@ expression: checks
row: 48
column: 20
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 51
column: 0
@ -498,7 +529,8 @@ expression: checks
row: 51
column: 21
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 52
column: 0
@ -514,7 +546,8 @@ expression: checks
row: 52
column: 26
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 53
column: 0
@ -530,7 +563,8 @@ expression: checks
row: 53
column: 14
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 55
column: 5
@ -546,7 +580,8 @@ expression: checks
row: 55
column: 26
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 57
column: 5
@ -562,7 +597,8 @@ expression: checks
row: 57
column: 31
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 59
column: 5
@ -578,7 +614,8 @@ expression: checks
row: 59
column: 19
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 62
column: 0
@ -594,7 +631,8 @@ expression: checks
row: 62
column: 25
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 63
column: 0
@ -610,7 +648,8 @@ expression: checks
row: 63
column: 109
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 64
column: 0
@ -626,7 +665,8 @@ expression: checks
row: 64
column: 68
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: ~
location:
row: 65
column: 0
@ -642,7 +682,8 @@ expression: checks
row: 65
column: 15
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 67
column: 0
@ -658,7 +699,8 @@ expression: checks
row: 67
column: 26
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 68
column: 0
@ -674,7 +716,8 @@ expression: checks
row: 68
column: 110
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 69
column: 0
@ -690,7 +733,8 @@ expression: checks
row: 69
column: 69
parent: ~
- kind: RedundantOpenModes
- kind:
RedundantOpenModes: "\"rb\""
location:
row: 70
column: 0

View file

@ -2,7 +2,8 @@
source: src/pyupgrade/mod.rs
expression: checks
---
- kind: NativeLiterals
- kind:
NativeLiterals: Str
location:
row: 18
column: 0
@ -18,7 +19,8 @@ expression: checks
row: 18
column: 5
parent: ~
- kind: NativeLiterals
- kind:
NativeLiterals: Str
location:
row: 19
column: 0
@ -34,7 +36,8 @@ expression: checks
row: 19
column: 10
parent: ~
- kind: NativeLiterals
- kind:
NativeLiterals: Str
location:
row: 20
column: 0
@ -50,7 +53,8 @@ expression: checks
row: 21
column: 7
parent: ~
- kind: NativeLiterals
- kind:
NativeLiterals: Bytes
location:
row: 22
column: 0
@ -66,7 +70,8 @@ expression: checks
row: 22
column: 7
parent: ~
- kind: NativeLiterals
- kind:
NativeLiterals: Bytes
location:
row: 23
column: 0
@ -82,7 +87,8 @@ expression: checks
row: 23
column: 13
parent: ~
- kind: NativeLiterals
- kind:
NativeLiterals: Bytes
location:
row: 24
column: 0

View file

@ -222,6 +222,7 @@ expression: checks
UnusedImport:
- shelve
- false
- false
location:
row: 85
column: 7

View file

@ -6,6 +6,7 @@ expression: checks
UnusedImport:
- typing.Union
- false
- false
location:
row: 28
column: 4
@ -111,6 +112,7 @@ expression: checks
UnusedImport:
- typing.Awaitable
- false
- true
location:
row: 64
column: 19
@ -130,6 +132,7 @@ expression: checks
UnusedImport:
- typing.AwaitableGenerator
- false
- true
location:
row: 64
column: 30

View file

@ -63,6 +63,7 @@ fn test_stdin_json() -> Result<()> {
"message": "`os` imported but unused",
"fix": {
"content": "",
"message": "Remove unused import: `os`",
"location": {
"row": 1,
"column": 0