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