Open-Source Wikis

/

Ruff

/

Crates

/

`ruff_linter`

astral-sh/ruff

ruff_linter

The linter engine — registry of rules, per-pass checkers, and the public check_path API. Source: crates/ruff_linter/.

Purpose

ruff_linter consumes a parsed Python file plus resolved settings and produces a list of Diagnostic values, optionally with attached Fixes. Every lint rule (~900 of them) lives in this crate.

Directory layout

crates/ruff_linter/src/
├── lib.rs
├── linter.rs          # check_path entry point
├── linter/            # supporting helpers
├── registry.rs        # Rule enum + categories
├── codes.rs           # rule code → Rule mapping (single source of truth)
├── rule_selector.rs   # parses --select / --ignore expressions
├── rule_redirects.rs  # legacy code aliases
├── checkers/          # per-pass visitors
│   ├── ast.rs         # the big AST visitor that drives most rules
│   ├── tokens.rs
│   ├── physical_lines.rs
│   ├── logical_lines.rs
│   ├── imports.rs
│   ├── filesystem.rs
│   └── noqa.rs
├── rules/             # 60 plugin folders, one per rule family
│   ├── pyflakes/
│   ├── pycodestyle/
│   ├── flake8_bugbear/
│   ├── pyupgrade/
│   ├── ruff/          # Ruff-native rules (RUF*)
│   └── …
├── fix/
├── docstrings/
├── importer/
├── settings/          # LinterSettings + per-rule sub-settings
├── message/
├── noqa.rs
├── package.rs
├── packaging.rs
├── pyproject_toml.rs
├── preview.rs
├── doc_lines.rs
├── line_width.rs
├── locator.rs
├── logging.rs
├── registry/
├── renamer.rs
├── snapshots/
├── comments/
├── cst/
└── fs.rs

Key abstractions

Type File Purpose
Rule crates/ruff_linter/src/registry.rs The exhaustive enum of every rule.
RuleCode crates/ruff_linter/src/codes.rs Mapping between user-facing codes and Rule.
RuleSelector crates/ruff_linter/src/rule_selector.rs Parsed --select expression (E, E5, E501, ALL).
LinterSettings crates/ruff_linter/src/settings/ Resolved per-file lint settings.
Checker (AST) crates/ruff_linter/src/checkers/ast.rs The main visitor that walks the AST and dispatches rules.
Diagnostic crates/ruff_diagnostics Emitted by rules; carries optional Fix.
check_path crates/ruff_linter/src/linter.rs Public entry point used by ruff and ruff_wasm.

How it runs (per file)

graph TD
    Source[Python source]
    Parser[ruff_python_parser]
    Indexer[ruff_python_index]
    Tokens[Token stream]
    AST[Mod AST]

    Source --> Parser
    Parser --> AST
    Parser --> Tokens
    AST --> Indexer
    Indexer --> Sem[SemanticModel]

    AST --> AstChecker[checkers/ast.rs]
    Tokens --> TokenChecker[checkers/tokens.rs]
    Source --> PhysChecker[checkers/physical_lines.rs]
    Tokens --> LogChecker[checkers/logical_lines.rs]
    Indexer --> ImpChecker[checkers/imports.rs]

    AstChecker --> Diag[Diagnostics + Fixes]
    TokenChecker --> Diag
    PhysChecker --> Diag
    LogChecker --> Diag
    ImpChecker --> Diag

    AstChecker -.uses.-> Sem

The AST checker is by far the largest. It implements Visitor over ruff_python_ast::Mod and, on each node, dispatches to every enabled rule that cares about that node kind. This is what lets Ruff run hundreds of rules in a single pass.

Rule plugin directories

The 60 directories under rules/ are one per upstream tool (or one per Ruff-native family):

airflow             flake8_django           flake8_pyi
copyright           flake8_errmsg           flake8_pytest_style
eradicate           flake8_executable       flake8_quotes
fastapi             flake8_fixme            flake8_raise
flake8_2020         flake8_future_annotations  flake8_return
flake8_annotations  flake8_gettext          flake8_self
flake8_async        flake8_implicit_str_concat  flake8_simplify
flake8_bandit       flake8_import_conventions   flake8_slots
flake8_blind_except flake8_logging          flake8_tidy_imports
flake8_boolean_trap flake8_logging_format   flake8_todos
flake8_bugbear      flake8_no_pep420        flake8_type_checking
flake8_builtins     flake8_pie              flake8_use_pathlib
flake8_commas       flake8_print            flynt
flake8_comprehensions  isort                mccabe
flake8_copyright    pycodestyle             pandas_vet
flake8_datetimez    pydoclint               pep8_naming
flake8_debugger     pydocstyle              perflint
                    pyflakes                pygrep_hooks
                    pylint                  pyupgrade
                    refurb                  ruff
                    tryceratops             numpy

(One plugin per Flake8 ecosystem tool, plus a few first-party families like ruff/, numpy/, airflow/.)

Inside each plugin folder:

flake8_bugbear/
├── mod.rs           # registration + helper functions
├── rules/           # one .rs file per rule (`B001.rs`, `B002.rs`, …)
└── snapshots/       # insta snapshots

Rule code → struct mapping

crates/ruff_linter/src/codes.rs is the canonical mapping. Each rule has:

  • A 1–4 character code prefix (e.g. F, B, RUF)
  • A 3–4 digit number
  • A struct that implements the rule (in rules/<plugin>/rules/<name>.rs)
  • Doc comments rendered in the rule explainer

Rule selectors and presets

Users select rules with codes (F401, B, E5) or category names (ALL, EXTEND). The grammar is implemented in rule_selector.rs. Presets such as ALL are computed at compile time.

rule_redirects.rs keeps old codes pointing to their renamed targets so user configs don't break across versions.

Fixes and applicability

Rules attach a Fix to a diagnostic when an automatic correction is possible. The Applicability enum (Safe, Unsafe, DisplayOnly) determines whether --fix will actually apply it without --unsafe-fixes. Fix machinery lives in crates/ruff_linter/src/fix/.

Noqa

noqa.rs parses # noqa: F401, E501 comments and exposes APIs the AST checker uses to filter diagnostics. The noqa checker also flags unused/unknown directives.

Settings

LinterSettings is a large struct combining global toggles (line length, target version) and per-rule sub-settings (e.g. flake8_bugbear::Settings, pylint::Settings). It's built from ruff_workspace::Settings.

Adding a rule

  1. Pick a plugin directory (or, for new families, create one).
  2. Add a RuleCode in codes.rs.
  3. Write rules/<plugin>/rules/<rule_name>.rs with the visitor function and the diagnostic struct (with rich doc-comments).
  4. Register the rule in the plugin's mod.rs.
  5. Add a fixture under resources/test/fixtures/<plugin>/<rule>.py.
  6. Add a snapshot test in the plugin module.
  7. Run cargo dev generate-all to update schemas and docs.

For an end-to-end walkthrough, see features/linter.

Integration points

Where to dig deeper

  • The big visitor: crates/ruff_linter/src/checkers/ast.rs.
  • Rule registration patterns: crates/ruff_linter/src/codes.rs and any rules/<plugin>/mod.rs.
  • Fix application: crates/ruff_linter/src/fix/.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

`ruff_linter` – Ruff wiki | Factory