Skip to main content

rustc_attr_parsing/attributes/
crate_level.rs

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