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