Merge registry into codes (#4651)

* Document codes.rs

* Refactor codes.rs before merging

Helper script:
```python
# %%

from pathlib import Path

codes = Path("crates/ruff/src/codes.rs").read_text().splitlines()
rules = Path("a.txt").read_text().strip().splitlines()
rule_map = {i.split("::")[-1]: i for i in rules}

# %%

codes_new = []
for line in codes:
    if ", Rule::" in line:
        left, right = line.split(", Rule::")
        right = right[:-2]
        line = left + ", " + rule_map[right] + "),"
    codes_new.append(line)

# %%

Path("crates/ruff/src/codes.rs").write_text("\n".join(codes_new))
```

Co-authored-by: Jonathan Plasse <13716151+JonathanPlasse@users.noreply.github.com>
This commit is contained in:
konstin 2023-06-02 12:33:01 +02:00 committed by GitHub
parent c4fdbf8903
commit 602b4b3519
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 847 additions and 1610 deletions

View file

@ -111,7 +111,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
1. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
1. Map the violation struct to a rule code in `crates/ruff/src/codes.rs` (e.g., `E402`).
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast/mod.rs` (for
AST-based checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks),

File diff suppressed because it is too large Load diff

View file

@ -1,690 +1,16 @@
//! Registry of all [`Rule`] implementations.
//! Remnant of the registry of all [`Rule`] implementations, now it's reexporting from codes.rs
//! with some helper symbols
use strum_macros::{AsRefStr, EnumIter};
use strum_macros::EnumIter;
use ruff_diagnostics::Violation;
pub use codes::Rule;
use ruff_macros::RuleNamespace;
pub use rule_set::{RuleSet, RuleSetIterator};
use crate::codes::{self, RuleCodePrefix};
use crate::rules;
mod rule_set;
ruff_macros::register_rules!(
// pycodestyle errors
rules::pycodestyle::rules::MixedSpacesAndTabs,
rules::pycodestyle::rules::logical_lines::IndentationWithInvalidMultiple,
rules::pycodestyle::rules::logical_lines::NoIndentedBlock,
rules::pycodestyle::rules::logical_lines::UnexpectedIndentation,
rules::pycodestyle::rules::logical_lines::IndentationWithInvalidMultipleComment,
rules::pycodestyle::rules::logical_lines::NoIndentedBlockComment,
rules::pycodestyle::rules::logical_lines::UnexpectedIndentationComment,
rules::pycodestyle::rules::logical_lines::OverIndented,
rules::pycodestyle::rules::logical_lines::WhitespaceAfterOpenBracket,
rules::pycodestyle::rules::logical_lines::WhitespaceBeforeCloseBracket,
rules::pycodestyle::rules::logical_lines::WhitespaceBeforePunctuation,
rules::pycodestyle::rules::logical_lines::MultipleSpacesBeforeOperator,
rules::pycodestyle::rules::logical_lines::MultipleSpacesAfterOperator,
rules::pycodestyle::rules::logical_lines::TabBeforeOperator,
rules::pycodestyle::rules::logical_lines::TabAfterOperator,
rules::pycodestyle::rules::logical_lines::TooFewSpacesBeforeInlineComment,
rules::pycodestyle::rules::logical_lines::NoSpaceAfterInlineComment,
rules::pycodestyle::rules::logical_lines::NoSpaceAfterBlockComment,
rules::pycodestyle::rules::logical_lines::MultipleLeadingHashesForBlockComment,
rules::pycodestyle::rules::logical_lines::MultipleSpacesAfterKeyword,
rules::pycodestyle::rules::logical_lines::MissingWhitespace,
rules::pycodestyle::rules::logical_lines::MissingWhitespaceAfterKeyword,
rules::pycodestyle::rules::logical_lines::MultipleSpacesBeforeKeyword,
rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundOperator,
rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundArithmeticOperator,
rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundBitwiseOrShiftOperator,
rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundModuloOperator,
rules::pycodestyle::rules::logical_lines::TabAfterKeyword,
rules::pycodestyle::rules::logical_lines::UnexpectedSpacesAroundKeywordParameterEquals,
rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundParameterEquals,
rules::pycodestyle::rules::logical_lines::WhitespaceBeforeParameters,
rules::pycodestyle::rules::logical_lines::TabBeforeKeyword,
rules::pycodestyle::rules::MultipleImportsOnOneLine,
rules::pycodestyle::rules::ModuleImportNotAtTopOfFile,
rules::pycodestyle::rules::LineTooLong,
rules::pycodestyle::rules::MultipleStatementsOnOneLineColon,
rules::pycodestyle::rules::MultipleStatementsOnOneLineSemicolon,
rules::pycodestyle::rules::UselessSemicolon,
rules::pycodestyle::rules::NoneComparison,
rules::pycodestyle::rules::TrueFalseComparison,
rules::pycodestyle::rules::NotInTest,
rules::pycodestyle::rules::NotIsTest,
rules::pycodestyle::rules::TypeComparison,
rules::pycodestyle::rules::BareExcept,
rules::pycodestyle::rules::LambdaAssignment,
rules::pycodestyle::rules::AmbiguousVariableName,
rules::pycodestyle::rules::AmbiguousClassName,
rules::pycodestyle::rules::AmbiguousFunctionName,
rules::pycodestyle::rules::IOError,
rules::pycodestyle::rules::SyntaxError,
// pycodestyle warnings
rules::pycodestyle::rules::TabIndentation,
rules::pycodestyle::rules::TrailingWhitespace,
rules::pycodestyle::rules::MissingNewlineAtEndOfFile,
rules::pycodestyle::rules::BlankLineWithWhitespace,
rules::pycodestyle::rules::DocLineTooLong,
rules::pycodestyle::rules::InvalidEscapeSequence,
// pyflakes
rules::pyflakes::rules::UnusedImport,
rules::pyflakes::rules::ImportShadowedByLoopVar,
rules::pyflakes::rules::UndefinedLocalWithImportStar,
rules::pyflakes::rules::LateFutureImport,
rules::pyflakes::rules::UndefinedLocalWithImportStarUsage,
rules::pyflakes::rules::UndefinedLocalWithNestedImportStarUsage,
rules::pyflakes::rules::FutureFeatureNotDefined,
rules::pyflakes::rules::PercentFormatInvalidFormat,
rules::pyflakes::rules::PercentFormatExpectedMapping,
rules::pyflakes::rules::PercentFormatExpectedSequence,
rules::pyflakes::rules::PercentFormatExtraNamedArguments,
rules::pyflakes::rules::PercentFormatMissingArgument,
rules::pyflakes::rules::PercentFormatMixedPositionalAndNamed,
rules::pyflakes::rules::PercentFormatPositionalCountMismatch,
rules::pyflakes::rules::PercentFormatStarRequiresSequence,
rules::pyflakes::rules::PercentFormatUnsupportedFormatCharacter,
rules::pyflakes::rules::StringDotFormatInvalidFormat,
rules::pyflakes::rules::StringDotFormatExtraNamedArguments,
rules::pyflakes::rules::StringDotFormatExtraPositionalArguments,
rules::pyflakes::rules::StringDotFormatMissingArguments,
rules::pyflakes::rules::StringDotFormatMixingAutomatic,
rules::pyflakes::rules::FStringMissingPlaceholders,
rules::pyflakes::rules::MultiValueRepeatedKeyLiteral,
rules::pyflakes::rules::MultiValueRepeatedKeyVariable,
rules::pyflakes::rules::ExpressionsInStarAssignment,
rules::pyflakes::rules::MultipleStarredExpressions,
rules::pyflakes::rules::AssertTuple,
rules::pyflakes::rules::IsLiteral,
rules::pyflakes::rules::InvalidPrintSyntax,
rules::pyflakes::rules::IfTuple,
rules::pyflakes::rules::BreakOutsideLoop,
rules::pyflakes::rules::ContinueOutsideLoop,
rules::pyflakes::rules::YieldOutsideFunction,
rules::pyflakes::rules::ReturnOutsideFunction,
rules::pyflakes::rules::DefaultExceptNotLast,
rules::pyflakes::rules::ForwardAnnotationSyntaxError,
rules::pyflakes::rules::RedefinedWhileUnused,
rules::pyflakes::rules::UndefinedName,
rules::pyflakes::rules::UndefinedExport,
rules::pyflakes::rules::UndefinedLocal,
rules::pyflakes::rules::UnusedVariable,
rules::pyflakes::rules::UnusedAnnotation,
rules::pyflakes::rules::RaiseNotImplemented,
// pylint
rules::pylint::rules::AssertOnStringLiteral,
rules::pylint::rules::UselessReturn,
rules::pylint::rules::YieldFromInAsyncFunction,
rules::pylint::rules::YieldInInit,
rules::pylint::rules::InvalidAllObject,
rules::pylint::rules::InvalidAllFormat,
rules::pylint::rules::InvalidEnvvarDefault,
rules::pylint::rules::InvalidEnvvarValue,
rules::pylint::rules::BadStringFormatType,
rules::pylint::rules::BidirectionalUnicode,
rules::pylint::rules::BinaryOpException,
rules::pylint::rules::ImportSelf,
rules::pylint::rules::InvalidCharacterBackspace,
rules::pylint::rules::InvalidCharacterSub,
rules::pylint::rules::InvalidCharacterEsc,
rules::pylint::rules::InvalidCharacterNul,
rules::pylint::rules::InvalidCharacterZeroWidthSpace,
rules::pylint::rules::BadStrStripCall,
rules::pylint::rules::CollapsibleElseIf,
rules::pylint::rules::ContinueInFinally,
rules::pylint::rules::UselessImportAlias,
rules::pylint::rules::UnnecessaryDirectLambdaCall,
rules::pylint::rules::NonlocalWithoutBinding,
rules::pylint::rules::LoadBeforeGlobalDeclaration,
rules::pylint::rules::AwaitOutsideAsync,
rules::pylint::rules::PropertyWithParameters,
rules::pylint::rules::ReturnInInit,
rules::pylint::rules::ManualFromImport,
rules::pylint::rules::CompareToEmptyString,
rules::pylint::rules::ComparisonOfConstant,
rules::pylint::rules::RepeatedIsinstanceCalls,
rules::pylint::rules::SysExitAlias,
rules::pylint::rules::MagicValueComparison,
rules::pylint::rules::UselessElseOnLoop,
rules::pylint::rules::GlobalStatement,
rules::pylint::rules::GlobalVariableNotAssigned,
rules::pylint::rules::TooManyReturnStatements,
rules::pylint::rules::TooManyArguments,
rules::pylint::rules::TooManyBranches,
rules::pylint::rules::TooManyStatements,
rules::pylint::rules::RedefinedLoopName,
rules::pylint::rules::LoggingTooFewArgs,
rules::pylint::rules::LoggingTooManyArgs,
rules::pylint::rules::UnexpectedSpecialMethodSignature,
rules::pylint::rules::NestedMinMax,
rules::pylint::rules::DuplicateValue,
rules::pylint::rules::DuplicateBases,
rules::pylint::rules::NamedExprWithoutContext,
rules::pylint::rules::IterationOverSet,
// flake8-async
rules::flake8_async::rules::BlockingHttpCallInAsyncFunction,
rules::flake8_async::rules::OpenSleepOrSubprocessInAsyncFunction,
rules::flake8_async::rules::BlockingOsCallInAsyncFunction,
// flake8-builtins
rules::flake8_builtins::rules::BuiltinVariableShadowing,
rules::flake8_builtins::rules::BuiltinArgumentShadowing,
rules::flake8_builtins::rules::BuiltinAttributeShadowing,
// flake8-bugbear
rules::flake8_bugbear::rules::UnaryPrefixIncrement,
rules::flake8_bugbear::rules::AssignmentToOsEnviron,
rules::flake8_bugbear::rules::UnreliableCallableCheck,
rules::flake8_bugbear::rules::StripWithMultiCharacters,
rules::flake8_bugbear::rules::MutableArgumentDefault,
rules::flake8_bugbear::rules::NoExplicitStacklevel,
rules::flake8_bugbear::rules::UnusedLoopControlVariable,
rules::flake8_bugbear::rules::FunctionCallInDefaultArgument,
rules::flake8_bugbear::rules::GetAttrWithConstant,
rules::flake8_bugbear::rules::SetAttrWithConstant,
rules::flake8_bugbear::rules::AssertFalse,
rules::flake8_bugbear::rules::JumpStatementInFinally,
rules::flake8_bugbear::rules::RedundantTupleInExceptionHandler,
rules::flake8_bugbear::rules::DuplicateHandlerException,
rules::flake8_bugbear::rules::UselessComparison,
rules::flake8_bugbear::rules::CannotRaiseLiteral,
rules::flake8_bugbear::rules::AssertRaisesException,
rules::flake8_bugbear::rules::UselessExpression,
rules::flake8_bugbear::rules::CachedInstanceMethod,
rules::flake8_bugbear::rules::LoopVariableOverridesIterator,
rules::flake8_bugbear::rules::FStringDocstring,
rules::flake8_bugbear::rules::UselessContextlibSuppress,
rules::flake8_bugbear::rules::FunctionUsesLoopVariable,
rules::flake8_bugbear::rules::AbstractBaseClassWithoutAbstractMethod,
rules::flake8_bugbear::rules::DuplicateTryBlockException,
rules::flake8_bugbear::rules::StarArgUnpackingAfterKeywordArg,
rules::flake8_bugbear::rules::EmptyMethodWithoutAbstractDecorator,
rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept,
rules::flake8_bugbear::rules::ZipWithoutExplicitStrict,
rules::flake8_bugbear::rules::ExceptWithEmptyTuple,
rules::flake8_bugbear::rules::ExceptWithNonExceptionClasses,
rules::flake8_bugbear::rules::ReuseOfGroupbyGenerator,
rules::flake8_bugbear::rules::UnintentionalTypeAnnotation,
// flake8-blind-except
rules::flake8_blind_except::rules::BlindExcept,
// flake8-comprehensions
rules::flake8_comprehensions::rules::UnnecessaryCallAroundSorted,
rules::flake8_comprehensions::rules::UnnecessaryCollectionCall,
rules::flake8_comprehensions::rules::UnnecessaryComprehension,
rules::flake8_comprehensions::rules::UnnecessaryComprehensionAnyAll,
rules::flake8_comprehensions::rules::UnnecessaryDoubleCastOrProcess,
rules::flake8_comprehensions::rules::UnnecessaryGeneratorDict,
rules::flake8_comprehensions::rules::UnnecessaryGeneratorList,
rules::flake8_comprehensions::rules::UnnecessaryGeneratorSet,
rules::flake8_comprehensions::rules::UnnecessaryListCall,
rules::flake8_comprehensions::rules::UnnecessaryListComprehensionDict,
rules::flake8_comprehensions::rules::UnnecessaryListComprehensionSet,
rules::flake8_comprehensions::rules::UnnecessaryLiteralDict,
rules::flake8_comprehensions::rules::UnnecessaryLiteralSet,
rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinDictCall,
rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinListCall,
rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinTupleCall,
rules::flake8_comprehensions::rules::UnnecessaryMap,
rules::flake8_comprehensions::rules::UnnecessarySubscriptReversal,
// flake8-debugger
rules::flake8_debugger::rules::Debugger,
// mccabe
rules::mccabe::rules::ComplexStructure,
// flake8-tidy-imports
rules::flake8_tidy_imports::rules::BannedApi,
rules::flake8_tidy_imports::rules::RelativeImports,
// flake8-return
rules::flake8_return::rules::UnnecessaryReturnNone,
rules::flake8_return::rules::ImplicitReturnValue,
rules::flake8_return::rules::ImplicitReturn,
rules::flake8_return::rules::UnnecessaryAssign,
rules::flake8_return::rules::SuperfluousElseReturn,
rules::flake8_return::rules::SuperfluousElseRaise,
rules::flake8_return::rules::SuperfluousElseContinue,
rules::flake8_return::rules::SuperfluousElseBreak,
// flake8-implicit-str-concat
rules::flake8_implicit_str_concat::rules::SingleLineImplicitStringConcatenation,
rules::flake8_implicit_str_concat::rules::MultiLineImplicitStringConcatenation,
rules::flake8_implicit_str_concat::rules::ExplicitStringConcatenation,
// flake8-print
rules::flake8_print::rules::Print,
rules::flake8_print::rules::PPrint,
// flake8-quotes
rules::flake8_quotes::rules::BadQuotesInlineString,
rules::flake8_quotes::rules::BadQuotesMultilineString,
rules::flake8_quotes::rules::BadQuotesDocstring,
rules::flake8_quotes::rules::AvoidableEscapedQuote,
// flake8-annotations
rules::flake8_annotations::rules::MissingTypeFunctionArgument,
rules::flake8_annotations::rules::MissingTypeArgs,
rules::flake8_annotations::rules::MissingTypeKwargs,
rules::flake8_annotations::rules::MissingTypeSelf,
rules::flake8_annotations::rules::MissingTypeCls,
rules::flake8_annotations::rules::MissingReturnTypeUndocumentedPublicFunction,
rules::flake8_annotations::rules::MissingReturnTypePrivateFunction,
rules::flake8_annotations::rules::MissingReturnTypeSpecialMethod,
rules::flake8_annotations::rules::MissingReturnTypeStaticMethod,
rules::flake8_annotations::rules::MissingReturnTypeClassMethod,
rules::flake8_annotations::rules::AnyType,
// flake8-future-annotations
rules::flake8_future_annotations::rules::FutureRewritableTypeAnnotation,
rules::flake8_future_annotations::rules::FutureRequiredTypeAnnotation,
// flake8-2020
rules::flake8_2020::rules::SysVersionSlice3,
rules::flake8_2020::rules::SysVersion2,
rules::flake8_2020::rules::SysVersionCmpStr3,
rules::flake8_2020::rules::SysVersionInfo0Eq3,
rules::flake8_2020::rules::SixPY3,
rules::flake8_2020::rules::SysVersionInfo1CmpInt,
rules::flake8_2020::rules::SysVersionInfoMinorCmpInt,
rules::flake8_2020::rules::SysVersion0,
rules::flake8_2020::rules::SysVersionCmpStr10,
rules::flake8_2020::rules::SysVersionSlice1,
// flake8-simplify
rules::flake8_simplify::rules::IfElseBlockInsteadOfDictLookup,
rules::flake8_simplify::rules::DuplicateIsinstanceCall,
rules::flake8_simplify::rules::CollapsibleIf,
rules::flake8_simplify::rules::NeedlessBool,
rules::flake8_simplify::rules::SuppressibleException,
rules::flake8_simplify::rules::ReturnInTryExceptFinally,
rules::flake8_simplify::rules::IfElseBlockInsteadOfIfExp,
rules::flake8_simplify::rules::CompareWithTuple,
rules::flake8_simplify::rules::ReimplementedBuiltin,
rules::flake8_simplify::rules::UncapitalizedEnvironmentVariables,
rules::flake8_simplify::rules::IfWithSameArms,
rules::flake8_simplify::rules::OpenFileWithContextHandler,
rules::flake8_simplify::rules::MultipleWithStatements,
rules::flake8_simplify::rules::InDictKeys,
rules::flake8_simplify::rules::NegateEqualOp,
rules::flake8_simplify::rules::NegateNotEqualOp,
rules::flake8_simplify::rules::DoubleNegation,
rules::flake8_simplify::rules::IfExprWithTrueFalse,
rules::flake8_simplify::rules::IfExprWithFalseTrue,
rules::flake8_simplify::rules::IfExprWithTwistedArms,
rules::flake8_simplify::rules::ExprAndNotExpr,
rules::flake8_simplify::rules::ExprOrNotExpr,
rules::flake8_simplify::rules::ExprOrTrue,
rules::flake8_simplify::rules::ExprAndFalse,
rules::flake8_simplify::rules::YodaConditions,
rules::flake8_simplify::rules::IfElseBlockInsteadOfDictGet,
rules::flake8_simplify::rules::DictGetWithNoneDefault,
// pyupgrade
rules::pyupgrade::rules::UselessMetaclassType,
rules::pyupgrade::rules::TypeOfPrimitive,
rules::pyupgrade::rules::UselessObjectInheritance,
rules::pyupgrade::rules::DeprecatedUnittestAlias,
rules::pyupgrade::rules::NonPEP585Annotation,
rules::pyupgrade::rules::NonPEP604Annotation,
rules::pyupgrade::rules::SuperCallWithParameters,
rules::pyupgrade::rules::UTF8EncodingDeclaration,
rules::pyupgrade::rules::UnnecessaryFutureImport,
rules::pyupgrade::rules::LRUCacheWithoutParameters,
rules::pyupgrade::rules::UnnecessaryEncodeUTF8,
rules::pyupgrade::rules::ConvertTypedDictFunctionalToClass,
rules::pyupgrade::rules::ConvertNamedTupleFunctionalToClass,
rules::pyupgrade::rules::RedundantOpenModes,
rules::pyupgrade::rules::DatetimeTimezoneUTC,
rules::pyupgrade::rules::NativeLiterals,
rules::pyupgrade::rules::TypingTextStrAlias,
rules::pyupgrade::rules::OpenAlias,
rules::pyupgrade::rules::ReplaceUniversalNewlines,
rules::pyupgrade::rules::ReplaceStdoutStderr,
rules::pyupgrade::rules::DeprecatedCElementTree,
rules::pyupgrade::rules::OSErrorAlias,
rules::pyupgrade::rules::UnicodeKindPrefix,
rules::pyupgrade::rules::DeprecatedMockImport,
rules::pyupgrade::rules::UnpackedListComprehension,
rules::pyupgrade::rules::YieldInForLoop,
rules::pyupgrade::rules::UnnecessaryBuiltinImport,
rules::pyupgrade::rules::FormatLiterals,
rules::pyupgrade::rules::PrintfStringFormatting,
rules::pyupgrade::rules::FString,
rules::pyupgrade::rules::LRUCacheWithMaxsizeNone,
rules::pyupgrade::rules::ExtraneousParentheses,
rules::pyupgrade::rules::DeprecatedImport,
rules::pyupgrade::rules::OutdatedVersionBlock,
rules::pyupgrade::rules::QuotedAnnotation,
rules::pyupgrade::rules::NonPEP604Isinstance,
// pydocstyle
rules::pydocstyle::rules::UndocumentedPublicModule,
rules::pydocstyle::rules::UndocumentedPublicClass,
rules::pydocstyle::rules::UndocumentedPublicMethod,
rules::pydocstyle::rules::UndocumentedPublicFunction,
rules::pydocstyle::rules::UndocumentedPublicPackage,
rules::pydocstyle::rules::UndocumentedMagicMethod,
rules::pydocstyle::rules::UndocumentedPublicNestedClass,
rules::pydocstyle::rules::UndocumentedPublicInit,
rules::pydocstyle::rules::FitsOnOneLine,
rules::pydocstyle::rules::NoBlankLineBeforeFunction,
rules::pydocstyle::rules::NoBlankLineAfterFunction,
rules::pydocstyle::rules::OneBlankLineBeforeClass,
rules::pydocstyle::rules::OneBlankLineAfterClass,
rules::pydocstyle::rules::BlankLineAfterSummary,
rules::pydocstyle::rules::IndentWithSpaces,
rules::pydocstyle::rules::UnderIndentation,
rules::pydocstyle::rules::OverIndentation,
rules::pydocstyle::rules::NewLineAfterLastParagraph,
rules::pydocstyle::rules::SurroundingWhitespace,
rules::pydocstyle::rules::BlankLineBeforeClass,
rules::pydocstyle::rules::MultiLineSummaryFirstLine,
rules::pydocstyle::rules::MultiLineSummarySecondLine,
rules::pydocstyle::rules::SectionNotOverIndented,
rules::pydocstyle::rules::SectionUnderlineNotOverIndented,
rules::pydocstyle::rules::TripleSingleQuotes,
rules::pydocstyle::rules::EscapeSequenceInDocstring,
rules::pydocstyle::rules::EndsInPeriod,
rules::pydocstyle::rules::NonImperativeMood,
rules::pydocstyle::rules::NoSignature,
rules::pydocstyle::rules::FirstLineCapitalized,
rules::pydocstyle::rules::DocstringStartsWithThis,
rules::pydocstyle::rules::CapitalizeSectionName,
rules::pydocstyle::rules::NewLineAfterSectionName,
rules::pydocstyle::rules::DashedUnderlineAfterSection,
rules::pydocstyle::rules::SectionUnderlineAfterName,
rules::pydocstyle::rules::SectionUnderlineMatchesSectionLength,
rules::pydocstyle::rules::NoBlankLineAfterSection,
rules::pydocstyle::rules::NoBlankLineBeforeSection,
rules::pydocstyle::rules::BlankLinesBetweenHeaderAndContent,
rules::pydocstyle::rules::BlankLineAfterLastSection,
rules::pydocstyle::rules::EmptyDocstringSection,
rules::pydocstyle::rules::EndsInPunctuation,
rules::pydocstyle::rules::SectionNameEndsInColon,
rules::pydocstyle::rules::UndocumentedParam,
rules::pydocstyle::rules::OverloadWithDocstring,
rules::pydocstyle::rules::EmptyDocstring,
// pep8-naming
rules::pep8_naming::rules::InvalidClassName,
rules::pep8_naming::rules::InvalidFunctionName,
rules::pep8_naming::rules::InvalidArgumentName,
rules::pep8_naming::rules::InvalidFirstArgumentNameForClassMethod,
rules::pep8_naming::rules::InvalidFirstArgumentNameForMethod,
rules::pep8_naming::rules::NonLowercaseVariableInFunction,
rules::pep8_naming::rules::DunderFunctionName,
rules::pep8_naming::rules::ConstantImportedAsNonConstant,
rules::pep8_naming::rules::LowercaseImportedAsNonLowercase,
rules::pep8_naming::rules::CamelcaseImportedAsLowercase,
rules::pep8_naming::rules::CamelcaseImportedAsConstant,
rules::pep8_naming::rules::MixedCaseVariableInClassScope,
rules::pep8_naming::rules::MixedCaseVariableInGlobalScope,
rules::pep8_naming::rules::CamelcaseImportedAsAcronym,
rules::pep8_naming::rules::ErrorSuffixOnExceptionName,
rules::pep8_naming::rules::InvalidModuleName,
// isort
rules::isort::rules::UnsortedImports,
rules::isort::rules::MissingRequiredImport,
// eradicate
rules::eradicate::rules::CommentedOutCode,
// flake8-bandit
rules::flake8_bandit::rules::Assert,
rules::flake8_bandit::rules::BadFilePermissions,
rules::flake8_bandit::rules::ExecBuiltin,
rules::flake8_bandit::rules::HardcodedBindAllInterfaces,
rules::flake8_bandit::rules::HardcodedPasswordDefault,
rules::flake8_bandit::rules::HardcodedPasswordFuncArg,
rules::flake8_bandit::rules::HardcodedPasswordString,
rules::flake8_bandit::rules::HardcodedSQLExpression,
rules::flake8_bandit::rules::HardcodedTempFile,
rules::flake8_bandit::rules::HashlibInsecureHashFunction,
rules::flake8_bandit::rules::Jinja2AutoescapeFalse,
rules::flake8_bandit::rules::ParamikoCall,
rules::flake8_bandit::rules::LoggingConfigInsecureListen,
rules::flake8_bandit::rules::RequestWithNoCertValidation,
rules::flake8_bandit::rules::RequestWithoutTimeout,
rules::flake8_bandit::rules::SnmpInsecureVersion,
rules::flake8_bandit::rules::SnmpWeakCryptography,
rules::flake8_bandit::rules::SubprocessPopenWithShellEqualsTrue,
rules::flake8_bandit::rules::SubprocessWithoutShellEqualsTrue,
rules::flake8_bandit::rules::CallWithShellEqualsTrue,
rules::flake8_bandit::rules::StartProcessWithAShell,
rules::flake8_bandit::rules::StartProcessWithNoShell,
rules::flake8_bandit::rules::StartProcessWithPartialPath,
rules::flake8_bandit::rules::SuspiciousEvalUsage,
rules::flake8_bandit::rules::SuspiciousFTPLibUsage,
rules::flake8_bandit::rules::SuspiciousInsecureCipherUsage,
rules::flake8_bandit::rules::SuspiciousInsecureCipherModeUsage,
rules::flake8_bandit::rules::SuspiciousInsecureHashUsage,
rules::flake8_bandit::rules::SuspiciousMarkSafeUsage,
rules::flake8_bandit::rules::SuspiciousMarshalUsage,
rules::flake8_bandit::rules::SuspiciousMktempUsage,
rules::flake8_bandit::rules::SuspiciousNonCryptographicRandomUsage,
rules::flake8_bandit::rules::SuspiciousPickleUsage,
rules::flake8_bandit::rules::SuspiciousTelnetUsage,
rules::flake8_bandit::rules::SuspiciousURLOpenUsage,
rules::flake8_bandit::rules::SuspiciousUnverifiedContextUsage,
rules::flake8_bandit::rules::SuspiciousXMLCElementTreeUsage,
rules::flake8_bandit::rules::SuspiciousXMLETreeUsage,
rules::flake8_bandit::rules::SuspiciousXMLElementTreeUsage,
rules::flake8_bandit::rules::SuspiciousXMLExpatBuilderUsage,
rules::flake8_bandit::rules::SuspiciousXMLExpatReaderUsage,
rules::flake8_bandit::rules::SuspiciousXMLMiniDOMUsage,
rules::flake8_bandit::rules::SuspiciousXMLPullDOMUsage,
rules::flake8_bandit::rules::SuspiciousXMLSaxUsage,
rules::flake8_bandit::rules::TryExceptContinue,
rules::flake8_bandit::rules::TryExceptPass,
rules::flake8_bandit::rules::UnsafeYAMLLoad,
// flake8-boolean-trap
rules::flake8_boolean_trap::rules::BooleanPositionalArgInFunctionDefinition,
rules::flake8_boolean_trap::rules::BooleanDefaultValueInFunctionDefinition,
rules::flake8_boolean_trap::rules::BooleanPositionalValueInFunctionCall,
// flake8-unused-arguments
rules::flake8_unused_arguments::rules::UnusedFunctionArgument,
rules::flake8_unused_arguments::rules::UnusedMethodArgument,
rules::flake8_unused_arguments::rules::UnusedClassMethodArgument,
rules::flake8_unused_arguments::rules::UnusedStaticMethodArgument,
rules::flake8_unused_arguments::rules::UnusedLambdaArgument,
// flake8-import-conventions
rules::flake8_import_conventions::rules::UnconventionalImportAlias,
rules::flake8_import_conventions::rules::BannedImportAlias,
rules::flake8_import_conventions::rules::BannedImportFrom,
// flake8-datetimez
rules::flake8_datetimez::rules::CallDatetimeWithoutTzinfo,
rules::flake8_datetimez::rules::CallDatetimeToday,
rules::flake8_datetimez::rules::CallDatetimeUtcnow,
rules::flake8_datetimez::rules::CallDatetimeUtcfromtimestamp,
rules::flake8_datetimez::rules::CallDatetimeNowWithoutTzinfo,
rules::flake8_datetimez::rules::CallDatetimeFromtimestamp,
rules::flake8_datetimez::rules::CallDatetimeStrptimeWithoutZone,
rules::flake8_datetimez::rules::CallDateToday,
rules::flake8_datetimez::rules::CallDateFromtimestamp,
// pygrep-hooks
rules::pygrep_hooks::rules::BlanketNOQA,
rules::pygrep_hooks::rules::BlanketTypeIgnore,
rules::pygrep_hooks::rules::DeprecatedLogWarn,
rules::pygrep_hooks::rules::Eval,
rules::pygrep_hooks::rules::InvalidMockAccess,
// pandas-vet
rules::pandas_vet::rules::PandasUseOfInplaceArgument,
rules::pandas_vet::rules::PandasUseOfDotIsNull,
rules::pandas_vet::rules::PandasUseOfDotNotNull,
rules::pandas_vet::rules::PandasUseOfDotIx,
rules::pandas_vet::rules::PandasUseOfDotAt,
rules::pandas_vet::rules::PandasUseOfDotIat,
rules::pandas_vet::rules::PandasUseOfDotPivotOrUnstack,
rules::pandas_vet::rules::PandasUseOfDotValues,
rules::pandas_vet::rules::PandasUseOfDotReadTable,
rules::pandas_vet::rules::PandasUseOfDotStack,
rules::pandas_vet::rules::PandasUseOfPdMerge,
rules::pandas_vet::rules::PandasDfVariableName,
// flake8-errmsg
rules::flake8_errmsg::rules::RawStringInException,
rules::flake8_errmsg::rules::FStringInException,
rules::flake8_errmsg::rules::DotFormatInException,
// flake8-pyi
rules::flake8_pyi::rules::AnyEqNeAnnotation,
rules::flake8_pyi::rules::ArgumentDefaultInStub,
rules::flake8_pyi::rules::AssignmentDefaultInStub,
rules::flake8_pyi::rules::BadVersionInfoComparison,
rules::flake8_pyi::rules::DocstringInStub,
rules::flake8_pyi::rules::IterMethodReturnIterable,
rules::flake8_pyi::rules::DuplicateUnionMember,
rules::flake8_pyi::rules::EllipsisInNonEmptyClassBody,
rules::flake8_pyi::rules::NonSelfReturnType,
rules::flake8_pyi::rules::CollectionsNamedTuple,
rules::flake8_pyi::rules::StringOrBytesTooLong,
rules::flake8_pyi::rules::NonEmptyStubBody,
rules::flake8_pyi::rules::PassInClassBody,
rules::flake8_pyi::rules::PassStatementStubBody,
rules::flake8_pyi::rules::NumericLiteralTooLong,
rules::flake8_pyi::rules::QuotedAnnotationInStub,
rules::flake8_pyi::rules::SnakeCaseTypeAlias,
rules::flake8_pyi::rules::StubBodyMultipleStatements,
rules::flake8_pyi::rules::TSuffixedTypeAlias,
rules::flake8_pyi::rules::TypeCommentInStub,
rules::flake8_pyi::rules::TypedArgumentDefaultInStub,
rules::flake8_pyi::rules::UnaliasedCollectionsAbcSetImport,
rules::flake8_pyi::rules::UnannotatedAssignmentInStub,
rules::flake8_pyi::rules::UnprefixedTypeParam,
rules::flake8_pyi::rules::UnrecognizedPlatformCheck,
rules::flake8_pyi::rules::UnrecognizedPlatformName,
// flake8-pytest-style
rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle,
rules::flake8_pytest_style::rules::PytestFixturePositionalArgs,
rules::flake8_pytest_style::rules::PytestExtraneousScopeFunction,
rules::flake8_pytest_style::rules::PytestMissingFixtureNameUnderscore,
rules::flake8_pytest_style::rules::PytestIncorrectFixtureNameUnderscore,
rules::flake8_pytest_style::rules::PytestParametrizeNamesWrongType,
rules::flake8_pytest_style::rules::PytestParametrizeValuesWrongType,
rules::flake8_pytest_style::rules::PytestPatchWithLambda,
rules::flake8_pytest_style::rules::PytestUnittestAssertion,
rules::flake8_pytest_style::rules::PytestRaisesWithoutException,
rules::flake8_pytest_style::rules::PytestRaisesTooBroad,
rules::flake8_pytest_style::rules::PytestRaisesWithMultipleStatements,
rules::flake8_pytest_style::rules::PytestIncorrectPytestImport,
rules::flake8_pytest_style::rules::PytestAssertAlwaysFalse,
rules::flake8_pytest_style::rules::PytestFailWithoutMessage,
rules::flake8_pytest_style::rules::PytestAssertInExcept,
rules::flake8_pytest_style::rules::PytestCompositeAssertion,
rules::flake8_pytest_style::rules::PytestFixtureParamWithoutValue,
rules::flake8_pytest_style::rules::PytestDeprecatedYieldFixture,
rules::flake8_pytest_style::rules::PytestFixtureFinalizerCallback,
rules::flake8_pytest_style::rules::PytestUselessYieldFixture,
rules::flake8_pytest_style::rules::PytestIncorrectMarkParenthesesStyle,
rules::flake8_pytest_style::rules::PytestUnnecessaryAsyncioMarkOnFixture,
rules::flake8_pytest_style::rules::PytestErroneousUseFixturesOnFixture,
rules::flake8_pytest_style::rules::PytestUseFixturesWithoutParameters,
// flake8-pie
rules::flake8_pie::rules::UnnecessaryPass,
rules::flake8_pie::rules::DuplicateClassFieldDefinition,
rules::flake8_pie::rules::NonUniqueEnums,
rules::flake8_pie::rules::UnnecessarySpread,
rules::flake8_pie::rules::UnnecessaryDictKwargs,
rules::flake8_pie::rules::ReimplementedListBuiltin,
rules::flake8_pie::rules::MultipleStartsEndsWith,
// flake8-commas
rules::flake8_commas::rules::MissingTrailingComma,
rules::flake8_commas::rules::TrailingCommaOnBareTuple,
rules::flake8_commas::rules::ProhibitedTrailingComma,
// flake8-no-pep420
rules::flake8_no_pep420::rules::ImplicitNamespacePackage,
// flake8-executable
rules::flake8_executable::rules::ShebangNotExecutable,
rules::flake8_executable::rules::ShebangMissingExecutableFile,
rules::flake8_executable::rules::ShebangMissingPython,
rules::flake8_executable::rules::ShebangLeadingWhitespace,
rules::flake8_executable::rules::ShebangNotFirstLine,
// flake8-type-checking
rules::flake8_type_checking::rules::TypingOnlyFirstPartyImport,
rules::flake8_type_checking::rules::TypingOnlyThirdPartyImport,
rules::flake8_type_checking::rules::TypingOnlyStandardLibraryImport,
rules::flake8_type_checking::rules::RuntimeImportInTypeCheckingBlock,
rules::flake8_type_checking::rules::EmptyTypeCheckingBlock,
// tryceratops
rules::tryceratops::rules::RaiseVanillaClass,
rules::tryceratops::rules::RaiseVanillaArgs,
rules::tryceratops::rules::TypeCheckWithoutTypeError,
rules::tryceratops::rules::ReraiseNoCause,
rules::tryceratops::rules::VerboseRaise,
rules::tryceratops::rules::TryConsiderElse,
rules::tryceratops::rules::UselessTryExcept,
rules::tryceratops::rules::RaiseWithinTry,
rules::tryceratops::rules::ErrorInsteadOfException,
rules::tryceratops::rules::VerboseLogMessage,
// flake8-use-pathlib
rules::flake8_use_pathlib::violations::OsPathAbspath,
rules::flake8_use_pathlib::violations::OsChmod,
rules::flake8_use_pathlib::violations::OsMkdir,
rules::flake8_use_pathlib::violations::OsMakedirs,
rules::flake8_use_pathlib::violations::OsRename,
rules::flake8_use_pathlib::violations::PathlibReplace,
rules::flake8_use_pathlib::violations::OsRmdir,
rules::flake8_use_pathlib::violations::OsRemove,
rules::flake8_use_pathlib::violations::OsUnlink,
rules::flake8_use_pathlib::violations::OsGetcwd,
rules::flake8_use_pathlib::violations::OsPathExists,
rules::flake8_use_pathlib::violations::OsPathExpanduser,
rules::flake8_use_pathlib::violations::OsPathIsdir,
rules::flake8_use_pathlib::violations::OsPathIsfile,
rules::flake8_use_pathlib::violations::OsPathIslink,
rules::flake8_use_pathlib::violations::OsReadlink,
rules::flake8_use_pathlib::violations::OsStat,
rules::flake8_use_pathlib::violations::OsPathIsabs,
rules::flake8_use_pathlib::violations::OsPathJoin,
rules::flake8_use_pathlib::violations::OsPathBasename,
rules::flake8_use_pathlib::violations::OsPathDirname,
rules::flake8_use_pathlib::violations::OsPathSamefile,
rules::flake8_use_pathlib::violations::OsPathSplitext,
rules::flake8_use_pathlib::violations::BuiltinOpen,
rules::flake8_use_pathlib::violations::PyPath,
// flake8-logging-format
rules::flake8_logging_format::violations::LoggingStringFormat,
rules::flake8_logging_format::violations::LoggingPercentFormat,
rules::flake8_logging_format::violations::LoggingStringConcat,
rules::flake8_logging_format::violations::LoggingFString,
rules::flake8_logging_format::violations::LoggingWarn,
rules::flake8_logging_format::violations::LoggingExtraAttrClash,
rules::flake8_logging_format::violations::LoggingExcInfo,
rules::flake8_logging_format::violations::LoggingRedundantExcInfo,
// flake8-raise
rules::flake8_raise::rules::UnnecessaryParenOnRaiseException,
// flake8-self
rules::flake8_self::rules::PrivateMemberAccess,
// flake8-gettext
rules::flake8_gettext::rules::FStringInGetTextFuncCall,
rules::flake8_gettext::rules::FormatInGetTextFuncCall,
rules::flake8_gettext::rules::PrintfInGetTextFuncCall,
// numpy
rules::numpy::rules::NumpyDeprecatedTypeAlias,
rules::numpy::rules::NumpyLegacyRandom,
// ruff
rules::ruff::rules::AmbiguousUnicodeCharacterString,
rules::ruff::rules::AmbiguousUnicodeCharacterDocstring,
rules::ruff::rules::AmbiguousUnicodeCharacterComment,
rules::ruff::rules::CollectionLiteralConcatenation,
rules::ruff::rules::AsyncioDanglingTask,
rules::ruff::rules::UnusedNOQA,
rules::ruff::rules::PairwiseOverZipped,
rules::ruff::rules::MutableDataclassDefault,
rules::ruff::rules::FunctionCallInDataclassDefaultArgument,
rules::ruff::rules::ExplicitFStringTypeConversion,
rules::ruff::rules::InvalidPyprojectToml,
// flake8-django
rules::flake8_django::rules::DjangoNullableModelStringField,
rules::flake8_django::rules::DjangoLocalsInRenderFunction,
rules::flake8_django::rules::DjangoExcludeWithModelForm,
rules::flake8_django::rules::DjangoAllWithModelForm,
rules::flake8_django::rules::DjangoModelWithoutDunderStr,
rules::flake8_django::rules::DjangoUnorderedBodyContentInModel,
rules::flake8_django::rules::DjangoNonLeadingReceiverDecorator,
// flynt
rules::flynt::rules::StaticJoinToFString,
// flake8-todo
rules::flake8_todos::rules::InvalidTodoTag,
rules::flake8_todos::rules::MissingTodoAuthor,
rules::flake8_todos::rules::MissingTodoLink,
rules::flake8_todos::rules::MissingTodoColon,
rules::flake8_todos::rules::MissingTodoDescription,
rules::flake8_todos::rules::InvalidTodoCapitalization,
rules::flake8_todos::rules::MissingSpaceAfterTodoColon,
// airflow
rules::airflow::rules::AirflowVariableNameTaskIdMismatch,
// flake8-fixme
rules::flake8_fixme::rules::LineContainsTodo,
rules::flake8_fixme::rules::LineContainsHack,
rules::flake8_fixme::rules::LineContainsXxx,
rules::flake8_fixme::rules::LineContainsFixme,
);
pub trait AsRule {
fn rule(&self) -> Rule;
}

View file

@ -254,7 +254,7 @@ impl RuleSet {
///
/// let iter: Vec<_> = set.iter().collect();
///
/// assert_eq!(iter, vec![Rule::AmbiguousFunctionName, Rule::AnyType]);
/// assert_eq!(iter, vec![Rule::AnyType, Rule::AmbiguousFunctionName]);
/// ```
pub fn iter(&self) -> RuleSetIterator {
RuleSetIterator {

View file

@ -6,7 +6,8 @@ use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use crate::codes::RuleCodePrefix;
use crate::registry::{Linter, Rule, RuleIter, RuleNamespace};
use crate::codes::RuleIter;
use crate::registry::{Linter, Rule, RuleNamespace};
use crate::rule_redirects::get_redirect;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -11,7 +11,6 @@ mod config;
mod derive_message_formats;
mod map_codes;
mod newtype_index;
mod register_rules;
mod rule_code_prefix;
mod rule_namespace;
mod violation;
@ -44,12 +43,6 @@ pub fn cache_key(input: TokenStream) -> TokenStream {
TokenStream::from(stream)
}
#[proc_macro]
pub fn register_rules(item: TokenStream) -> TokenStream {
let mapping = parse_macro_input!(item as register_rules::Input);
register_rules::register_rules(&mapping).into()
}
/// Adds an `explanation()` method from the doc comment.
#[proc_macro_attribute]
pub fn violation(_attr: TokenStream, item: TokenStream) -> TokenStream {

View file

@ -10,61 +10,59 @@ use syn::{
use crate::rule_code_prefix::{get_prefix_ident, if_all_same, is_nursery};
struct LinterToRuleData {
/// The rule identifier, e.g., `Rule::UnaryPrefixIncrement`.
rule_id: Path,
/// The rule group identifiers, e.g., `RuleGroup::Unspecified`.
rule_group_id: Path,
/// The rule attributes.
/// A rule entry in the big match statement such a
/// `(Pycodestyle, "E112") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::NoIndentedBlock),`
#[derive(Clone)]
struct Rule {
/// The actual name of the rule, e.g., `NoIndentedBlock`.
name: Ident,
/// The linter associated with the rule, e.g., `Pycodestyle`.
linter: Ident,
/// The code associated with the rule, e.g., `"E112"`.
code: LitStr,
/// The rule group identifier, e.g., `RuleGroup::Nursery`.
group: Path,
/// The path to the struct implementing the rule, e.g.
/// `rules::pycodestyle::rules::logical_lines::NoIndentedBlock`
path: Path,
/// The rule attributes, e.g. for feature gates
attrs: Vec<Attribute>,
}
struct RuleToLinterData<'a> {
/// The linter associated with the rule, e.g., `Flake8Bugbear`.
linter: &'a Ident,
/// The code associated with the rule, e.g., `"002"`.
code: &'a str,
/// The rule group identifier, e.g., `RuleGroup::Unspecified`.
rule_group_id: &'a Path,
/// The rule attributes.
attrs: &'a [Attribute],
}
pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
let Some(last_stmt) = func.block.stmts.last() else {
return Err(Error::new(func.block.span(), "expected body to end in an expression"));
};
let Stmt::Expr(Expr::Call(ExprCall{args: some_args, ..}), _) = last_stmt else {
return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"))
let Stmt::Expr(Expr::Call(ExprCall { args: some_args, .. }), _) = last_stmt else {
return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"));
};
let mut some_args = some_args.into_iter();
let (Some(Expr::Match(ExprMatch { arms, .. })), None) = (some_args.next(), some_args.next()) else {
return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"))
return Err(Error::new(last_stmt.span(), "expected last expression to be `Some(match (..) { .. })`"));
};
// Map from: linter (e.g., `Flake8Bugbear`) to rule code (e.g.,`"002"`) to rule data (e.g.,
// `(Rule::UnaryPrefixIncrement, RuleGroup::Unspecified, vec![])`).
let mut linter_to_rules: BTreeMap<Ident, BTreeMap<String, LinterToRuleData>> = BTreeMap::new();
let mut linter_to_rules: BTreeMap<Ident, BTreeMap<String, Rule>> = BTreeMap::new();
for arm in arms {
if matches!(arm.pat, Pat::Wild(..)) {
break;
}
let entry = syn::parse::<Entry>(arm.into_token_stream().into())?;
linter_to_rules.entry(entry.linter).or_default().insert(
entry.code.value(),
LinterToRuleData {
rule_id: entry.rule,
rule_group_id: entry.group,
attrs: entry.attrs,
},
);
let rule = syn::parse::<Rule>(arm.into_token_stream().into())?;
linter_to_rules
.entry(rule.linter.clone())
.or_default()
.insert(rule.code.value(), rule);
}
let linter_idents: Vec<_> = linter_to_rules.keys().collect();
let mut output = quote! {
let all_rules = linter_to_rules.values().flat_map(BTreeMap::values);
let mut output = register_rules(all_rules);
output.extend(quote! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RuleCodePrefix {
#(#linter_idents(#linter_idents),)*
@ -83,21 +81,14 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
}
}
}
};
});
for (linter, rules) in &linter_to_rules {
output.extend(super::rule_code_prefix::expand(
linter,
rules.iter().map(
|(
code,
LinterToRuleData {
rule_group_id,
attrs,
..
},
)| (code.as_str(), rule_group_id, attrs),
),
rules
.iter()
.map(|(code, Rule { group, attrs, .. })| (code.as_str(), group, attrs)),
));
output.extend(quote! {
@ -117,56 +108,7 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
let mut all_codes = Vec::new();
for (linter, rules) in &linter_to_rules {
// Group the rules by their common prefixes.
// TODO(charlie): Why do we do this here _and_ in `rule_code_prefix::expand`?
let mut rules_by_prefix = BTreeMap::new();
for (
code,
LinterToRuleData {
rule_id,
rule_group_id,
attrs,
},
) in rules
{
// Nursery rules have to be explicitly selected, so we ignore them when looking at
// prefixes.
if is_nursery(rule_group_id) {
rules_by_prefix.insert(code.clone(), vec![(rule_id.clone(), attrs.clone())]);
continue;
}
for i in 1..=code.len() {
let prefix = code[..i].to_string();
let rules: Vec<_> = rules
.iter()
.filter_map(
|(
code,
LinterToRuleData {
rule_id,
rule_group_id,
attrs,
},
)| {
// Nursery rules have to be explicitly selected, so we ignore them when
// looking at prefixes.
if is_nursery(rule_group_id) {
return None;
}
if code.starts_with(&prefix) {
Some((rule_id.clone(), attrs.clone()))
} else {
None
}
},
)
.collect();
rules_by_prefix.insert(prefix, rules);
}
}
let rules_by_prefix = rules_by_prefix(rules);
for (prefix, rules) in &rules_by_prefix {
let prefix_ident = get_prefix_ident(prefix);
@ -182,9 +124,10 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
let mut prefix_into_iter_match_arms = quote!();
for (prefix, rules) in rules_by_prefix {
let rule_paths = rules
.iter()
.map(|(path, .., attrs)| quote!(#(#attrs)* #path));
let rule_paths = rules.iter().map(|(path, .., attrs)| {
let rule_name = path.segments.last().unwrap();
quote!(#(#attrs)* Rule::#rule_name)
});
let prefix_ident = get_prefix_ident(&prefix);
let attr = match if_all_same(rules.iter().map(|(.., attrs)| attrs)) {
Some(attr) => quote!(#(#attr)*),
@ -232,35 +175,71 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
}
});
// Map from rule to codes that can be used to select it.
// This abstraction exists to support a one-to-many mapping, whereby a single rule could map
// to multiple codes (e.g., if it existed in multiple linters, like Pylint and Flake8, under
// different codes). We haven't actually activated this functionality yet, but some work was
// done to support it, so the logic exists here.
let mut rule_to_codes: HashMap<&Path, Vec<RuleToLinterData>> = HashMap::new();
let rule_to_code = generate_rule_to_code(&linter_to_rules);
output.extend(rule_to_code);
let iter = generate_iter_impl(&linter_to_rules, &all_codes);
output.extend(iter);
Ok(output)
}
/// Group the rules by their common prefixes.
fn rules_by_prefix(
rules: &BTreeMap<String, Rule>,
) -> BTreeMap<String, Vec<(Path, Vec<Attribute>)>> {
// TODO(charlie): Why do we do this here _and_ in `rule_code_prefix::expand`?
let mut rules_by_prefix = BTreeMap::new();
for (code, rule) in rules {
// Nursery rules have to be explicitly selected, so we ignore them when looking at
// prefixes.
if is_nursery(&rule.group) {
rules_by_prefix.insert(code.clone(), vec![(rule.path.clone(), rule.attrs.clone())]);
continue;
}
for i in 1..=code.len() {
let prefix = code[..i].to_string();
let rules: Vec<_> = rules
.iter()
.filter_map(|(code, rule)| {
// Nursery rules have to be explicitly selected, so we ignore them when
// looking at prefixes.
if is_nursery(&rule.group) {
return None;
}
if code.starts_with(&prefix) {
Some((rule.path.clone(), rule.attrs.clone()))
} else {
None
}
})
.collect();
rules_by_prefix.insert(prefix, rules);
}
}
rules_by_prefix
}
/// Map from rule to codes that can be used to select it.
/// This abstraction exists to support a one-to-many mapping, whereby a single rule could map
/// to multiple codes (e.g., if it existed in multiple linters, like Pylint and Flake8, under
/// different codes). We haven't actually activated this functionality yet, but some work was
/// done to support it, so the logic exists here.
fn generate_rule_to_code(linter_to_rules: &BTreeMap<Ident, BTreeMap<String, Rule>>) -> TokenStream {
let mut rule_to_codes: HashMap<&Path, Vec<&Rule>> = HashMap::new();
let mut linter_code_for_rule_match_arms = quote!();
for (linter, map) in &linter_to_rules {
for (
code,
LinterToRuleData {
rule_id,
rule_group_id,
attrs,
},
) in map
{
rule_to_codes
.entry(rule_id)
.or_default()
.push(RuleToLinterData {
linter,
code,
rule_group_id,
attrs,
});
for (linter, map) in linter_to_rules {
for (code, rule) in map {
let Rule {
path, attrs, name, ..
} = rule;
rule_to_codes.entry(path).or_default().push(rule);
linter_code_for_rule_match_arms.extend(quote! {
#(#attrs)* (Self::#linter, #rule_id) => Some(#code),
#(#attrs)* (Self::#linter, Rule::#name) => Some(#code),
});
}
}
@ -269,6 +248,7 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
let mut rule_group_match_arms = quote!();
for (rule, codes) in rule_to_codes {
let rule_name = rule.segments.last().unwrap();
assert_eq!(
codes.len(),
1,
@ -284,30 +264,31 @@ and before we can do that we have to rename all our rules to match our naming co
See also https://github.com/charliermarsh/ruff/issues/2186.
",
rule.segments.last().unwrap().ident
rule_name.ident
);
let RuleToLinterData {
let Rule {
linter,
code,
rule_group_id,
group,
attrs,
..
} = codes
.iter()
.sorted_by_key(|data| *data.linter == "Pylint")
.sorted_by_key(|data| data.linter == "Pylint")
.next()
.unwrap();
rule_noqa_code_match_arms.extend(quote! {
#(#attrs)* #rule => NoqaCode(crate::registry::Linter::#linter.common_prefix(), #code),
#(#attrs)* Rule::#rule_name => NoqaCode(crate::registry::Linter::#linter.common_prefix(), #code),
});
rule_group_match_arms.extend(quote! {
#(#attrs)* #rule => #rule_group_id,
#(#attrs)* Rule::#rule_name => #group,
});
}
output.extend(quote! {
let rule_to_code = quote! {
impl Rule {
pub fn noqa_code(&self) -> NoqaCode {
use crate::registry::RuleNamespace;
@ -338,19 +319,27 @@ See also https://github.com/charliermarsh/ruff/issues/2186.
}
}
}
});
};
rule_to_code
}
/// Implement `impl IntoIterator for &Linter` and `RuleCodePrefix::iter()`
fn generate_iter_impl(
linter_to_rules: &BTreeMap<Ident, BTreeMap<String, Rule>>,
all_codes: &[TokenStream],
) -> TokenStream {
let mut linter_into_iter_match_arms = quote!();
for (linter, map) in &linter_to_rules {
let rule_paths = map
.values()
.map(|LinterToRuleData { rule_id, attrs, .. }| quote!(#(#attrs)* #rule_id));
for (linter, map) in linter_to_rules {
let rule_paths = map.values().map(|Rule { attrs, path, .. }| {
let rule_name = path.segments.last().unwrap();
quote!(#(#attrs)* Rule::#rule_name)
});
linter_into_iter_match_arms.extend(quote! {
Linter::#linter => vec![#(#rule_paths,)*].into_iter(),
});
}
output.extend(quote! {
quote! {
impl IntoIterator for &Linter {
type Item = Rule;
type IntoIter = ::std::vec::IntoIter<Self::Item>;
@ -362,29 +351,94 @@ See also https://github.com/charliermarsh/ruff/issues/2186.
}
}
});
output.extend(quote! {
impl RuleCodePrefix {
pub fn iter() -> ::std::vec::IntoIter<RuleCodePrefix> {
vec![ #(#all_codes,)* ].into_iter()
}
}
});
Ok(output)
}
}
struct Entry {
linter: Ident,
code: LitStr,
group: Path,
rule: Path,
attrs: Vec<Attribute>,
/// Generate the `Rule` enum
fn register_rules<'a>(input: impl Iterator<Item = &'a Rule>) -> TokenStream {
let mut rule_variants = quote!();
let mut rule_message_formats_match_arms = quote!();
let mut rule_autofixable_match_arms = quote!();
let mut rule_explanation_match_arms = quote!();
let mut from_impls_for_diagnostic_kind = quote!();
for Rule {
name, attrs, path, ..
} in input
{
rule_variants.extend(quote! {
#(#attrs)*
#name,
});
// Apply the `attrs` to each arm, like `[cfg(feature = "foo")]`.
rule_message_formats_match_arms
.extend(quote! {#(#attrs)* Self::#name => <#path as ruff_diagnostics::Violation>::message_formats(),});
rule_autofixable_match_arms.extend(
quote! {#(#attrs)* Self::#name => <#path as ruff_diagnostics::Violation>::AUTOFIX,},
);
rule_explanation_match_arms
.extend(quote! {#(#attrs)* Self::#name => #path::explanation(),});
// Enable conversion from `DiagnosticKind` to `Rule`.
from_impls_for_diagnostic_kind
.extend(quote! {#(#attrs)* stringify!(#name) => Rule::#name,});
}
quote! {
#[derive(
EnumIter,
Debug,
PartialEq,
Eq,
Copy,
Clone,
Hash,
PartialOrd,
Ord,
::ruff_macros::CacheKey,
AsRefStr,
::strum_macros::IntoStaticStr,
)]
#[repr(u16)]
#[strum(serialize_all = "kebab-case")]
pub enum Rule { #rule_variants }
impl Rule {
/// Returns the format strings used to report violations of this rule.
pub fn message_formats(&self) -> &'static [&'static str] {
match self { #rule_message_formats_match_arms }
}
/// Returns the documentation for this rule.
pub fn explanation(&self) -> Option<&'static str> {
match self { #rule_explanation_match_arms }
}
/// Returns the autofix status of this rule.
pub const fn autofixable(&self) -> ruff_diagnostics::AutofixKind {
match self { #rule_autofixable_match_arms }
}
}
impl AsRule for ruff_diagnostics::DiagnosticKind {
fn rule(&self) -> Rule {
match self.name.as_str() {
#from_impls_for_diagnostic_kind
_ => unreachable!("invalid rule name: {}", self.name),
}
}
}
}
}
impl Parse for Entry {
/// Parses a match arm such as `(Pycodestyle, "E112") => (RuleGroup::Nursery, Rule::NoIndentedBlock),`
impl Parse for Rule {
/// Parses a match arm such as `(Pycodestyle, "E112") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::NoIndentedBlock),`
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let attrs = Attribute::parse_outer(input)?;
let pat_tuple;
@ -397,13 +451,15 @@ impl Parse for Entry {
parenthesized!(pat_tuple in input);
let group: Path = pat_tuple.parse()?;
let _: Token!(,) = pat_tuple.parse()?;
let rule: Path = pat_tuple.parse()?;
let rule_path: Path = pat_tuple.parse()?;
let _: Token!(,) = input.parse()?;
Ok(Entry {
let rule_name = rule_path.segments.last().unwrap().ident.clone();
Ok(Rule {
name: rule_name,
linter,
code,
group,
rule,
path: rule_path,
attrs,
})
}

View file

@ -1,95 +0,0 @@
use quote::quote;
use syn::parse::Parse;
use syn::{Attribute, Ident, Path, Token};
pub(crate) fn register_rules(input: &Input) -> proc_macro2::TokenStream {
let mut rule_variants = quote!();
let mut rule_message_formats_match_arms = quote!();
let mut rule_autofixable_match_arms = quote!();
let mut rule_explanation_match_arms = quote!();
let mut from_impls_for_diagnostic_kind = quote!();
for (path, name, attr) in &input.entries {
rule_variants.extend(quote! {
#(#attr)*
#name,
});
// Apply the `attrs` to each arm, like `[cfg(feature = "foo")]`.
rule_message_formats_match_arms
.extend(quote! {#(#attr)* Self::#name => <#path as ruff_diagnostics::Violation>::message_formats(),});
rule_autofixable_match_arms.extend(
quote! {#(#attr)* Self::#name => <#path as ruff_diagnostics::Violation>::AUTOFIX,},
);
rule_explanation_match_arms.extend(quote! {#(#attr)* Self::#name => #path::explanation(),});
// Enable conversion from `DiagnosticKind` to `Rule`.
from_impls_for_diagnostic_kind.extend(quote! {#(#attr)* stringify!(#name) => Rule::#name,});
}
quote! {
#[derive(
EnumIter,
Debug,
PartialEq,
Eq,
Copy,
Clone,
Hash,
PartialOrd,
Ord,
::ruff_macros::CacheKey,
AsRefStr,
::strum_macros::IntoStaticStr,
)]
#[repr(u16)]
#[strum(serialize_all = "kebab-case")]
pub enum Rule { #rule_variants }
impl Rule {
/// Returns the format strings used to report violations of this rule.
pub fn message_formats(&self) -> &'static [&'static str] {
match self { #rule_message_formats_match_arms }
}
/// Returns the documentation for this rule.
pub fn explanation(&self) -> Option<&'static str> {
match self { #rule_explanation_match_arms }
}
/// Returns the autofix status of this rule.
pub const fn autofixable(&self) -> ruff_diagnostics::AutofixKind {
match self { #rule_autofixable_match_arms }
}
}
impl AsRule for ruff_diagnostics::DiagnosticKind {
fn rule(&self) -> Rule {
match self.name.as_str() {
#from_impls_for_diagnostic_kind
_ => unreachable!("invalid rule name: {}", self.name),
}
}
}
}
}
pub(crate) struct Input {
entries: Vec<(Path, Ident, Vec<Attribute>)>,
}
impl Parse for Input {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut entries = Vec::new();
while !input.is_empty() {
// Grab the `#[cfg(...)]` attributes.
let attrs = input.call(Attribute::parse_outer)?;
let path: Path = input.parse()?;
let name = path.segments.last().unwrap().ident.clone();
let _: Token![,] = input.parse()?;
entries.push((path, name, attrs));
}
Ok(Self { entries })
}
}

View file

@ -103,8 +103,7 @@ pub struct {name};
impl Violation for {name} {{
#[derive_message_formats]
fn message(&self) -> String {{
todo!("implement message");
format!("TODO: write message")
format!("TODO: write message: {{}}", todo!("implement message"))
}}
}}
""",
@ -116,56 +115,6 @@ pub(crate) fn {rule_name_snake}(checker: &mut Checker) {{}}
""",
)
# Add the relevant code-to-violation pair to `src/registry.rs`.
content = (ROOT_DIR / "crates/ruff/src/registry.rs").read_text()
seen_macro = False
has_written = False
has_seen_linter = False
with (ROOT_DIR / "crates/ruff/src/registry.rs").open("w") as fp:
lines = []
for line in content.splitlines():
if has_written:
fp.write(line)
fp.write("\n")
continue
if line.startswith("ruff_macros::register_rules!"):
seen_macro = True
fp.write(line)
fp.write("\n")
continue
if not seen_macro:
fp.write(line)
fp.write("\n")
continue
if line.strip() == f"// {linter}":
indent = get_indent(line)
lines.append(f"{indent}rules::{dir_name(linter)}::rules::{name},")
has_seen_linter = True
fp.write(line)
fp.write("\n")
continue
if not has_seen_linter:
fp.write(line)
fp.write("\n")
continue
if not line.strip().startswith("// "):
lines.append(line)
else:
lines.sort()
fp.write("\n".join(lines))
fp.write("\n")
fp.write(line)
fp.write("\n")
has_written = True
assert has_written
text = ""
with (ROOT_DIR / "crates/ruff/src/codes.rs").open("r") as fp:
while (line := next(fp)).strip() != f"// {linter}":
@ -177,17 +126,15 @@ pub(crate) fn {rule_name_snake}(checker: &mut Checker) {{}}
lines.append(line)
variant = pascal_case(linter)
rule = f"""rules::{linter.split(" ")[0]}::rules::{name}"""
lines.append(
" " * 8
+ f"""({variant}, "{code}") => (RuleGroup::Unspecified, Rule::{name}),\n""",
+ f"""({variant}, "{code}") => (RuleGroup::Unspecified, {rule}),\n""",
)
lines.sort()
text += "".join(lines)
text += "\n"
text += fp.read()
with (ROOT_DIR / "crates/ruff/src/codes.rs").open("w") as fp:
fp.write(text)