Skip to main content

rustc_attr_parsing/attributes/
rustc_internal.rs

1use std::path::PathBuf;
2
3use rustc_ast::{LitIntType, LitKind, MetaItemLit};
4use rustc_hir::attrs::{
5    BorrowckGraphvizFormatKind, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType,
6    RustcMirKind,
7};
8use rustc_session::errors;
9use rustc_span::Symbol;
10
11use super::prelude::*;
12use super::util::parse_single_integer;
13use crate::session_diagnostics::{AttributeRequiresOpt, RustcScalableVectorCountOutOfRange};
14
15pub(crate) struct RustcMainParser;
16
17impl<S: Stage> NoArgsAttributeParser<S> for RustcMainParser {
18    const PATH: &'static [Symbol] = &[sym::rustc_main];
19    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
20    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
21    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain;
22}
23
24pub(crate) struct RustcMustImplementOneOfParser;
25
26impl<S: Stage> SingleAttributeParser<S> for RustcMustImplementOneOfParser {
27    const PATH: &[Symbol] = &[sym::rustc_must_implement_one_of];
28    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
29    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
30    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
31    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["function1, function2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["function1, function2, ..."]);
32    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
33        let Some(list) = args.list() else {
34            cx.expected_list(cx.attr_span, args);
35            return None;
36        };
37
38        let mut fn_names = ThinVec::new();
39
40        let inputs: Vec<_> = list.mixed().collect();
41
42        if inputs.len() < 2 {
43            cx.expected_list_with_num_args_or_more(2, list.span);
44            return None;
45        }
46
47        let mut errored = false;
48        for argument in inputs {
49            let Some(meta) = argument.meta_item() else {
50                cx.expected_identifier(argument.span());
51                return None;
52            };
53
54            let Some(ident) = meta.ident() else {
55                cx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span: meta.span() });
56                errored = true;
57                continue;
58            };
59
60            fn_names.push(ident);
61        }
62        if errored {
63            return None;
64        }
65
66        Some(AttributeKind::RustcMustImplementOneOf { attr_span: cx.attr_span, fn_names })
67    }
68}
69
70pub(crate) struct RustcNeverReturnsNullPointerParser;
71
72impl<S: Stage> NoArgsAttributeParser<S> for RustcNeverReturnsNullPointerParser {
73    const PATH: &[Symbol] = &[sym::rustc_never_returns_null_ptr];
74    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
75    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
76        Allow(Target::Fn),
77        Allow(Target::Method(MethodKind::Inherent)),
78        Allow(Target::Method(MethodKind::Trait { body: false })),
79        Allow(Target::Method(MethodKind::Trait { body: true })),
80        Allow(Target::Method(MethodKind::TraitImpl)),
81    ]);
82    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPointer;
83}
84pub(crate) struct RustcNoImplicitAutorefsParser;
85
86impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitAutorefsParser {
87    const PATH: &[Symbol] = &[sym::rustc_no_implicit_autorefs];
88    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
89    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
90        Allow(Target::Fn),
91        Allow(Target::Method(MethodKind::Inherent)),
92        Allow(Target::Method(MethodKind::Trait { body: false })),
93        Allow(Target::Method(MethodKind::Trait { body: true })),
94        Allow(Target::Method(MethodKind::TraitImpl)),
95    ]);
96
97    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitAutorefs;
98}
99
100pub(crate) struct RustcLayoutScalarValidRangeStartParser;
101
102impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartParser {
103    const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
104    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
105    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
106    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
107    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["start"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["start"]);
108
109    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
110        parse_single_integer(cx, args)
111            .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span))
112    }
113}
114
115pub(crate) struct RustcLayoutScalarValidRangeEndParser;
116
117impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEndParser {
118    const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
119    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
120    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
121    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
122    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["end"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["end"]);
123
124    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
125        parse_single_integer(cx, args)
126            .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span))
127    }
128}
129
130pub(crate) struct RustcLegacyConstGenericsParser;
131
132impl<S: Stage> SingleAttributeParser<S> for RustcLegacyConstGenericsParser {
133    const PATH: &[Symbol] = &[sym::rustc_legacy_const_generics];
134    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
135    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
136    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
137    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["N"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["N"]);
138
139    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
140        let ArgParser::List(meta_items) = args else {
141            cx.expected_list(cx.attr_span, args);
142            return None;
143        };
144
145        let mut parsed_indexes = ThinVec::new();
146        let mut errored = false;
147
148        for possible_index in meta_items.mixed() {
149            if let MetaItemOrLitParser::Lit(MetaItemLit {
150                kind: LitKind::Int(index, LitIntType::Unsuffixed),
151                ..
152            }) = possible_index
153            {
154                parsed_indexes.push((index.0 as usize, possible_index.span()));
155            } else {
156                cx.expected_integer_literal(possible_index.span());
157                errored = true;
158            }
159        }
160        if errored {
161            return None;
162        } else if parsed_indexes.is_empty() {
163            cx.expected_at_least_one_argument(args.span()?);
164            return None;
165        }
166
167        Some(AttributeKind::RustcLegacyConstGenerics {
168            fn_indexes: parsed_indexes,
169            attr_span: cx.attr_span,
170        })
171    }
172}
173
174pub(crate) struct RustcLintOptDenyFieldAccessParser;
175
176impl<S: Stage> SingleAttributeParser<S> for RustcLintOptDenyFieldAccessParser {
177    const PATH: &[Symbol] = &[sym::rustc_lint_opt_deny_field_access];
178    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
179    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
180    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Field)]);
181    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: true,
    list: None,
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word);
182    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
183        let Some(arg) = args.list().and_then(MetaItemListParser::single) else {
184            cx.expected_single_argument(cx.attr_span);
185            return None;
186        };
187
188        let MetaItemOrLitParser::Lit(MetaItemLit { kind: LitKind::Str(lint_message, _), .. }) = arg
189        else {
190            cx.expected_string_literal(arg.span(), arg.lit());
191            return None;
192        };
193
194        Some(AttributeKind::RustcLintOptDenyFieldAccess { lint_message: *lint_message })
195    }
196}
197
198pub(crate) struct RustcLintOptTyParser;
199
200impl<S: Stage> NoArgsAttributeParser<S> for RustcLintOptTyParser {
201    const PATH: &[Symbol] = &[sym::rustc_lint_opt_ty];
202    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
203    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
204    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintOptTy;
205}
206
207pub(crate) struct RustcLintQueryInstabilityParser;
208
209impl<S: Stage> NoArgsAttributeParser<S> for RustcLintQueryInstabilityParser {
210    const PATH: &[Symbol] = &[sym::rustc_lint_query_instability];
211    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
212    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
213        Allow(Target::Fn),
214        Allow(Target::Method(MethodKind::Inherent)),
215        Allow(Target::Method(MethodKind::Trait { body: false })),
216        Allow(Target::Method(MethodKind::Trait { body: true })),
217        Allow(Target::Method(MethodKind::TraitImpl)),
218    ]);
219    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintQueryInstability;
220}
221
222pub(crate) struct RustcLintUntrackedQueryInformationParser;
223
224impl<S: Stage> NoArgsAttributeParser<S> for RustcLintUntrackedQueryInformationParser {
225    const PATH: &[Symbol] = &[sym::rustc_lint_untracked_query_information];
226    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
227    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
228        Allow(Target::Fn),
229        Allow(Target::Method(MethodKind::Inherent)),
230        Allow(Target::Method(MethodKind::Trait { body: false })),
231        Allow(Target::Method(MethodKind::Trait { body: true })),
232        Allow(Target::Method(MethodKind::TraitImpl)),
233    ]);
234
235    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintUntrackedQueryInformation;
236}
237
238pub(crate) struct RustcObjectLifetimeDefaultParser;
239
240impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
241    const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default];
242    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
243    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
244    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
245    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: true,
    list: None,
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word);
246
247    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
248        if let Err(span) = args.no_args() {
249            cx.expected_no_args(span);
250            return None;
251        }
252
253        Some(AttributeKind::RustcObjectLifetimeDefault)
254    }
255}
256
257pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;
258
259impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser {
260    const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit];
261    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
262    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
263    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
264    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["N"]),
    docs: None,
}template!(NameValueStr: "N");
265
266    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
267        let ArgParser::NameValue(nv) = args else {
268            cx.expected_name_value(cx.attr_span, None);
269            return None;
270        };
271        Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?))
272    }
273}
274
275pub(crate) struct RustcScalableVectorParser;
276
277impl<S: Stage> SingleAttributeParser<S> for RustcScalableVectorParser {
278    const PATH: &[Symbol] = &[sym::rustc_scalable_vector];
279    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
280    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
281    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
282    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: true,
    list: Some(&["count"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word, List: &["count"]);
283
284    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
285        if args.no_args().is_ok() {
286            return Some(AttributeKind::RustcScalableVector {
287                element_count: None,
288                span: cx.attr_span,
289            });
290        }
291
292        let n = parse_single_integer(cx, args)?;
293        let Ok(n) = n.try_into() else {
294            cx.emit_err(RustcScalableVectorCountOutOfRange { span: cx.attr_span, n });
295            return None;
296        };
297        Some(AttributeKind::RustcScalableVector { element_count: Some(n), span: cx.attr_span })
298    }
299}
300
301pub(crate) struct RustcHasIncoherentInherentImplsParser;
302
303impl<S: Stage> NoArgsAttributeParser<S> for RustcHasIncoherentInherentImplsParser {
304    const PATH: &[Symbol] = &[sym::rustc_has_incoherent_inherent_impls];
305    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
306    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
307        Allow(Target::Trait),
308        Allow(Target::Struct),
309        Allow(Target::Enum),
310        Allow(Target::Union),
311        Allow(Target::ForeignTy),
312    ]);
313    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls;
314}
315
316pub(crate) struct RustcHiddenTypeOfOpaquesParser;
317
318impl<S: Stage> NoArgsAttributeParser<S> for RustcHiddenTypeOfOpaquesParser {
319    const PATH: &[Symbol] = &[sym::rustc_hidden_type_of_opaques];
320    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
321    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
322    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHiddenTypeOfOpaques;
323}
324pub(crate) struct RustcNounwindParser;
325
326impl<S: Stage> NoArgsAttributeParser<S> for RustcNounwindParser {
327    const PATH: &[Symbol] = &[sym::rustc_nounwind];
328    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
329    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
330        Allow(Target::Fn),
331        Allow(Target::ForeignFn),
332        Allow(Target::Method(MethodKind::Inherent)),
333        Allow(Target::Method(MethodKind::TraitImpl)),
334        Allow(Target::Method(MethodKind::Trait { body: true })),
335    ]);
336    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNounwind;
337}
338
339pub(crate) struct RustcOffloadKernelParser;
340
341impl<S: Stage> NoArgsAttributeParser<S> for RustcOffloadKernelParser {
342    const PATH: &[Symbol] = &[sym::rustc_offload_kernel];
343    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
344    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
345    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOffloadKernel;
346}
347
348pub(crate) struct RustcLayoutParser;
349
350impl<S: Stage> CombineAttributeParser<S> for RustcLayoutParser {
351    const PATH: &[Symbol] = &[sym::rustc_layout];
352
353    type Item = RustcLayoutType;
354
355    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::RustcLayout(items);
356
357    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
358        Allow(Target::Struct),
359        Allow(Target::Enum),
360        Allow(Target::Union),
361        Allow(Target::TyAlias),
362    ]);
363
364    const TEMPLATE: AttributeTemplate =
365        ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["abi", "align", "size", "homogenous_aggregate", "debug"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]);
366    fn extend(
367        cx: &mut AcceptContext<'_, '_, S>,
368        args: &ArgParser,
369    ) -> impl IntoIterator<Item = Self::Item> {
370        let ArgParser::List(items) = args else {
371            cx.expected_list(cx.attr_span, args);
372            return ::alloc::vec::Vec::new()vec![];
373        };
374
375        let mut result = Vec::new();
376        for item in items.mixed() {
377            let Some(arg) = item.meta_item() else {
378                cx.unexpected_literal(item.span());
379                continue;
380            };
381            let Some(ident) = arg.ident() else {
382                cx.expected_identifier(arg.span());
383                return ::alloc::vec::Vec::new()vec![];
384            };
385            let ty = match ident.name {
386                sym::abi => RustcLayoutType::Abi,
387                sym::align => RustcLayoutType::Align,
388                sym::size => RustcLayoutType::Size,
389                sym::homogeneous_aggregate => RustcLayoutType::HomogenousAggregate,
390                sym::debug => RustcLayoutType::Debug,
391                _ => {
392                    cx.expected_specific_argument(
393                        ident.span,
394                        &[sym::abi, sym::align, sym::size, sym::homogeneous_aggregate, sym::debug],
395                    );
396                    continue;
397                }
398            };
399            result.push(ty);
400        }
401        result
402    }
403}
404
405pub(crate) struct RustcMirParser;
406
407impl<S: Stage> CombineAttributeParser<S> for RustcMirParser {
408    const PATH: &[Symbol] = &[sym::rustc_mir];
409
410    type Item = RustcMirKind;
411
412    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::RustcMir(items);
413
414    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
415        Allow(Target::Fn),
416        Allow(Target::Method(MethodKind::Inherent)),
417        Allow(Target::Method(MethodKind::TraitImpl)),
418        Allow(Target::Method(MethodKind::Trait { body: false })),
419        Allow(Target::Method(MethodKind::Trait { body: true })),
420    ]);
421
422    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["arg1, arg2, ..."]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["arg1, arg2, ..."]);
423
424    fn extend(
425        cx: &mut AcceptContext<'_, '_, S>,
426        args: &ArgParser,
427    ) -> impl IntoIterator<Item = Self::Item> {
428        let Some(list) = args.list() else {
429            cx.expected_list(cx.attr_span, args);
430            return ThinVec::new();
431        };
432
433        list.mixed()
434            .filter_map(|arg| arg.meta_item())
435            .filter_map(|mi| {
436                if let Some(ident) = mi.ident() {
437                    match ident.name {
438                        sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit),
439                        sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit),
440                        sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness),
441                        sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow),
442                        sym::borrowck_graphviz_postflow => {
443                            let Some(nv) = mi.args().name_value() else {
444                                cx.expected_name_value(
445                                    mi.span(),
446                                    Some(sym::borrowck_graphviz_postflow),
447                                );
448                                return None;
449                            };
450                            let Some(path) = nv.value_as_str() else {
451                                cx.expected_string_literal(nv.value_span, None);
452                                return None;
453                            };
454                            let path = PathBuf::from(path.to_string());
455                            if path.file_name().is_some() {
456                                Some(RustcMirKind::BorrowckGraphvizPostflow { path })
457                            } else {
458                                cx.expected_filename_literal(nv.value_span);
459                                None
460                            }
461                        }
462                        sym::borrowck_graphviz_format => {
463                            let Some(nv) = mi.args().name_value() else {
464                                cx.expected_name_value(
465                                    mi.span(),
466                                    Some(sym::borrowck_graphviz_format),
467                                );
468                                return None;
469                            };
470                            let Some(format) = nv.value_as_ident() else {
471                                cx.expected_identifier(nv.value_span);
472                                return None;
473                            };
474                            match format.name {
475                                sym::two_phase => Some(RustcMirKind::BorrowckGraphvizFormat {
476                                    format: BorrowckGraphvizFormatKind::TwoPhase,
477                                }),
478                                _ => {
479                                    cx.expected_specific_argument(format.span, &[sym::two_phase]);
480                                    None
481                                }
482                            }
483                        }
484                        _ => None,
485                    }
486                } else {
487                    None
488                }
489            })
490            .collect()
491    }
492}
493pub(crate) struct RustcNonConstTraitMethodParser;
494
495impl<S: Stage> NoArgsAttributeParser<S> for RustcNonConstTraitMethodParser {
496    const PATH: &'static [Symbol] = &[sym::rustc_non_const_trait_method];
497    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
498    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
499        Allow(Target::Method(MethodKind::Trait { body: true })),
500        Allow(Target::Method(MethodKind::Trait { body: false })),
501    ]);
502    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod;
503}
504
505pub(crate) struct RustcCleanParser;
506
507impl<S: Stage> CombineAttributeParser<S> for RustcCleanParser {
508    const PATH: &[Symbol] = &[sym::rustc_clean];
509
510    type Item = RustcCleanAttribute;
511
512    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::RustcClean(items);
513
514    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
515        // tidy-alphabetical-start
516        Allow(Target::AssocConst),
517        Allow(Target::AssocTy),
518        Allow(Target::Const),
519        Allow(Target::Enum),
520        Allow(Target::Expression),
521        Allow(Target::Field),
522        Allow(Target::Fn),
523        Allow(Target::ForeignMod),
524        Allow(Target::Impl { of_trait: false }),
525        Allow(Target::Impl { of_trait: true }),
526        Allow(Target::Method(MethodKind::Inherent)),
527        Allow(Target::Method(MethodKind::Trait { body: false })),
528        Allow(Target::Method(MethodKind::Trait { body: true })),
529        Allow(Target::Method(MethodKind::TraitImpl)),
530        Allow(Target::Mod),
531        Allow(Target::Static),
532        Allow(Target::Struct),
533        Allow(Target::Trait),
534        Allow(Target::TyAlias),
535        Allow(Target::Union),
536        // tidy-alphabetical-end
537    ]);
538
539    const TEMPLATE: AttributeTemplate =
540        ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&[r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &[r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#]);
541
542    fn extend(
543        cx: &mut AcceptContext<'_, '_, S>,
544        args: &ArgParser,
545    ) -> impl IntoIterator<Item = Self::Item> {
546        if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
547            cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
548        }
549        let Some(list) = args.list() else {
550            cx.expected_list(cx.attr_span, args);
551            return None;
552        };
553        let mut except = None;
554        let mut loaded_from_disk = None;
555        let mut cfg = None;
556
557        for item in list.mixed() {
558            let Some((value, name)) =
559                item.meta_item().and_then(|m| Option::zip(m.args().name_value(), m.ident()))
560            else {
561                cx.expected_name_value(item.span(), None);
562                continue;
563            };
564            let value_span = value.value_span;
565            let Some(value) = value.value_as_str() else {
566                cx.expected_string_literal(value_span, None);
567                continue;
568            };
569            match name.name {
570                sym::cfg if cfg.is_some() => {
571                    cx.duplicate_key(item.span(), sym::cfg);
572                }
573
574                sym::cfg => {
575                    cfg = Some(value);
576                }
577                sym::except if except.is_some() => {
578                    cx.duplicate_key(item.span(), sym::except);
579                }
580                sym::except => {
581                    let entries =
582                        value.as_str().split(',').map(|s| Symbol::intern(s.trim())).collect();
583                    except = Some(RustcCleanQueries { entries, span: value_span });
584                }
585                sym::loaded_from_disk if loaded_from_disk.is_some() => {
586                    cx.duplicate_key(item.span(), sym::loaded_from_disk);
587                }
588                sym::loaded_from_disk => {
589                    let entries =
590                        value.as_str().split(',').map(|s| Symbol::intern(s.trim())).collect();
591                    loaded_from_disk = Some(RustcCleanQueries { entries, span: value_span });
592                }
593                _ => {
594                    cx.expected_specific_argument(
595                        name.span,
596                        &[sym::cfg, sym::except, sym::loaded_from_disk],
597                    );
598                }
599            }
600        }
601        let Some(cfg) = cfg else {
602            cx.expected_specific_argument(list.span, &[sym::cfg]);
603            return None;
604        };
605
606        Some(RustcCleanAttribute { span: cx.attr_span, cfg, except, loaded_from_disk })
607    }
608}
609
610pub(crate) struct RustcIfThisChangedParser;
611
612impl<S: Stage> SingleAttributeParser<S> for RustcIfThisChangedParser {
613    const PATH: &[Symbol] = &[sym::rustc_if_this_changed];
614
615    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
616
617    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
618
619    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
620        // tidy-alphabetical-start
621        Allow(Target::AssocConst),
622        Allow(Target::AssocTy),
623        Allow(Target::Const),
624        Allow(Target::Enum),
625        Allow(Target::Expression),
626        Allow(Target::Field),
627        Allow(Target::Fn),
628        Allow(Target::ForeignMod),
629        Allow(Target::Impl { of_trait: false }),
630        Allow(Target::Impl { of_trait: true }),
631        Allow(Target::Method(MethodKind::Inherent)),
632        Allow(Target::Method(MethodKind::Trait { body: false })),
633        Allow(Target::Method(MethodKind::Trait { body: true })),
634        Allow(Target::Method(MethodKind::TraitImpl)),
635        Allow(Target::Mod),
636        Allow(Target::Static),
637        Allow(Target::Struct),
638        Allow(Target::Trait),
639        Allow(Target::TyAlias),
640        Allow(Target::Union),
641        // tidy-alphabetical-end
642    ]);
643
644    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: true,
    list: Some(&["DepNode"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word, List: &["DepNode"]);
645
646    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
647        if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
648            cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
649        }
650        match args {
651            ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)),
652            ArgParser::List(list) => {
653                let Some(item) = list.single() else {
654                    cx.expected_single_argument(list.span);
655                    return None;
656                };
657                let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
658                    cx.expected_identifier(item.span());
659                    return None;
660                };
661                Some(AttributeKind::RustcIfThisChanged(cx.attr_span, Some(ident.name)))
662            }
663            ArgParser::NameValue(_) => {
664                cx.expected_list_or_no_args(cx.inner_span);
665                None
666            }
667        }
668    }
669}
670
671pub(crate) struct RustcThenThisWouldNeedParser;
672
673impl<S: Stage> CombineAttributeParser<S> for RustcThenThisWouldNeedParser {
674    const PATH: &[Symbol] = &[sym::rustc_then_this_would_need];
675    type Item = Ident;
676
677    const CONVERT: ConvertFn<Self::Item> =
678        |items, span| AttributeKind::RustcThenThisWouldNeed(span, items);
679    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
680        // tidy-alphabetical-start
681        Allow(Target::AssocConst),
682        Allow(Target::AssocTy),
683        Allow(Target::Const),
684        Allow(Target::Enum),
685        Allow(Target::Expression),
686        Allow(Target::Field),
687        Allow(Target::Fn),
688        Allow(Target::ForeignMod),
689        Allow(Target::Impl { of_trait: false }),
690        Allow(Target::Impl { of_trait: true }),
691        Allow(Target::Method(MethodKind::Inherent)),
692        Allow(Target::Method(MethodKind::Trait { body: false })),
693        Allow(Target::Method(MethodKind::Trait { body: true })),
694        Allow(Target::Method(MethodKind::TraitImpl)),
695        Allow(Target::Mod),
696        Allow(Target::Static),
697        Allow(Target::Struct),
698        Allow(Target::Trait),
699        Allow(Target::TyAlias),
700        Allow(Target::Union),
701        // tidy-alphabetical-end
702    ]);
703
704    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["DepNode"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["DepNode"]);
705
706    fn extend(
707        cx: &mut AcceptContext<'_, '_, S>,
708        args: &ArgParser,
709    ) -> impl IntoIterator<Item = Self::Item> {
710        if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
711            cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
712        }
713        let Some(item) = args.list().and_then(|l| l.single()) else {
714            cx.expected_single_argument(cx.inner_span);
715            return None;
716        };
717        let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
718            cx.expected_identifier(item.span());
719            return None;
720        };
721        Some(ident)
722    }
723}
724
725pub(crate) struct RustcEffectiveVisibilityParser;
726
727impl<S: Stage> NoArgsAttributeParser<S> for RustcEffectiveVisibilityParser {
728    const PATH: &'static [Symbol] = &[sym::rustc_effective_visibility];
729    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
730    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
731        Allow(Target::Use),
732        Allow(Target::Static),
733        Allow(Target::Const),
734        Allow(Target::Fn),
735        Allow(Target::Closure),
736        Allow(Target::Mod),
737        Allow(Target::ForeignMod),
738        Allow(Target::TyAlias),
739        Allow(Target::Enum),
740        Allow(Target::Variant),
741        Allow(Target::Struct),
742        Allow(Target::Field),
743        Allow(Target::Union),
744        Allow(Target::Trait),
745        Allow(Target::TraitAlias),
746        Allow(Target::Impl { of_trait: false }),
747        Allow(Target::Impl { of_trait: true }),
748        Allow(Target::AssocConst),
749        Allow(Target::Method(MethodKind::Inherent)),
750        Allow(Target::Method(MethodKind::Trait { body: false })),
751        Allow(Target::Method(MethodKind::Trait { body: true })),
752        Allow(Target::Method(MethodKind::TraitImpl)),
753        Allow(Target::AssocTy),
754        Allow(Target::ForeignFn),
755        Allow(Target::ForeignStatic),
756        Allow(Target::ForeignTy),
757        Allow(Target::MacroDef),
758        Allow(Target::PatField),
759        Allow(Target::Crate),
760    ]);
761    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEffectiveVisibility;
762}