Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions text/3850-autogenerated-attr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
- Feature Name: autogenerated_attr
- Start Date: 2025-08-24
- RFC PR: [rust-lang/rfcs#3850](https://github.com/rust-lang/rfcs/pull/3850)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Add an attribute for marking items as being generated by a tool.
```rust
mod foo {
#![autogenerated(
by = "example tool",
suppress_lints = false,
format = true,
regen_hint = "./regen_example_tool",
document = false,
stable_api = true,
)]
// autogenerated code here...
}
```
Many attributes could be provided to allow easy configuration of what would break code and what wouldn't. For example, if someone is using a test and string manipulation to change code at test time, then the tool likely would need `format = false`.

# Motivation
[motivation]: #motivation

Rust doesn't have a general attribute for this (some more specific attributes do exist, such as `#[automatically_derived]`), and this would be useful for scenarios such as:
- `rust-analyzer`: Mark files as autogenerated and discourage users from editing them
- `rustfmt`: Don't format files marked as autogenerated by default
Copy link
Contributor

Choose a reason for hiding this comment

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

Auto-generated code should get formatting to be readable to humans, because the generator sure doesn't need to duplicate all formatting logic into itself.

- `clippy` and `rustc`: Suppress lints in autogenerated files by default - in other words, reset the list of lints to empty outside of lints marked explicitly in the file
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be better for lints to be "summarized" instead of suppressed entirely. Tell me one spot it appears (probably the first occurrence), and tell me it appears X many other times. Generators should generate code that is lint free, either because it does whatever to not fire the lint, or because it issues an allow in an appropriate location.

Choose a reason for hiding this comment

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

Generators should generate code that is lint free, either because it does whatever to not fire the lint, or because it issues an allow in an appropriate location.

As a data point, I tend to turn on all clippy lints and then opt out of the ones I don't want as triggering makes me aware of them, which generated code generally doesn't anticipate, and, when it runs afoul of that, I move the generated code into a submodule where I allow all to silence the lints because I don't think it's reasonable to bother dependency authors about that use-case.

(Which leaves the biggest annoyance as when I have to switch dependencies or move the generated code out into its own crate because it's violating the #![forbid(unsafe_code)] that I put in my crates for the non-generated code.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Something like that sounds fine.

To elaborate a little more on why a "summary" is better than normal warnings: when working on the generator I want to generate some code, then have RA or Clippy or whatever tool inspect it. When doing this, I don't need to know all 4500 warnings and their line numbers. That's too much noise. Even with the most compact of current output options that's just too much. What I need to know is that one warning is triggered 1500 times and another warning is triggered 3000 times, and then I can go adjust the generator to fix what is actually just two problems, not 4500.

Choose a reason for hiding this comment

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

I don't have a specific example right now, but I have frequently run into code generators that used to be lint free, then you upgrade Rust and it's not anymore. That can happen because no maintenance or even just pinning Rust for an MSRV via rust-toolchain.toml.

I'm not sure what I think of the proposal, but it would definitely be nice if generated code didn't spam warnings because it e.g. doesn't use some new idiom. I feel like a real answer to that however would require having some sort of warnings level for generated code because it is indeed the case that many are important. I'll be honest that this specific point is why I took note that this RFC exists; literally everything else isn't a pain point I've managed to hit. I think it's also worth noting that the problem is slightly more general: you will see it e.g. sometimes with normal macro_rules.

It really feels like #[allow(autogenerated_safe)] or something is the answer. But, the reason I'm replying to this subthread is that a summary doesn't actually fix the problem for downstream users, it just punts my specific problem with code generators as a consumer--I still have to go silence it and leave the ugly "because generated code" comments.

Copy link

@ssokolow ssokolow Aug 30, 2025

Choose a reason for hiding this comment

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

It really feels like #[allow(autogenerated_safe)] or something is the answer. But, the reason I'm replying to this subthread is that a summary doesn't actually fix the problem for downstream users, it just punts my specific problem with code generators as a consumer--I still have to go silence it and leave the ugly "because generated code" comments.

*nod* It reminds me of how I've become banner blind to the multiple screenfuls of "Please badger your upstream to bump their Flatpak runtime dependency. This one is no longer maintained" messages that I get when I run flatpak upgrade. (Which are just silenced by frontends like KDE Discover.)

Are there other types of warnings buried in there? Probably. I know it used to be giving me "This Flatpak is unmaintained: Please install Nix and use our Nix flake for jstest-gtk" before I confirmed that KDE's Gamepads control panel is no longer lagging and dropped jstest-gtk as an app that'll eventually have to accept GTK 4's GNOME-isms.

(I generally trust my strategy for hand-tightening the sandbox on every flatpak'd app I install and applying a policy that an application can have network access or non-portalized access to filesystem locations outside its ~/.var/app/` folder but not both.)

What I do know is that I've got better things to do than play volunteer messenger and unpaid advocate.

- `clippy`: Add a `clippy::restriction` lint to not allow using autogenerated code. Also, add a lint to encourage providing the `by` and `regen_hint` parameters.
- `rustc`: Reduce error verbosity in autogenerated files and reference the tool that created the file, particularly for syntax errors
- `rustdoc`: Mark autogenerated files as such and state that the API may change over time
- Code line counters: Don't count lines of code in autogenerated files

This is just a rough idea of what this RFC could enable, and not by any means an exhaustive list.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

When writing tools that produce Rust code, they should add a `#[autogenerated]` attribute to the generated code. This attribute can be added onto any item, such as modules, but also functions, constants, etc. The attribute takes many parameters:
Copy link
Contributor

Choose a reason for hiding this comment

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

The attribute takes many parameters:

What is the advantage of these parameters over separate, existing or new, tool attributes? It seems to me that combining them may have a slight benefit in readability, but has the large cost that it's a new mechanism that needs a schema and/or namespacing strategy. As you wrote yourself:

One problem that could arise is tools having their own parameters for the attribute that aren't specified, and it's unclear how to work around that.

I think it would make sense to keep the parameters down to ones which relate to the generation itself rather than setting any policy:

#[autogenerated(
	by = "example tool", // The tool that created this item - required
	regen_hint = "./regen_example_tool", // A hint for how to regenerate the item - strongly encouraged to be provided
)]

Everything else should be separate tool attributes (like rustfmt::skip) possibly combined with a default behavior from those tools when they see any #[autogenerated] attribute. If there’s some reason this can't work, it should at least be discussed “Rationale and alternatives” section.

```rust
#[autogenerated(
by = "example tool", // The tool that created this item - required
suppress_lints = false, // Whether to suppress lints on this code - default true
format = true, // Whether to format these items - default false
regen_hint = "./regen_example_tool", // A hint for how to regenerate the item - strongly encouraged to be provided
document = true, // Whether to document the items or not - default true
rustdoc_marker = false, // Whether to have a special marker stating that the item is autogenerated
stable_api = true, // Whether the item has a stable API and shouldn't trigger lints for accessing it or it's subitems - default false
discourage_editing = false, // Whether IDEs and rust-analyzer should discourage editing this item. Default true.
)]
```

These parameters are used to configure the behavior of different tools. Think of this feature as a way to communicate to many different tools at once how this autogenerated item should be treated. This can also be used to communicate to IDEs and `rust-analyzer` that this item should be marked as autogenerated and that it shouldn't be edited.

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

This has no direct effect on how Rust is parsed. Instead, each tool will dictate how to understand the attribute based on what the tool is/does. Effects that commonly used tools would need to worry about were specified in the [motivation section](motivation). Most tools would need to worry about parsing the `by` attribute and whichever others affect the tool itself, and then use the `by` attribute for better error messages. For example, instead of:

```plain
error: expected one of `(`, `[`, or `{`, found `<eof>`
--> foobar.rs:2:1
|
2 | not rust
| ^ expected one of `(`, `[`, or `{`

error: aborting due to 1 previous error
```

This message could be output:

```plain
error: expected one of `(`, `[`, or `{`, found `<eof>`
--> foobar.rs:2:1 - generated by example tool (is there a bug in this tool?)
error: aborting due to 1 previous error
```

# Drawbacks
[drawbacks]: #drawbacks

There aren't many drawbacks - if a tool doesn't understand the attribute then it should just skip over it. One problem that could arise is tools having their own parameters for the attribute that aren't specified, and it's unclear how to work around that.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

Unlike many other languages with a similar feature, we have attributes which can be used for easy scoping like this. Other possible designs commonly used in other languages are a simple comment, such as Go's regex matching at the start of a file:
```regex
^// Code generated .* DO NOT EDIT.$
```
or C#'s:
```csharp
// <auto-generated>
// (arbitrary text, usually a long description of the file)
// </auto-generated>
```
However, these both have the same problem: All that tools can do is check if a file is autogenerated, and nothing else. The design being suggested in this RFC allows tools to configure themselves to work better with autogenerated files and even see which tool made them. Another problem with this comment approach is it only supports files, not arbitrary items.

This could in theory be done in a tool, however it works much better as a language feature for consistency's sake. Also, if it were done in a tool, where would it be placed? It wouldn't fit in any of the current ones given it's general and wouldn't be worth creating a new one for this. That's a bit of a nitpick however.

This also improves code readability, allowing users and tools alike to easily see which tool created the generated code and even roughly how it was created (through `regen_hint`).

Some things for individual tools do already exist such as `#[rustfmt::skip]`, however these of course apply only to individual tools meaning that a more general attribute would work well for this scenario.

# Prior art
Copy link
Contributor

Choose a reason for hiding this comment

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

A good addition to this section would be a summary of what effects these “generated” markers have on tools in other languages.

[prior-art]: #prior-art

Similar features have been implemented in other languages, however usually as just a comment. Examples for Go and C# (the only languages with a standard for autogenerated files I could find) were shared in [the above section](rationale-and-alternatives). Rust also already has [`#[automatically_derived]`](https://doc.rust-lang.org/reference/attributes/derive.html#the-automatically_derived-attribute) for marking derived implementations along with some attributes for individual tools to configure them as mentioned before.

# Unresolved questions
[unresolved-questions]: #unresolved-questions

1. How could other tools configure themselves for the autogenerated attribute? I'd like to answer this either before the RFC is merged or before stabilization. One answer could be just stating it's out of scope.
2. What if you want some of these attributes without marking a file as autogenerated? This is out of scope. Many of them already exist (e.g. `#[rustfmt::skip]`) and the ones that don't probably shouldn't exist outside of autogenerated files (e.g. `suppress_lints`, `discourage_editing`) or wouldn't make sense outside of these files (e.g. `stable_api` (partially exists inversely with `#[nonexhaustive]`), `rustdoc_marker`).

# Future possibilities
[future-possibilities]: #future-possibilities

A natural extension that would answer question number one in [unresolved questions](unresolved-questions) is having some kind of standard of connecting certain proc macros to this attribute, but that'd be a much larger/more important RFC. Another thing would be adding more attributes to this for configuring future tools.