Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document inert vs active attributes #1110

Merged
merged 12 commits into from
Jun 23, 2024
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
- [Lexing and Parsing](./the-parser.md)
- [Macro expansion](./macro-expansion.md)
- [Name resolution](./name-resolution.md)
- [Attributes](./attributes.md)
- [`#[test]` Implementation](./test-implementation.md)
- [Panic Implementation](./panic-implementation.md)
- [AST Validation](./ast-validation.md)
Expand Down
50 changes: 50 additions & 0 deletions src/attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Attributes

Attributes come in two types: *inert* (or *built-in*) and *active* (*non-builtin*).

## Builtin/inert attributes

These attributes are defined in the compiler itself, in
[`compiler/rustc_feature/src/builtin_attrs.rs`][builtin_attrs].

Examples include `#[allow]` and `#[macro_use]`.

[builtin_attrs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/builtin_attrs/index.html

These attributes have several important characteristics:
* They are always in scope, and do not participate in typical path-based resolution.
* They cannot be renamed. For example, `use allow as foo` will compile, but writing `#[foo]` will
produce an error.
* They are 'inert', meaning they are left as-is by the macro expansion code.
As a result, any behavior comes as a result of the compiler explicitly checking for their presence.
For example, lint-related code explicitly checks for `#[allow]`, `#[warn]`, `#[deny]`, and
`#[forbid]`, rather than the behavior coming from the expansion of the attributes themselves.

## 'Non-builtin'/'active' attributes

These attributes are defined by a crate - either the standard library, or a proc-macro crate.

**Important**: Many non-builtin attributes, such as `#[derive]`, are still considered part of the
core Rust language. However, they are **not** called 'builtin attributes', since they have a
corresponding definition in the standard library.

Definitions of non-builtin attributes take two forms:

1. Proc-macro attributes, defined via a function annotated with `#[proc_macro_attribute]` in a
proc-macro crate.
2. AST-based attributes, defined in the standard library. These attributes have special 'stub'
macros defined in places like [`library/core/src/macros/mod.rs`][core_macros].

[core_macros]: https://github.com/rust-lang/rust/blob/master/library/core/src/macros/mod.rs

These definitions exist to allow the macros to participate in typical path-based resolution - they
can be imported, re-exported, and renamed just like any other item definition. However, the body of
the definition is empty. Instead, the macro is annotated with the `#[rustc_builtin_macro]`
attribute, which tells the compiler to run a corresponding function in `rustc_builtin_macros`.
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a link to the crate's API docs? That way it is easy for people to see more about it, and it will make our CI fail if the name ever changes.


All non-builtin attributes have the following characteristics:
* Like all other definitions (e.g. structs), they must be brought into scope via an import.
Many standard library attributes are included in the prelude - this is why writing `#[derive]`
works without an import.
* They participate in macro expansion. The implementation of the macro may leave the attribute
target unchanged, modify the target, produce new AST nodes, or remove the target entirely.
3 changes: 3 additions & 0 deletions src/test-implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

<!-- toc -->



Many Rust programmers rely on a built-in attribute called `#[test]`. All
you have to do is mark a function and include some asserts like so:


```rust,ignore
#[test]
fn my_test() {
Expand Down