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 SingleAttributeParser for IgnoreParser {
9 const PATH: &[Symbol] = &[sym::ignore];
10 const ON_DUPLICATE: OnDuplicate = 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<'_, '_>, 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().as_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 SingleAttributeParser for ShouldPanicParser {
50 const PATH: &[Symbol] = &[sym::should_panic];
51 const ON_DUPLICATE: OnDuplicate = 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<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
60 Some(AttributeKind::ShouldPanic {
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 single = cx.expect_single(list)?;
75 let (ident, arg) =
76 cx.expect_name_value(single, single.span(), Some(sym::expected))?;
77 if ident.name != sym::expected {
78 cx.adcx().expected_specific_argument_strings(list.span, &[sym::expected]);
79 return None;
80 }
81 let Some(expected) = arg.value_as_str() else {
82 cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit()));
83 return None;
84 };
85 Some(expected)
86 }
87 },
88 })
89 }
90}
91
92pub(crate) struct ReexportTestHarnessMainParser;
93
94impl SingleAttributeParser for ReexportTestHarnessMainParser {
95 const PATH: &[Symbol] = &[sym::reexport_test_harness_main];
96 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
97 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["name"]),
docs: None,
}template!(NameValueStr: "name");
98
99 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
100 let nv = cx.expect_name_value(
101 args,
102 args.span().unwrap_or(cx.inner_span),
103 Some(sym::reexport_test_harness_main),
104 )?;
105
106 let Some(name) = nv.value_as_str() else {
107 cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
108 return None;
109 };
110
111 Some(AttributeKind::ReexportTestHarnessMain(name))
112 }
113}
114
115pub(crate) struct RustcAbiParser;
116
117impl SingleAttributeParser for RustcAbiParser {
118 const PATH: &[Symbol] = &[sym::rustc_abi];
119 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]);
120 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
121 Allow(Target::TyAlias),
122 Allow(Target::Fn),
123 Allow(Target::ForeignFn),
124 Allow(Target::Method(MethodKind::Inherent)),
125 Allow(Target::Method(MethodKind::Trait { body: true })),
126 Allow(Target::Method(MethodKind::Trait { body: false })),
127 Allow(Target::Method(MethodKind::TraitImpl)),
128 ]);
129
130 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
131 let Some(args) = args.as_list() else {
132 let attr_span = cx.attr_span;
133 cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::assert_eq, sym::debug]);
134 return None;
135 };
136
137 let arg = cx.expect_single(args)?;
138
139 let mut fail_incorrect_argument =
140 |span| cx.adcx().expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
141
142 let Some(arg) = arg.meta_item() else {
143 fail_incorrect_argument(args.span);
144 return None;
145 };
146
147 let kind: RustcAbiAttrKind = match arg.path().word_sym() {
148 Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
149 Some(sym::debug) => RustcAbiAttrKind::Debug,
150 None | Some(_) => {
151 fail_incorrect_argument(arg.span());
152 return None;
153 }
154 };
155
156 Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
157 }
158}
159
160pub(crate) struct RustcDelayedBugFromInsideQueryParser;
161
162impl NoArgsAttributeParser for RustcDelayedBugFromInsideQueryParser {
163 const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query];
164 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
165 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery;
166}
167
168pub(crate) struct RustcEvaluateWhereClausesParser;
169
170impl NoArgsAttributeParser for RustcEvaluateWhereClausesParser {
171 const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses];
172 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
173 Allow(Target::Fn),
174 Allow(Target::Method(MethodKind::Inherent)),
175 Allow(Target::Method(MethodKind::Trait { body: true })),
176 Allow(Target::Method(MethodKind::TraitImpl)),
177 Allow(Target::Method(MethodKind::Trait { body: false })),
178 ]);
179 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses;
180}
181
182pub(crate) struct TestRunnerParser;
183
184impl SingleAttributeParser for TestRunnerParser {
185 const PATH: &[Symbol] = &[sym::test_runner];
186 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
187 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["path"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["path"]);
188
189 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
190 let single = cx.expect_single_element_list(args, cx.attr_span)?;
191
192 let Some(meta) = single.meta_item() else {
193 cx.adcx().expected_not_literal(single.span());
194 return None;
195 };
196
197 Some(AttributeKind::TestRunner(meta.path().0.clone()))
198 }
199}
200
201pub(crate) struct RustcTestMarkerParser;
202
203impl SingleAttributeParser for RustcTestMarkerParser {
204 const PATH: &[Symbol] = &[sym::rustc_test_marker];
205 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
206 Allow(Target::Const),
207 Allow(Target::Fn),
208 Allow(Target::Static),
209 ]);
210 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["test_path"]),
docs: None,
}template!(NameValueStr: "test_path");
211
212 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
213 let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::rustc_test_marker))?;
214
215 let Some(value_str) = name_value.value_as_str() else {
216 cx.adcx().expected_string_literal(name_value.value_span, None);
217 return None;
218 };
219
220 if value_str.as_str().trim().is_empty() {
221 cx.adcx().expected_non_empty_string_literal(name_value.value_span);
222 return None;
223 }
224
225 Some(AttributeKind::RustcTestMarker(value_str))
226 }
227}