Skip to main content

rustc_attr_parsing/attributes/
test_attrs.rs

1use rustc_hir::attrs::RustcAbiAttrKind;
2use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
3
4use super::prelude::*;
5
6pub(crate) struct IgnoreParser;
7
8impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
9    const PATH: &[Symbol] = &[sym::ignore];
10    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
11    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
12    const ALLOWED_TARGETS: AllowedTargets =
13        AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]);
14    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: true,
    list: None,
    one_of: &[],
    name_value_str: Some(&["reason"]),
    docs: Some("https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"),
}template!(
15        Word, NameValueStr: "reason",
16        "https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"
17    );
18
19    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
20        Some(AttributeKind::Ignore {
21            span: cx.attr_span,
22            reason: match args {
23                ArgParser::NoArgs => None,
24                ArgParser::NameValue(name_value) => {
25                    let Some(str_value) = name_value.value_as_str() else {
26                        cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
27                        return None;
28                    };
29                    Some(str_value)
30                }
31                ArgParser::List(_) => {
32                    cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
33                    return None;
34                }
35            },
36        })
37    }
38}
39
40pub(crate) struct ShouldPanicParser;
41
42impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
43    const PATH: &[Symbol] = &[sym::should_panic];
44    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
45    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
46    const ALLOWED_TARGETS: AllowedTargets =
47        AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]);
48    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: true,
    list: Some(&[r#"expected = "reason""#]),
    one_of: &[],
    name_value_str: Some(&["reason"]),
    docs: Some("https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"),
}template!(
49        Word, List: &[r#"expected = "reason""#], NameValueStr: "reason",
50        "https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
51    );
52
53    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
54        Some(AttributeKind::ShouldPanic {
55            span: cx.attr_span,
56            reason: match args {
57                ArgParser::NoArgs => None,
58                ArgParser::NameValue(name_value) => {
59                    let Some(str_value) = name_value.value_as_str() else {
60                        cx.expected_string_literal(
61                            name_value.value_span,
62                            Some(name_value.value_as_lit()),
63                        );
64                        return None;
65                    };
66                    Some(str_value)
67                }
68                ArgParser::List(list) => {
69                    let Some(single) = list.single() else {
70                        cx.expected_single_argument(list.span);
71                        return None;
72                    };
73                    let Some(single) = single.meta_item() else {
74                        cx.expected_name_value(single.span(), Some(sym::expected));
75                        return None;
76                    };
77                    if !single.path().word_is(sym::expected) {
78                        cx.expected_specific_argument_strings(list.span, &[sym::expected]);
79                        return None;
80                    }
81                    let Some(nv) = single.args().name_value() else {
82                        cx.expected_name_value(single.span(), Some(sym::expected));
83                        return None;
84                    };
85                    let Some(expected) = nv.value_as_str() else {
86                        cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
87                        return None;
88                    };
89                    Some(expected)
90                }
91            },
92        })
93    }
94}
95
96pub(crate) struct RustcVarianceParser;
97
98impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceParser {
99    const PATH: &[Symbol] = &[sym::rustc_variance];
100    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
101    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
102        Allow(Target::Struct),
103        Allow(Target::Enum),
104        Allow(Target::Union),
105    ]);
106    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVariance;
107}
108
109pub(crate) struct RustcVarianceOfOpaquesParser;
110
111impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceOfOpaquesParser {
112    const PATH: &[Symbol] = &[sym::rustc_variance_of_opaques];
113    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
114    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
115    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVarianceOfOpaques;
116}
117
118pub(crate) struct ReexportTestHarnessMainParser;
119
120impl<S: Stage> SingleAttributeParser<S> for ReexportTestHarnessMainParser {
121    const PATH: &[Symbol] = &[sym::reexport_test_harness_main];
122    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
123    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
124    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
125    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["name"]),
    docs: None,
}template!(NameValueStr: "name");
126
127    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
128        let Some(nv) = args.name_value() else {
129            cx.expected_name_value(
130                args.span().unwrap_or(cx.inner_span),
131                Some(sym::reexport_test_harness_main),
132            );
133            return None;
134        };
135
136        let Some(name) = nv.value_as_str() else {
137            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
138            return None;
139        };
140
141        Some(AttributeKind::ReexportTestHarnessMain(name))
142    }
143}
144
145pub(crate) struct RustcAbiParser;
146
147impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
148    const PATH: &[Symbol] = &[sym::rustc_abi];
149    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
150    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[sym::debug, sym::assert_eq],
    name_value_str: None,
    docs: None,
}template!(OneOf: &[sym::debug, sym::assert_eq]);
151    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
152    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
153        Allow(Target::TyAlias),
154        Allow(Target::Fn),
155        Allow(Target::ForeignFn),
156        Allow(Target::Method(MethodKind::Inherent)),
157        Allow(Target::Method(MethodKind::Trait { body: true })),
158        Allow(Target::Method(MethodKind::Trait { body: false })),
159        Allow(Target::Method(MethodKind::TraitImpl)),
160    ]);
161
162    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
163        let Some(args) = args.list() else {
164            cx.expected_specific_argument_and_list(cx.attr_span, &[sym::assert_eq, sym::debug]);
165            return None;
166        };
167
168        let Some(arg) = args.single() else {
169            cx.expected_single_argument(cx.attr_span);
170            return None;
171        };
172
173        let fail_incorrect_argument =
174            |span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
175
176        let Some(arg) = arg.meta_item() else {
177            fail_incorrect_argument(args.span);
178            return None;
179        };
180
181        let kind: RustcAbiAttrKind = match arg.path().word_sym() {
182            Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
183            Some(sym::debug) => RustcAbiAttrKind::Debug,
184            None | Some(_) => {
185                fail_incorrect_argument(arg.span());
186                return None;
187            }
188        };
189
190        Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
191    }
192}
193
194pub(crate) struct RustcDelayedBugFromInsideQueryParser;
195
196impl<S: Stage> NoArgsAttributeParser<S> for RustcDelayedBugFromInsideQueryParser {
197    const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query];
198    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
199    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
200    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery;
201}
202
203pub(crate) struct RustcEvaluateWhereClausesParser;
204
205impl<S: Stage> NoArgsAttributeParser<S> for RustcEvaluateWhereClausesParser {
206    const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses];
207    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
208    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
209        Allow(Target::Fn),
210        Allow(Target::Method(MethodKind::Inherent)),
211        Allow(Target::Method(MethodKind::Trait { body: true })),
212        Allow(Target::Method(MethodKind::TraitImpl)),
213        Allow(Target::Method(MethodKind::Trait { body: false })),
214    ]);
215    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses;
216}
217
218pub(crate) struct RustcOutlivesParser;
219
220impl<S: Stage> NoArgsAttributeParser<S> for RustcOutlivesParser {
221    const PATH: &[Symbol] = &[sym::rustc_outlives];
222    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
223    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
224        Allow(Target::Struct),
225        Allow(Target::Enum),
226        Allow(Target::Union),
227        Allow(Target::TyAlias),
228    ]);
229    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives;
230}
231
232pub(crate) struct TestRunnerParser;
233
234impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
235    const PATH: &[Symbol] = &[sym::test_runner];
236    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
237    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
238    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
239    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["path"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["path"]);
240
241    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
242        let Some(list) = args.list() else {
243            cx.expected_list(cx.attr_span, args);
244            return None;
245        };
246
247        let Some(single) = list.single() else {
248            cx.expected_single_argument(list.span);
249            return None;
250        };
251
252        let Some(meta) = single.meta_item() else {
253            cx.unexpected_literal(single.span());
254            return None;
255        };
256
257        Some(AttributeKind::TestRunner(meta.path().0.clone()))
258    }
259}
260
261pub(crate) struct RustcTestMarkerParser;
262
263impl<S: Stage> SingleAttributeParser<S> for RustcTestMarkerParser {
264    const PATH: &[Symbol] = &[sym::rustc_test_marker];
265    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
266    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
267    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
268        Allow(Target::Const),
269        Allow(Target::Fn),
270        Allow(Target::Static),
271    ]);
272    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["test_path"]),
    docs: None,
}template!(NameValueStr: "test_path");
273
274    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
275        let Some(name_value) = args.name_value() else {
276            cx.expected_name_value(cx.attr_span, Some(sym::rustc_test_marker));
277            return None;
278        };
279
280        let Some(value_str) = name_value.value_as_str() else {
281            cx.expected_string_literal(name_value.value_span, None);
282            return None;
283        };
284
285        if value_str.as_str().trim().is_empty() {
286            cx.expected_non_empty_string_literal(name_value.value_span);
287            return None;
288        }
289
290        Some(AttributeKind::RustcTestMarker(value_str))
291    }
292}