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