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