Skip to main content

rustc_attr_parsing/attributes/
crate_level.rs

1use rustc_attr_ir::WindowsSubsystemKind;
2use rustc_data_structures::fx::FxIndexSet;
3use rustc_feature::AttributeStability;
4use rustc_lint_defs::builtin::{DUPLICATE_TOOLS, UNKNOWN_CRATE_TYPES};
5use rustc_span::Symbol;
6use rustc_span::edit_distance::find_best_match_for_name_with_substrings;
7use rustc_structures::CrateType;
8
9use super::prelude::*;
10use crate::diagnostics::{
11    DuplicateTool, ToolReserved, UnknownCrateTypes, UnknownCrateTypesSuggestion,
12};
13
14pub(crate) struct CrateNameParser;
15
16impl SingleAttributeParser for CrateNameParser {
17    const PATH: &[Symbol] = &[sym::crate_name];
18    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
19    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["name"]),
    docs: None,
}template!(NameValueStr: "name");
20    const ALLOWED_TARGETS: AllowedTargets<'_> =
21        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
22    const STABILITY: AttributeStability = AttributeStability::Stable;
23
24    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
25        let n = cx.expect_name_value(args, cx.attr_span, None)?;
26
27        let name = cx.expect_string_literal(n)?;
28
29        Some(AttributeKind::CrateName { name, name_span: n.value_span, attr_span: cx.attr_span })
30    }
31}
32
33pub(crate) struct CrateTypeParser;
34
35impl CombineAttributeParser for CrateTypeParser {
36    const PATH: &[Symbol] = &[sym::crate_type];
37    type Item = CrateType;
38    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::CrateType(items);
39    const ALLOWED_TARGETS: AllowedTargets<'_> =
40        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
41    const TEMPLATE: AttributeTemplate =
42        crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["crate type"]),
    docs: Some("https://doc.rust-lang.org/reference/linkage.html"),
}template!(NameValueStr: "crate type", "https://doc.rust-lang.org/reference/linkage.html");
43    const STABILITY: AttributeStability = AttributeStability::Stable;
44
45    fn extend(
46        cx: &mut AcceptContext<'_, '_>,
47        args: &ArgParser,
48    ) -> impl IntoIterator<Item = Self::Item> {
49        let n = cx.expect_name_value(args, cx.attr_span, None)?;
50
51        let crate_type = cx.expect_string_literal(n)?;
52
53        let Ok(crate_type) = crate_type.try_into() else {
54            // We don't error on invalid `#![crate_type]` when not applied to a crate
55            if cx.shared.target == Target::Crate {
56                let candidate = find_best_match_for_name_with_substrings(
57                    &CrateType::all_stable().iter().map(|(name, _)| *name).collect::<Vec<_>>(),
58                    crate_type,
59                    Some(5),
60                );
61                let span = n.value_span;
62                cx.emit_lint(
63                    UNKNOWN_CRATE_TYPES,
64                    UnknownCrateTypes {
65                        sugg: candidate.map(|s| UnknownCrateTypesSuggestion { span, snippet: s }),
66                    },
67                    span,
68                );
69            }
70            return None;
71        };
72
73        Some(crate_type)
74    }
75}
76
77pub(crate) struct RecursionLimitParser;
78
79impl SingleAttributeParser for RecursionLimitParser {
80    const PATH: &[Symbol] = &[sym::recursion_limit];
81    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
82    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: Some("https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"),
}template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute");
83    const ALLOWED_TARGETS: AllowedTargets<'_> =
84        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
85    const STABILITY: AttributeStability = AttributeStability::Stable;
86
87    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
88        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
89
90        Some(AttributeKind::RecursionLimit { limit: cx.parse_limit_int(nv)? })
91    }
92}
93
94pub(crate) struct MoveSizeLimitParser;
95
96impl SingleAttributeParser for MoveSizeLimitParser {
97    const PATH: &[Symbol] = &[sym::move_size_limit];
98    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
99    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
100    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::large_assignments;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::large_assignments,
        notes: &[],
    }
}unstable!(large_assignments);
101
102    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
103        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
104
105        Some(AttributeKind::MoveSizeLimit { limit: cx.parse_limit_int(nv)? })
106    }
107}
108
109pub(crate) struct TypeLengthLimitParser;
110
111impl SingleAttributeParser for TypeLengthLimitParser {
112    const PATH: &[Symbol] = &[sym::type_length_limit];
113    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
114    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
115    const ALLOWED_TARGETS: AllowedTargets<'_> =
116        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
117    const STABILITY: AttributeStability = AttributeStability::Stable;
118
119    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
120        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
121
122        Some(AttributeKind::TypeLengthLimit { limit: cx.parse_limit_int(nv)? })
123    }
124}
125
126pub(crate) struct PatternComplexityLimitParser;
127
128impl SingleAttributeParser for PatternComplexityLimitParser {
129    const PATH: &[Symbol] = &[sym::pattern_complexity_limit];
130    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
131    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
132    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::rustc_attrs;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::rustc_attrs,
        notes: &["the `pattern_complexity_limit` attribute is used for rustc unit tests"],
    }
}unstable!(
133        rustc_attrs,
134        "the `pattern_complexity_limit` attribute is used for rustc unit tests"
135    );
136
137    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
138        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
139
140        Some(AttributeKind::PatternComplexityLimit { limit: cx.parse_limit_int(nv)? })
141    }
142}
143
144pub(crate) struct NoCoreParser;
145
146impl NoArgsAttributeParser for NoCoreParser {
147    const PATH: &[Symbol] = &[sym::no_core];
148    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
149    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::no_core;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::no_core,
        notes: &[],
    }
}unstable!(no_core);
150    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoCore;
151}
152
153pub(crate) struct NoStdParser;
154
155impl NoArgsAttributeParser for NoStdParser {
156    const PATH: &[Symbol] = &[sym::no_std];
157    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
158    const ALLOWED_TARGETS: AllowedTargets<'_> =
159        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
160    const STABILITY: AttributeStability = AttributeStability::Stable;
161    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoStd;
162}
163
164pub(crate) struct NoMainParser;
165
166impl NoArgsAttributeParser for NoMainParser {
167    const PATH: &[Symbol] = &[sym::no_main];
168    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
169    const ALLOWED_TARGETS: AllowedTargets<'_> =
170        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
171    const STABILITY: AttributeStability = AttributeStability::Stable;
172    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoMain;
173}
174
175pub(crate) struct RustcCoherenceIsCoreParser;
176
177impl NoArgsAttributeParser for RustcCoherenceIsCoreParser {
178    const PATH: &[Symbol] = &[sym::rustc_coherence_is_core];
179    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
180    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::rustc_attrs;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::rustc_attrs,
        notes: &[],
    }
}unstable!(rustc_attrs);
181    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCoherenceIsCore;
182}
183
184pub(crate) struct WindowsSubsystemParser;
185
186impl SingleAttributeParser for WindowsSubsystemParser {
187    const PATH: &[Symbol] = &[sym::windows_subsystem];
188    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
189    const ALLOWED_TARGETS: AllowedTargets<'_> =
190        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
191    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["windows", "console"]),
    docs: Some("https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"),
}template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute");
192    const STABILITY: AttributeStability = AttributeStability::Stable;
193
194    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
195        let nv = cx.expect_name_value(args, cx.inner_span, Some(sym::windows_subsystem))?;
196
197        let kind = match nv.value_as_str() {
198            Some(sym::console) => WindowsSubsystemKind::Console,
199            Some(sym::windows) => WindowsSubsystemKind::Windows,
200            Some(_) | None => {
201                cx.adcx().expected_specific_argument_strings(
202                    nv.value_span,
203                    &[sym::console, sym::windows],
204                );
205                return None;
206            }
207        };
208
209        Some(AttributeKind::WindowsSubsystem(kind))
210    }
211}
212
213pub(crate) struct PanicRuntimeParser;
214
215impl NoArgsAttributeParser for PanicRuntimeParser {
216    const PATH: &[Symbol] = &[sym::panic_runtime];
217    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
218    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::panic_runtime;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::panic_runtime,
        notes: &[],
    }
}unstable!(panic_runtime);
219    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PanicRuntime;
220}
221
222pub(crate) struct NeedsPanicRuntimeParser;
223
224impl NoArgsAttributeParser for NeedsPanicRuntimeParser {
225    const PATH: &[Symbol] = &[sym::needs_panic_runtime];
226    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
227    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::needs_panic_runtime;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::needs_panic_runtime,
        notes: &[],
    }
}unstable!(needs_panic_runtime);
228    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsPanicRuntime;
229}
230
231pub(crate) struct ProfilerRuntimeParser;
232
233impl NoArgsAttributeParser for ProfilerRuntimeParser {
234    const PATH: &[Symbol] = &[sym::profiler_runtime];
235    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
236    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::profiler_runtime;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::profiler_runtime,
        notes: &[],
    }
}unstable!(profiler_runtime);
237    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProfilerRuntime;
238}
239
240pub(crate) struct NoBuiltinsParser;
241
242impl NoArgsAttributeParser for NoBuiltinsParser {
243    const PATH: &[Symbol] = &[sym::no_builtins];
244    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
245    const ALLOWED_TARGETS: AllowedTargets<'_> =
246        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
247    const STABILITY: AttributeStability = AttributeStability::Stable;
248    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins;
249}
250
251pub(crate) struct RustcPreserveUbChecksParser;
252
253impl NoArgsAttributeParser for RustcPreserveUbChecksParser {
254    const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks];
255    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
256    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::rustc_attrs;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::rustc_attrs,
        notes: &[],
    }
}unstable!(rustc_attrs);
257    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks;
258}
259
260pub(crate) struct RustcNoImplicitBoundsParser;
261
262impl NoArgsAttributeParser for RustcNoImplicitBoundsParser {
263    const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds];
264    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
265    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::rustc_attrs;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::rustc_attrs,
        notes: &[],
    }
}unstable!(rustc_attrs);
266    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds;
267}
268
269pub(crate) struct DefaultLibAllocatorParser;
270
271impl NoArgsAttributeParser for DefaultLibAllocatorParser {
272    const PATH: &[Symbol] = &[sym::default_lib_allocator];
273    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
274    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::allocator_internals;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::allocator_internals,
        notes: &[],
    }
}unstable!(allocator_internals);
275    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator;
276}
277
278pub(crate) struct FeatureParser;
279
280impl CombineAttributeParser for FeatureParser {
281    const PATH: &[Symbol] = &[sym::feature];
282    type Item = Ident;
283    const CONVERT: ConvertFn<Self::Item> = AttributeKind::Feature;
284    const ALLOWED_TARGETS: AllowedTargets<'_> =
285        AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]);
286    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: Some(&["feature1, feature2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["feature1, feature2, ..."]);
287    const STABILITY: AttributeStability = AttributeStability::Stable;
288
289    fn extend(
290        cx: &mut AcceptContext<'_, '_>,
291        args: &ArgParser,
292    ) -> impl IntoIterator<Item = Self::Item> {
293        let Some(list) = cx.expect_list(args, cx.attr_span) else {
294            return Vec::new();
295        };
296
297        if list.is_empty() {
298            let attr_span = cx.attr_span;
299            cx.adcx().warn_empty_attribute(attr_span);
300        }
301
302        let mut res = Vec::new();
303
304        for elem in list.mixed() {
305            let Some(elem) = elem.meta_item() else {
306                cx.adcx().expected_identifier(elem.span());
307                continue;
308            };
309            let Some(()) = cx.expect_no_args(elem.args()) else {
310                continue;
311            };
312            let path = elem.path();
313            let Some(ident) = path.word() else {
314                cx.adcx().expected_identifier(path.span());
315                continue;
316            };
317            res.push(ident);
318        }
319
320        res
321    }
322}
323
324#[derive(#[automatically_derived]
impl ::core::default::Default for RegisterToolParser {
    #[inline]
    fn default() -> RegisterToolParser {
        RegisterToolParser {
            attr_tools: ::core::default::Default::default(),
            lint_tools: ::core::default::Default::default(),
        }
    }
}Default)]
325pub(crate) struct RegisterToolParser {
326    attr_tools: FxIndexSet<Ident>,
327    lint_tools: FxIndexSet<Ident>,
328}
329
330fn parse_register_tool(
331    tools: &mut [&mut FxIndexSet<Ident>],
332    cx: &mut AcceptContext<'_, '_>,
333    args: &ArgParser,
334) {
335    let Some(list) = cx.expect_list(args, cx.attr_span) else {
336        return;
337    };
338
339    if list.is_empty() {
340        let attr_span = cx.attr_span;
341        cx.adcx().warn_empty_attribute(attr_span);
342    }
343
344    for elem in list.mixed() {
345        let Some(elem) = elem.meta_item() else {
346            cx.adcx().expected_identifier(elem.span());
347            continue;
348        };
349        let Some(()) = cx.expect_no_args(elem.args()) else {
350            continue;
351        };
352
353        let path = elem.path();
354        let Some(ident) = path.word() else {
355            cx.adcx().expected_identifier(path.span());
356            continue;
357        };
358        if !ident.name.can_be_raw() {
359            cx.adcx().expected_identifier(path.span());
360            continue;
361        }
362
363        if ident.name == sym::rustc {
364            cx.should_emit
365                .emit_err(cx.dcx().create_err(ToolReserved { span: ident.span, tool: ident }));
366            continue;
367        }
368
369        let mut lint_emitted = false;
370        for tools in tools.iter_mut() {
371            if let Some(old_ident) = tools.replace(ident)
372                && !lint_emitted
373            {
374                lint_emitted = true;
375                cx.emit_lint(
376                    DUPLICATE_TOOLS,
377                    DuplicateTool { span: ident.span, tool: ident, old_ident_span: old_ident.span },
378                    ident.span,
379                );
380            }
381        }
382    }
383}
384
385impl AttributeParser for RegisterToolParser {
386    const ATTRIBUTES: AcceptMapping<Self> = &[
387        (
388            &[sym::register_tool],
389            crate::AttributeTemplate {
    word: false,
    list: Some(&["tool1, tool2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["tool1, tool2, ..."]),
390            {
    _ = rustc_feature::Features::register_tool;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::register_tool,
        notes: &[],
    }
}unstable!(register_tool),
391            |this, cx, args| {
392                parse_register_tool(&mut [&mut this.attr_tools, &mut this.lint_tools], cx, args)
393            },
394        ),
395        (
396            &[sym::register_attribute_tool],
397            crate::AttributeTemplate {
    word: false,
    list: Some(&["tool1, tool2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["tool1, tool2, ..."]),
398            {
    _ = rustc_feature::Features::register_tool;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::register_tool,
        notes: &[],
    }
}unstable!(register_tool),
399            |this, cx, args| parse_register_tool(&mut [&mut this.attr_tools], cx, args),
400        ),
401        (
402            &[sym::register_lint_tool],
403            crate::AttributeTemplate {
    word: false,
    list: Some(&["tool1, tool2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["tool1, tool2, ..."]),
404            {
    _ = rustc_feature::Features::register_tool;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::register_tool,
        notes: &[],
    }
}unstable!(register_tool),
405            |this, cx, args| parse_register_tool(&mut [&mut this.lint_tools], cx, args),
406        ),
407    ];
408
409    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
410
411    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
412        if self.attr_tools.is_empty() && self.lint_tools.is_empty() {
413            None
414        } else {
415            Some(AttributeKind::RegisterTool {
416                attr_tools: self.attr_tools.into_iter().collect(),
417                lint_tools: self.lint_tools.into_iter().collect(),
418            })
419        }
420    }
421}