Skip to main content

rustc_attr_parsing/attributes/
crate_level.rs

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