|
| 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