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