Skip to content

Allow rules without codes - #28049

Open
ntBre wants to merge 13 commits into
brent/update-preview-category-defaultsfrom
brent/codeless-rules
Open

Allow rules without codes#28049
ntBre wants to merge 13 commits into
brent/update-preview-category-defaultsfrom
brent/codeless-rules

Conversation

@ntBre

@ntBre ntBre commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR allows adding rules that only belong to categories and only have names rather than requiring
a linter group and code.

The first non-empty commit introduces a Rule::identifier shim and uses it to replace
Rule::noqa_code calls mechanically in all the test snapshots.

The second commit updates FixTable from a mapping of SecondaryCode -> (name, fix_count) to
DiagnosticId -> (Option<SecondaryCode>, fix_count). This doesn't really change its usage but
involves minor diffs to a substantial number of lines. It also allows us to drop some of the special
hashbrown handling and the hashbrown dependency in ruff_linter, which is nice.

The third commit contains the bulk of the change, including the macro changes needed to support
map_codes entries like that for the pytest-fixture-autouse rule in the fourth commit:

() => rules::ruff::rules::PytestFixtureAutouse,

These changes are pretty mechanical overall, just handling cases where the linter or noqa code could
be missing, which fortunately seemed pretty straightforward in every case.

The fourth commit takes advantage of these changes to reinstate RUF076 as our first purely
pedantic rule. I'm happy to drop that if it's too controversial or opens too many questions about
the previously-removed rule and its code, but I thought it was nice to have a motivating example. We
moved this rule from preview to removed because it was too pedantic for the RUF category, even
in preview, so it's exactly the kind of rule we can support now with the categories.

Finally, the fifth commit updates the contributing docs to reflect that linters and codes are
optional. This may need to be adjusted if we back out the RUF076 change.

Test Plan

Existing tests, plus a few new ones for pytest-fixture-autouse

@ntBre ntBre added preview Related to preview mode features rule-selection Related to enabling or disabling rules labels Aug 25, 2026
@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 97.79%. The percentage of expected errors that received a diagnostic held steady at 94.33%. The number of fully passing files held steady at 112/136.

@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

(Flake8UsePathlib, "124") => rules::flake8_use_pathlib::violations::PyPath,
(Flake8UsePathlib, "201") => rules::flake8_use_pathlib::rules::PathConstructorCurrentDirectory,
(Flake8UsePathlib, "202") => rules::flake8_use_pathlib::rules::OsPathGetsize,
(Flake8UsePathlib, "202") => rules::flake8_use_pathlib::rules::OsPathGetsize,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Unrelated but noticed while I was here.

@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

No diagnostic changes detected ✅

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Full report with detailed diff (timing results)

@codspeed-hq

codspeed-hq Bot commented Aug 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 186 untouched benchmarks
⏩ 24 skipped benchmarks1


Comparing brent/codeless-rules (9c5afd2) with brent/update-preview-category-defaults (eb0752a)

Open in CodSpeed

Footnotes

  1. 24 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+740 -0 violations, +0 -0 fixes in 8 projects; 51 projects unchanged)

