Skip to main content

rustc_attr_parsing/attributes/
lint.rs

1use rustc_attr_ir::AttributeKind;
2use rustc_attr_ir::lint::{LintCheck, LintCheckKind};
3use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target};
4use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES;
5use rustc_span::sym;
6use thin_vec::ThinVec;
7
8use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability};
9use crate::context::{AcceptContext, ExpectStringLiteral, FinalizeContext};
10use crate::parser::ArgParser;
11use crate::target_checking::AllowedTargets;
12use crate::target_checking::Policy::{Allow, Warn};
13use crate::{AttributeTemplate, diagnostics, template};
14
15const LINT_TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: Some(&["lint1", "lint1, lint2, ...",
                    r#"lint1, lint2, lint3, reason = "...""#]),
    one_of: &[],
    name_value_str: None,
    docs: Some("https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"),
}template!(
16    List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
17    "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
18);
19
20#[derive(#[automatically_derived]
impl ::core::default::Default for LintParser {
    #[inline]
    fn default() -> Self {
        LintParser { lints: ::core::default::Default::default() }
    }
}Default, #[automatically_derived]
impl ::core::fmt::Debug for LintParser {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "LintParser",
            "lints", &&self.lints)
    }
}Debug)]
21pub(crate) struct LintParser {
22    lints: ThinVec<LintCheck>,
23}
24
25impl LintParser {
26    fn parse(&mut self, kind: LintCheckKind, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
27        let attr_span = cx.attr_span;
28        let attr_id = cx.attr_id.expect("no `AttrId` for lint attribute");
29
30        if let Some(list) = cx.expect_list(args, cx.attr_span) {
31            let mut parsers = list.sub_parsers();
32            // Optionally, the last (and only the last)
33            // element can be `reason = "reason"`
34            let reason = try {
35                let p = parsers.last()?.meta_item()?;
36                let nv = p.args().as_name_value()?;
37                if !p.path().word_is(sym::reason) {
38                    cx.emit_err(diagnostics::MalformedAttribute {
39                        span: p.span(),
40                        sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(p.span()),
41                    });
42                } else {
43                    parsers = &parsers[..(parsers.len() - 1)];
44                }
45
46                let reason = nv.expect_string_literal(cx)?;
47                reason
48            };
49
50            for item in parsers {
51                if let Some(p) = item.meta_item() {
52                    match p.args() {
53                        ArgParser::NoArgs => {
54                            let (tool, lint_name, rest) = match &*p.path().0.segments {
55                                [] => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
56                                [lint_name] => (None, lint_name.ident.name, None),
57                                [tool, lint_name] => (Some(tool.ident), lint_name.ident.name, None),
58                                [tool, lint_name, rest @ ..] => {
59                                    let rest = rest
60                                        .iter()
61                                        .map(|s| s.ident.name)
62                                        .collect::<Vec<_>>()
63                                        .into();
64                                    (Some(tool.ident), lint_name.ident.name, Some(rest))
65                                }
66                            };
67
68                            self.lints.push(LintCheck {
69                                tool,
70                                lint_name,
71                                lint_span: p.span(),
72                                kind,
73                                attr_id,
74                                reason,
75                                attr_span,
76                                rest,
77                            })
78                        }
79                        // We're found a `reason = "reason"` but we're not the last element.
80                        ArgParser::NameValue(nv) if p.path().word_is(sym::reason) => {
81                            cx.emit_err(diagnostics::MalformedAttribute {
82                                span: p.span(),
83                                sub: diagnostics::MalformedAttributeSub::ReasonMustComeLast(
84                                    item.span(),
85                                ),
86                            });
87                            nv.expect_string_literal(cx);
88                        }
89                        ArgParser::NameValue(_) | ArgParser::List(_) => {
90                            cx.emit_err(diagnostics::MalformedAttribute {
91                                span: p.span(),
92                                sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(
93                                    item.span(),
94                                ),
95                            });
96                        }
97                    }
98                } else {
99                    cx.emit_err(diagnostics::MalformedAttribute {
100                        span: item.span(),
101                        sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(item.span()),
102                    });
103                }
104            }
105            if parsers.is_empty() {
106                cx.emit_lint(
107                    UNUSED_ATTRIBUTES,
108                    diagnostics::Unused {
109                        attr_span,
110                        note: if list.is_empty() {
111                            diagnostics::UnusedNote::EmptyList { name: kind.sym() }
112                        } else {
113                            diagnostics::UnusedNote::NoLints { name: kind.sym() }
114                        },
115                    },
116                    attr_span,
117                );
118            }
119        }
120    }
121}
122
123impl AttributeParser for LintParser {
124    const ATTRIBUTES: AcceptMapping<Self> = &[
125        (&[sym::allow], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| {
126            this.parse(LintCheckKind::Allow, cx, args)
127        }),
128        (&[sym::warn], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| {
129            this.parse(LintCheckKind::Warn, cx, args)
130        }),
131        (&[sym::deny], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| {
132            this.parse(LintCheckKind::Deny, cx, args)
133        }),
134        (&[sym::forbid], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| {
135            this.parse(LintCheckKind::Forbid, cx, args)
136        }),
137        (&[sym::expect], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| {
138            this.parse(LintCheckKind::Expect, cx, args)
139        }),
140    ];
141    const ALLOWED_TARGETS: AllowedTargets<'_> = {
142        AllowedTargets::AllowList(&[
143            Allow(Target::ExternCrate),
144            Allow(Target::Use),
145            Allow(Target::Static),
146            Allow(Target::Const),
147            Allow(Target::Fn),
148            Allow(Target::Closure),
149            Allow(Target::Mod),
150            Allow(Target::ForeignMod),
151            Allow(Target::GlobalAsm),
152            Allow(Target::TyAlias),
153            Allow(Target::Enum),
154            Allow(Target::Variant),
155            Allow(Target::Struct),
156            Allow(Target::Field),
157            Allow(Target::Union),
158            Allow(Target::Trait),
159            Allow(Target::TraitAlias),
160            Allow(Target::Impl { of_trait: false }),
161            Allow(Target::Impl { of_trait: true }),
162            Allow(Target::Expression),
163            Allow(Target::Statement),
164            Allow(Target::Arm),
165            Allow(Target::AssocConst(AssocCtxt::Impl { of_trait: false })),
166            Allow(Target::AssocConst(AssocCtxt::Trait)),
167            Allow(Target::AssocConst(AssocCtxt::Impl { of_trait: true })),
168            Allow(Target::Method(MethodKind::Inherent)),
169            Allow(Target::Method(MethodKind::Trait { body: false })),
170            Allow(Target::Method(MethodKind::Trait { body: true })),
171            Allow(Target::Method(MethodKind::TraitImpl)),
172            Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: false })),
173            Allow(Target::AssocTy(AssocCtxt::Trait)),
174            Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: true })),
175            Allow(Target::ForeignFn),
176            Allow(Target::ForeignStatic),
177            Allow(Target::ForeignTy),
178            Allow(Target::MacroDef),
179            Allow(Target::Param),
180            Allow(Target::PatField),
181            Allow(Target::ExprField),
182            Allow(Target::Crate),
183            Allow(Target::Delegation { mac: false }),
184            Allow(Target::Delegation { mac: true }),
185            Allow(Target::ConstParam),
186            Allow(Target::LifetimeParam),
187            Allow(Target::TypeParam),
188            Allow(Target::Loop),
189            Allow(Target::ForLoop),
190            Allow(Target::While),
191            Allow(Target::Break),
192            Warn(Target::MacroCall),
193        ])
194    };
195    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
196        if self.lints.is_empty() { None } else { Some(AttributeKind::LintCheck(self.lints)) }
197    }
198}