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