Skip to main content

rustc_attr_parsing/attributes/
crate_level.rs

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