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 TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
112    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
113
114    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
115        let ArgParser::NameValue(nv) = args else {
116            let attr_span = cx.attr_span;
117            cx.adcx().expected_name_value(attr_span, None);
118            return None;
119        };
120
121        Some(AttributeKind::MoveSizeLimit {
122            limit: cx.parse_limit_int(nv)?,
123            attr_span: cx.attr_span,
124            limit_span: nv.value_span,
125        })
126    }
127}
128
129pub(crate) struct TypeLengthLimitParser;
130
131impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
132    const PATH: &[Symbol] = &[sym::type_length_limit];
133    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
134    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
135    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
136
137    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
138        let ArgParser::NameValue(nv) = args else {
139            let attr_span = cx.attr_span;
140            cx.adcx().expected_name_value(attr_span, None);
141            return None;
142        };
143
144        Some(AttributeKind::TypeLengthLimit {
145            limit: cx.parse_limit_int(nv)?,
146            attr_span: cx.attr_span,
147            limit_span: nv.value_span,
148        })
149    }
150}
151
152pub(crate) struct PatternComplexityLimitParser;
153
154impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
155    const PATH: &[Symbol] = &[sym::pattern_complexity_limit];
156    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
157    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
158
159    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
160        let ArgParser::NameValue(nv) = args else {
161            let attr_span = cx.attr_span;
162            cx.adcx().expected_name_value(attr_span, None);
163            return None;
164        };
165
166        Some(AttributeKind::PatternComplexityLimit {
167            limit: cx.parse_limit_int(nv)?,
168            attr_span: cx.attr_span,
169            limit_span: nv.value_span,
170        })
171    }
172}
173
174pub(crate) struct NoCoreParser;
175
176impl<S: Stage> NoArgsAttributeParser<S> for NoCoreParser {
177    const PATH: &[Symbol] = &[sym::no_core];
178    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
179    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore;
180}
181
182pub(crate) struct NoStdParser;
183
184impl<S: Stage> NoArgsAttributeParser<S> for NoStdParser {
185    const PATH: &[Symbol] = &[sym::no_std];
186    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
187    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
188    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd;
189}
190
191pub(crate) struct NoMainParser;
192
193impl<S: Stage> NoArgsAttributeParser<S> for NoMainParser {
194    const PATH: &[Symbol] = &[sym::no_main];
195    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
196    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
197    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoMain;
198}
199
200pub(crate) struct RustcCoherenceIsCoreParser;
201
202impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser {
203    const PATH: &[Symbol] = &[sym::rustc_coherence_is_core];
204    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
205    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore;
206}
207
208pub(crate) struct WindowsSubsystemParser;
209
210impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
211    const PATH: &[Symbol] = &[sym::windows_subsystem];
212    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
213    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
214    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");
215
216    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
217        let Some(nv) = args.name_value() else {
218            let inner_span = cx.inner_span;
219            cx.adcx().expected_name_value(
220                args.span().unwrap_or(inner_span),
221                Some(sym::windows_subsystem),
222            );
223            return None;
224        };
225
226        let kind = match nv.value_as_str() {
227            Some(sym::console) => WindowsSubsystemKind::Console,
228            Some(sym::windows) => WindowsSubsystemKind::Windows,
229            Some(_) | None => {
230                cx.adcx().expected_specific_argument_strings(
231                    nv.value_span,
232                    &[sym::console, sym::windows],
233                );
234                return None;
235            }
236        };
237
238        Some(AttributeKind::WindowsSubsystem(kind, cx.attr_span))
239    }
240}
241
242pub(crate) struct PanicRuntimeParser;
243
244impl<S: Stage> NoArgsAttributeParser<S> for PanicRuntimeParser {
245    const PATH: &[Symbol] = &[sym::panic_runtime];
246    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
247    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PanicRuntime;
248}
249
250pub(crate) struct NeedsPanicRuntimeParser;
251
252impl<S: Stage> NoArgsAttributeParser<S> for NeedsPanicRuntimeParser {
253    const PATH: &[Symbol] = &[sym::needs_panic_runtime];
254    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
255    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsPanicRuntime;
256}
257
258pub(crate) struct ProfilerRuntimeParser;
259
260impl<S: Stage> NoArgsAttributeParser<S> for ProfilerRuntimeParser {
261    const PATH: &[Symbol] = &[sym::profiler_runtime];
262    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
263    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProfilerRuntime;
264}
265
266pub(crate) struct NoBuiltinsParser;
267
268impl<S: Stage> NoArgsAttributeParser<S> for NoBuiltinsParser {
269    const PATH: &[Symbol] = &[sym::no_builtins];
270    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
271    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
272    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins;
273}
274
275pub(crate) struct RustcPreserveUbChecksParser;
276
277impl<S: Stage> NoArgsAttributeParser<S> for RustcPreserveUbChecksParser {
278    const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks];
279    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
280    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks;
281}
282
283pub(crate) struct RustcNoImplicitBoundsParser;
284
285impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitBoundsParser {
286    const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds];
287    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
288    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds;
289}
290
291pub(crate) struct DefaultLibAllocatorParser;
292
293impl<S: Stage> NoArgsAttributeParser<S> for DefaultLibAllocatorParser {
294    const PATH: &[Symbol] = &[sym::default_lib_allocator];
295    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
296    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator;
297}
298
299pub(crate) struct FeatureParser;
300
301impl<S: Stage> CombineAttributeParser<S> for FeatureParser {
302    const PATH: &[Symbol] = &[sym::feature];
303    type Item = Ident;
304    const CONVERT: ConvertFn<Self::Item> = AttributeKind::Feature;
305    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
306    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["feature1, feature2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["feature1, feature2, ..."]);
307
308    fn extend(
309        cx: &mut AcceptContext<'_, '_, S>,
310        args: &ArgParser,
311    ) -> impl IntoIterator<Item = Self::Item> {
312        let ArgParser::List(list) = args else {
313            let attr_span = cx.attr_span;
314            cx.adcx().expected_list(attr_span, args);
315            return Vec::new();
316        };
317
318        if list.is_empty() {
319            let attr_span = cx.attr_span;
320            cx.adcx().warn_empty_attribute(attr_span);
321        }
322
323        let mut res = Vec::new();
324
325        for elem in list.mixed() {
326            let Some(elem) = elem.meta_item() else {
327                cx.adcx().expected_identifier(elem.span());
328                continue;
329            };
330            if let Err(arg_span) = elem.args().no_args() {
331                cx.adcx().expected_no_args(arg_span);
332                continue;
333            }
334
335            let path = elem.path();
336            let Some(ident) = path.word() else {
337                cx.adcx().expected_identifier(path.span());
338                continue;
339            };
340            res.push(ident);
341        }
342
343        res
344    }
345}
346
347pub(crate) struct RegisterToolParser;
348
349impl<S: Stage> CombineAttributeParser<S> for RegisterToolParser {
350    const PATH: &[Symbol] = &[sym::register_tool];
351    type Item = Ident;
352    const CONVERT: ConvertFn<Self::Item> = AttributeKind::RegisterTool;
353    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
354    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["tool1, tool2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["tool1, tool2, ..."]);
355
356    fn extend(
357        cx: &mut AcceptContext<'_, '_, S>,
358        args: &ArgParser,
359    ) -> impl IntoIterator<Item = Self::Item> {
360        let ArgParser::List(list) = args else {
361            let attr_span = cx.attr_span;
362            cx.adcx().expected_list(attr_span, args);
363            return Vec::new();
364        };
365
366        if list.is_empty() {
367            let attr_span = cx.attr_span;
368            cx.adcx().warn_empty_attribute(attr_span);
369        }
370
371        let mut res = Vec::new();
372
373        for elem in list.mixed() {
374            let Some(elem) = elem.meta_item() else {
375                cx.adcx().expected_identifier(elem.span());
376                continue;
377            };
378            if let Err(arg_span) = elem.args().no_args() {
379                cx.adcx().expected_no_args(arg_span);
380                continue;
381            }
382
383            let path = elem.path();
384            let Some(ident) = path.word() else {
385                cx.adcx().expected_identifier(path.span());
386                continue;
387            };
388
389            res.push(ident);
390        }
391
392        res
393    }
394}