apache/airflow (+579 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ airflow-core/tests/conftest.py:166:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/system/conftest.py:34:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/always/test_providers_manager.py:346:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/always/test_providers_manager.py:93:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/always/test_secrets_environment_variables.py:62:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api/common/test_trigger_dag.py:42:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/auth/managers/simple/conftest.py:34:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/auth/test_tokens.py:329:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/common/db/test_dags.py:45:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/common/test_dagbag.py:52:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/common/test_exceptions.py:113:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_asset_state_store.py:57:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:1335:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:1340:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:2174:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:310:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:814:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py:101:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py:165:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py:40:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_backfills.py:77:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_config.py:165:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_config.py:513:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_config.py:579:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_connections.py:102:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_parsing.py:41:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py:142:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py:4432:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_sources.py:71:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_tags.py:106:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_versions.py:32:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_warning.py:50:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dags.py:214:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py:69:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_extra_links.py:91:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py:278:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_import_error.py:104:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_import_error.py:116:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_log.py:52:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
... 540 additional changes omitted for project

apache/superset (+111 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ tests/integration_tests/celery_tests.py:71:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/charts/api_tests.py:91:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/charts/data/api_tests.py:111:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/charts/version_restore_tests.py:68:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/conftest.py:120:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dao/base_dao_test.py:72:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dao/conftest.py:46:33: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dashboards/filter_state/api_tests.py:56:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dashboards/update_tabs_test.py:41:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dashboards/version_restore_tests.py:69:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
... 101 additional changes omitted for project

bokeh/bokeh (+3 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ tests/codebase/conftest.py:29:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_examples.py:112:34: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/unit/bokeh/io/test_export.py:104:33: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators

langchain-ai/langchain (+16 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ libs/core/tests/unit_tests/conftest.py:12:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/language_models/chat_models/test_rate_limiting.py:12:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/load/test_secret_injection.py:35:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/runnables/conftest.py:10:34: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/runnables/test_tracing_interops.py:303:21: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/utils/test_gateway.py:23:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/langchain_v1/tests/unit_tests/agents/middleware/core/test_tracing.py:23:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/langchain_v1/tests/unit_tests/conftest.py:20:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/partners/openai/tests/integration_tests/chat_models/conftest.py:28:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/partners/openai/tests/integration_tests/chat_models/conftest.py:95:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
... 6 additional changes omitted for project

pypa/build (+4 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ tests/conftest.py:103:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:109:34: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:88:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:96:17: pytest-fixture-autouse Avoid using `autouse=True` in `pytest.fixture` decorators

... Truncated remaining completed project reports due to GitHub comment length restrictions

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
pytest-fixture-autouse 740 740 0 0 0

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@ntBre
ntBre force-pushed the brent/codeless-rules branch from b36a3c4 to 8e75e0c Compare August 25, 2026 22:12
@ntBre
ntBre marked this pull request as ready for review August 25, 2026 22:34
@ntBre
ntBre requested a review from MichaReiser August 25, 2026 22:36

@MichaReiser MichaReiser left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you.

I think I'd prefer if we used rule.identifier in fewer places.

  • Let's move existing tests. While a little more work, it's not that involved and it simplifies the tests, they can simply call name. This also makes the test future prove for when we remove or deprecate codes. It also expresses that names are the preferred default now (I can't wait for when I don't need to remember both)
  • Let's review the uses of identifier in Support lint rules without legacy codes. Are there places where we can just use name because it isn't user facing? Are some of the uses user facing and need preview gating...

Comment thread crates/ruff/src/printer.rs
Comment thread crates/ruff_linter/src/registry.rs Outdated
Comment on lines +387 to +392
pub fn identifier(&self) -> LintIdentifier {
match self.noqa_code() {
Some(code) => LintIdentifier::Code(code),
None => LintIdentifier::Name(self.name()),
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason that we continue preferring the code over its name? Why can't we always use the name as identifier? Do we need to preview gate this?

Overall, the name identifier seems a bit confusing to me. A rule's unique identifier now and going forward is always its name. This seems to be mainly a compatibility layer, but it can't be for anything user facing, because it would otherwise have to be preview gated?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex is telling me that this breaks the ruff server.

With preview enabled and output-prefer-rule-codes = true, the server requests Noqa-style suppression edits, so this branch drops the edit for pytest-fixture-autouse. I reproduced that its “Disable for this line” action disappears; setting the option to false restores it. Fall back to a name-based ruff: ignore edit for codeless diagnostics in the server, regardless of the output preference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My Codex told me this as well, but I don't think this is actually a problem. This requires:

  • preview enabled
  • output-prefer-rule-codes = true
  • activating the preview, category-only rule pytest-fixture-autouse that is also quite pedantic
  • trying to quick-fix the rule in an editor

and the only "breakage" is that we don't offer the ruff: ignore quickfix when you've expressed a preference for codes. My codex also tried to fix this and it seemed more trouble than it was worth to allow changing the suppression style per diagnostic (it's currently selected for the whole batch of diagnostics).

I also wanted to fix it initially but was put off at least by Codex's initial approach, and it's quite a niche use case anyway. I can take a closer look if you want, though.

Comment thread crates/ruff/src/commands/format.rs Outdated
let mut rule_names: Vec<_> = incompatible_rules
.into_iter()
.map(|rule| format!("`{}`", rule.noqa_code()))
.map(|rule| format!("`{}`", rule.identifier()))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use seems user visible. Do we need to preview gate it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already preview-gated by virtue of the fact that the name-only rules are going to be in preview. There's no other option for rules without codes than to show the name, so I don't think there's any additional level of preview-gating we can apply. That's why identifier prefers codes when they're available, to preserve compatibility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we could just unwrap here for now. This is only used to warn for two rules that definitely have noqa codes.

Alternatively, are warnings part of our stable interface? It doesn't seem totally crazy to me just to switch this always to use names.

This also applies to this warning in the server:

tracing::warn!("Rule {} does not have an explanation", rule.noqa_code());
output.push_str("An issue occurred: an explanation for this rule was not found.");

We're down to only 3 uses of identifier including these two warnings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what you'll think about this, but I went ahead and unwrapped noqa_code here, used name_and_code in the server warning, and inlined identifier into RUF105, which allowed me to delete identifier completely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, are warnings part of our stable interface? It doesn't seem totally crazy to me just to switch this always to use names.

They're part of what I consider Ruff's user interface. Which is why I'd expect it to follow my configuration. But I admit, it's probably not worth bothering too much about it, but we should use codes or names based on the user's configuration if we can.

Comment thread crates/ruff/src/commands/check.rs Outdated
Comment thread crates/ruff/src/commands/format.rs Outdated
let mut rule_names: Vec<_> = incompatible_rules
.into_iter()
.map(|rule| format!("`{}`", rule.noqa_code()))
.map(|rule| format!("`{}`", rule.identifier()))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this user facing? I think we need to use names here in preview

Comment thread crates/ruff/src/printer.rs Outdated
.inner
.iter()
.sorted_by_key(|diagnostic| diagnostic.secondary_code())
.sorted_by_key(|diagnostic| diagnostic.secondary_code_or_id())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we group by secondary_code_or_id and not by id and only store the secondary code if needed for display?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good catch, I didn't realize we were calling secondary_code again down below for the output. We can use name here too. I always find this whole fold pretty complicated to follow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, codex pointed out that using name here has a very small impact on the stable output because the second sort below this won't reorder ties with the same count. So "F401 previously appeared before F821 [when they had the same count]; now F821 appears first." I think this is probably fine?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have to take a closer look, but if it's user facing, I'd expect it to follow my setting. Again, I agree, that's a detail, so probably not worth spending much time on, but it degrades polish

for rule in rules {
let name = rule.name();
let code = rule.noqa_code();
let code = rule.identifier();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use name_and_code here (and above)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name_and_code formats exactly as name (code), but this formats them differently:

            let _ = writeln!(output, "- [`{name}`](rules/{name}.md) (`{code}`)");

This code is actually problematic for a different reason since it loops over linters, disregarding rules that don't belong to a linter group. I think for now I'll just use noqa_code().unwrap() in light of that.

We'll have to stabilize both the categories themselves and a rule without a code for this to be a real problem, and I'm planning to include a secondary, category-focused rule page in my migration guide PR anyway.

Comment thread crates/ruff_dev/src/generate_docs.rs Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably search by both here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do already? This has both encoded_name and rule_code, producing example links like this:

(%27unused-import%27%20OR%20F401)

https://docs.astral.sh/ruff/rules/unused-import/

Ah but I guess we should drop the rule_code if it ends up being the name again.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also have codex retitle all issues

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also have codex retitle all issues

That's a bit scary 😆 I'll start with the rule_code change.

Comment thread crates/ruff_dev/src/generate_docs.rs Outdated
@ntBre
ntBre force-pushed the brent/codeless-rules branch from 8e75e0c to 8f81cca Compare August 26, 2026 18:57
ntBre added 12 commits August 26, 2026 18:44
Summary
--

This PR allows adding rules that only belong to categories and only have names rather than requiring
a linter group and code.

The first non-empty commit introduces a `Rule::identifier` shim and uses it to replace
`Rule::noqa_code` calls mechanically in all the test snapshots.

The second commit updates `FixTable` from a mapping of `SecondaryCode -> (name, fix_count)` to
`DiagnosticId -> (Option<SecondaryCode>, fix_count)`. This doesn't really change its usage but
involves minor diffs to a substantial number of lines. It also allows us to drop some of the special
`hashbrown` handling and the `hashbrown` dependency in `ruff_linter`, which is nice.

The third commit contains the bulk of the change, including the macro changes needed to support
`map_codes` entries like that for the `pytest-fixture-autouse` rule in the fourth commit:

```rust
() => rules::ruff::rules::PytestFixtureAutouse,
```

These changes are pretty mechanical overall, just handling cases where the linter or noqa code could
be missing, which fortunately seemed pretty straightforward in every case.

The fourth commit takes advantage of these changes to reinstate `RUF076` as our first purely
`pedantic` rule. I'm happy to drop that if it's too controversial or opens too many questions about
the previously-removed rule and its code, but I thought it was nice to have a motivating example. We
moved this rule from `preview` to `removed` because it was too pedantic for the `RUF` category, even
in preview, so it's exactly the kind of rule we can support now with the categories.

Finally, the fifth commit updates the contributing docs to reflect that linters and codes are
optional. This may need to be adjusted if we back out the `RUF076` change.

Test Plan
--

Existing tests, plus a few new ones for `pytest-fixture-autouse`
@ntBre
ntBre force-pushed the brent/codeless-rules branch from 8f81cca to 2941dfc Compare August 26, 2026 22:48
@ntBre
ntBre requested a review from MichaReiser August 26, 2026 23:20

@MichaReiser MichaReiser Aug 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, this touched more snapshots than I expected 😆

Waiting for the day where we run into Windows path-length issues

@MichaReiser

Copy link
Copy Markdown
Member

Can you squash some of your commit? It's otherwise very hard to review this PR because I can't review all changes because of the million snapshot changes, but going commit by commit only gives me partial changes, that I then need to keep in mind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule-selection Related to enabling or disabling rules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants