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