|
| 1 | +- Start Date: 2012-03-20 |
| 2 | +- RFC PR #: 3 |
| 3 | +- Rust Issue #: TBD |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Rust currently has an attribute usage lint but it does not work particularly |
| 8 | +well. This RFC proposes a new implementation strategy that should make it |
| 9 | +significantly more useful. |
| 10 | + |
| 11 | +# Motivation |
| 12 | + |
| 13 | +The current implementation has two major issues: |
| 14 | + |
| 15 | ++ There are very limited warnings for valid attributes that end up in the |
| 16 | +wrong place. Something like this will be silently ignored: |
| 17 | +```rust |
| 18 | +#[deriving(Clone)]; // Shouldn't have put a ; here |
| 19 | +struct Foo; |
| 20 | + |
| 21 | +#[ignore(attribute-usage)] // Should have used #[allow(attribute-usage)] instead! |
| 22 | +mod bar { |
| 23 | + //... |
| 24 | +} |
| 25 | +``` |
| 26 | ++ `ItemDecorators` can now be defined outside of the compiler, and there's no |
| 27 | +way to tag them and associated attributes as valid. Something like this |
| 28 | +requires an `#[allow(attribute-usage)]`: |
| 29 | +```rust |
| 30 | +#[feature(phase)]; |
| 31 | +#[phase(syntax, link)] |
| 32 | +extern crate some_orm; |
| 33 | + |
| 34 | +#[ormify] |
| 35 | +pub struct Foo { |
| 36 | + #[column(foo_)] |
| 37 | + #[primary_key] |
| 38 | + foo: int |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +# Detailed design |
| 43 | + |
| 44 | +The current implementation is implemented as a simple fold over the AST, |
| 45 | +comparing attributes against a whitelist. Crate-level attributes use a separate |
| 46 | +whitelist, but no other distinctions are made. |
| 47 | + |
| 48 | +This RFC would change the implementation to actually track which attributes are |
| 49 | +used during the compilation process. `syntax::ast::Attribute_` would be |
| 50 | +modified to add an ID field: |
| 51 | +```rust |
| 52 | +pub struct AttrId(uint); |
| 53 | + |
| 54 | +pub struct Attribute_ { |
| 55 | + id: AttrId, |
| 56 | + style: AttrStyle, |
| 57 | + value: @MetaItem, |
| 58 | + is_sugared_doc: bool, |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +`syntax::ast::parse::ParseSess` will generate new `AttrId`s on demand. I |
| 63 | +believe that attributes will only be created during parsing and expansion, and |
| 64 | +the `ParseSess` is accessible in both. |
| 65 | + |
| 66 | +The `AttrId`s will be used to create a side table of used attributes. This will |
| 67 | +most likely be a thread local to make it easily accessible during all stages of |
| 68 | +compilation by calling a function in `syntax::attr`: |
| 69 | +```rust |
| 70 | +fn mark_used(attr: &Attribute) { } |
| 71 | +``` |
| 72 | + |
| 73 | +The `attribute-usage` lint would run at the end of compilation and warn on all |
| 74 | +attributes whose ID does not appear in the side table. |
| 75 | + |
| 76 | +One interesting edge case is attributes like `doc` that are used, but not in |
| 77 | +the normal compilation process. There could either be a separate fold pass to |
| 78 | +mark all `doc` attributes as used or `doc` could simply be whitelisted in the |
| 79 | +`attribute-usage` lint. |
| 80 | + |
| 81 | +Attributes in code that has been eliminated with `#[cfg()]` will not be linted, |
| 82 | +but I feel that this is consistent with the way `#[cfg()]` works in general |
| 83 | +(e.g. the code won't be type-checked either). |
| 84 | + |
| 85 | +# Alternatives |
| 86 | + |
| 87 | +An alternative would be to rewrite `rustc::middle::lint` to robustly check |
| 88 | +that attributes are used where they're supposed to be. This will be fairly |
| 89 | +complex and be prone to failure if/when more nodes are added to the AST. This |
| 90 | +also doesn't solve motivation #2, which would require externally loaded lint |
| 91 | +support. |
| 92 | + |
| 93 | +# Unresolved questions |
| 94 | + |
| 95 | ++ This implementation doesn't allow for a distinction between "unused" and |
| 96 | +"unknown" attributes. The `#[phase(syntax)]` crate loading infrastructure could |
| 97 | +be extended to pull a list of attributes from crates to use in the lint pass, |
| 98 | +but I'm not sure if the extra complexity is worth it. |
| 99 | ++ The side table could be threaded through all of the compilation stages that |
| 100 | +need to use it instead of being a thread local. This would probably require |
| 101 | +significantly more work than the thread local approach, however. The thread |
| 102 | +local approach should not negatively impact any future parallelization work as |
| 103 | +each thread can keep its own side table, which can be merged into one for the |
| 104 | +lint pass. |
| 105 | + |
0 commit comments