Skip to content

Commit 75068cd

Browse files
authored
Merge pull request #2892 from JonathanBrouwer/attributes
Add a chapter about attribute parsing
2 parents 05ff92e + 57857b9 commit 75068cd

3 files changed

Lines changed: 75 additions & 1 deletion

File tree

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
- [Lang Items](./lang-items.md)
130130
- [The HIR (High-level IR)](./hir.md)
131131
- [Lowering AST to HIR](./hir/lowering.md)
132+
- [Attribute Parsing](./hir/attribute-parsing.md)
132133
- [Debugging](./hir/debugging.md)
133134
- [Ambig/Unambig Types and Consts](./ambig-unambig-ty-and-consts.md)
134135
- [The THIR (Typed High-level IR)](./thir.md)

src/attributes.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ Attributes come in two types: *inert* (or *built-in*) and *active* (*non-builtin
55
## Builtin/inert attributes
66

77
These attributes are defined in the compiler itself, in
8-
[`compiler/rustc_feature/src/builtin_attrs.rs`][builtin_attrs].
8+
[`compiler/rustc_feature/src/builtin_attrs.rs`][builtin_attrs] and in the [attribute parsers][attr_parsing].
99

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

1212
[builtin_attrs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/builtin_attrs/index.html
13+
[attr_parsing]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
1314

1415
These attributes have several important characteristics:
1516
* They are always in scope, and do not participate in typical path-based resolution.
@@ -20,6 +21,10 @@ These attributes have several important characteristics:
2021
For example, lint-related code explicitly checks for `#[allow]`, `#[warn]`, `#[deny]`, and
2122
`#[forbid]`, rather than the behavior coming from the expansion of the attributes themselves.
2223

24+
For more information on these attributes, see the chapter about [attribute parsing][attr-parsing-chapter].
25+
26+
[attr-parsing-chapter]: ./hir/attribute-parsing.md
27+
2328
## 'Non-builtin'/'active' attributes
2429

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

src/hir/attribute-parsing.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Attribute Parsing
2+
3+
Attributes come in two types: *inert* (or *built-in*) and *active* (*non-builtin*).
4+
Inert attributes are parsed during AST lowering to the HIR, while active attributes are expanded during expansion.
5+
For more information about the difference, see [the page about attributes][attributes_page].
6+
7+
During [AST lowering][lowering], inert attributes are converted from an unparsed `TokenStream` to a parsed representation.
8+
The parsed form [is defined][hir_attrs] in the `rustc_hir` crate, and the parsers [are defined][attr_parsing] in the `rustc_attr_parsing` crate.
9+
10+
## A step by step guide of adding a new inert attribute parser
11+
12+
1. Add the attribute name to the [BUILTIN_ATTRIBUTES][builtin_attributes]. This list defines the set of inert attributes.
13+
2. Add a variant to `AttributeKind` in the [hir definition of attributes][hir_attrs]. This will define the parsed form of the attribute that the attribute parser will produce.
14+
3. Add your variant to the match in `rustc_hir/attrs/encode_cross_crate.rs`, which should return whether your attribute should be visible in dependent crates.
15+
This is usually `No` for code-gen related attributes, and `Yes` for analysis related attributes.
16+
4. Create a new struct in `rustc_attr_parsing/attributes/*.rs` that will hold the state for your attribute parser. For most parsers this will be an empty struct.
17+
5. Implement one of the attribute parsing traits for this struct:
18+
* `NoArgsAttributeParser` for attributes that may appear only a single time per item, and take no arguments. For example `#[no_mangle]`.
19+
* `SingleAttributeParser` for attributes that may appear only a single time per item, and may take arguments. For example `#[inline(...)]`.
20+
* `CombineAttributeParser` for attributes that may appear multiple times per item, and whose occurrences are combined into a single parsed attribute. For example `#[feature(...)]`.
21+
* `AttributeParser` for attributes that don't fit cleanly into one of the above traits. For example when multiple different attributes need to be combined into a parsed attribute (such as `#[stable]` and `#[unstable]`),
22+
6. Implement the associated constants of the attribute parser trait that you chose. Not all of these constants are applicable to all traits.
23+
* `PATH`: The name of the attribute that is added
24+
* `TEMPLATE`: A structural description of your attribute, used only to guide diagnostics. Use the [template!][template] macro to construct this type.
25+
* `STABILITY`: Whether your attribute is stable (ungated) or unstable (feature gated).
26+
* `ALLOWED_TARGETS`: Which targets the attribute may appear on. For new attributes, all incorrect targets should error, so use the `AllowedTargets::AllowList` for new attributes, and only use `Policy::Allow` to allow targets.
27+
* `ON_DUPLICATE`: Controls the behavior when the attribute incorrectly appears multiple times. Should usually be `OnDuplicate::Error` for new attributes, this is the default.
28+
* `SAFETY`: Controls whether using the attribute requires marking it as unsafe, such as `#[unsafe(no_mangle)]`. Should usually be `AttributeSafety::Normal`, this is the default.
29+
7. Implement the associated parsing functions. In general, you will be provided with an `AcceptContext` providing the context in which the attribute is used, and `ArgParser` which contains the arguments that were provided to the attribute.
30+
When the arguments are incorrect, you should emit an error using the suite of `cx.adcx().expected_*` function and then `return None`.
31+
Sometimes these two steps can be combined, using the `cx.expect_*` suite of functions, on which you can use the `?` operator to early-return.
32+
The attribute parser should do all checks that are possible to ensure the attribute is valid, before returning the parsed attribute.
33+
8. Register the attribute parser by adding it to [the list of attribute parsers][attribute_parsers].
34+
9. If not all validation could be done in the attribute parser because not enough information was available, add an extra validation step in `rustc_passes/check_attr.rs`. This code is ran right after the HIR is fully built, so it has access to the full HIR for analysis.
35+
36+
## Using the attribute parsers
37+
38+
There are two ways of using an attribute parser, referred to as `Early` and `Late` attribute parsing.
39+
40+
### Late (the usual way)
41+
To use the parsed representation after the HIR is built, use the `find_attr!` macro [defined here][find_attr].
42+
This macro can be used in the following ways:
43+
* `find_attr!(tcx, <def_id>, Variant(...))` to find an attribute on a def id.
44+
* `find_attr!(tcx, <hir_id>, Variant(...))` to find an attribute on a HIR id.
45+
* `find_attr!(tcx, crate, Variant(...))` to find an attribute on the current crate.
46+
* `find_attr!(attrs, Variant(...))` to find an attribute in attrs, a `&[hir::Attribute]`.
47+
48+
`Variant` is a pattern matching one of the `AttributeKind` variants, and can take one of the following shapes:
49+
* `find_attr!(..., Variant)` will return a boolean representing whether the attribute is present.
50+
* `find_attr!(..., Variant(a, b, _) => (a, b))` will return the value after the `=>`, constructed from fields bound by the pattern.
51+
52+
### Early (also referred to as "limited")
53+
To use the parsed representation before the HIR is built, you usually need to use the [`AttributeParser::parse_limited`][parse_limited] function.
54+
This function takes a slice of `&[ast::Attribute]`s, and the name of an attribute you're interested in, and parses the attribute if it is present.
55+
56+
No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while errors will be emitted as a delayed bugs.
57+
In other words, we expect attributes parsed with parse_limited to be reparsed later during ast lowering where we do emit the errors
58+
59+
60+
[attributes_page]: ../attributes.md
61+
[lowering]: ./lowering.md
62+
[hir_attrs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/attrs/index.html
63+
[attr_parsing]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/
64+
[attribute_parsers]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/context/static.ATTRIBUTE_PARSERS.html
65+
[builtin_attributes]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/builtin_attrs/static.BUILTIN_ATTRIBUTES.html
66+
[template]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/macro.template.html
67+
[find_attr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/macro.find_attr.html
68+
[parse_limited]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/interface/struct.AttributeParser.html#method.parse_limited

0 commit comments

Comments
 (0)