Skip to main content

rustc_attr_parsing/attributes/
crate_level.rs

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