rustc_attr_parsing/attributes/
test_attrs.rs1use rustc_feature::AttributeStability;
2use rustc_hir::attrs::RustcAbiAttrKind;
3use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
4
5use super::prelude::*;
6
7pub(crate) struct IgnoreParser;
8
9impl SingleAttributeParser for IgnoreParser {
10 const PATH: &[Symbol] = &[sym::ignore];
11 const ON_DUPLICATE: OnDuplicate = 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 const STABILITY: AttributeStability = AttributeStability::Stable;
19
20 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
21 Some(AttributeKind::Ignore {
22 span: cx.attr_span,
23 reason: match args {
24 ArgParser::NoArgs => None,
25 ArgParser::NameValue(name_value) => {
26 let Some(str_value) = name_value.value_as_str() else {
27 cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
28 return None;
29 };
30 Some(str_value)
31 }
32 ArgParser::List(list) => {
33 let help =
34 list.as_single().and_then(|item| item.meta_item()).and_then(|item| {
35 item.args().as_no_args().ok()?;
36 Some(item.path().to_string())
37 });
38 cx.adcx().warn_ill_formed_attribute_input_with_help(
39 ILL_FORMED_ATTRIBUTE_INPUT,
40 help,
41 );
42 return None;
43 }
44 },
45 })
46 }
47}
48
49pub(crate) struct ShouldPanicParser;
50
51impl SingleAttributeParser for ShouldPanicParser {
52 const PATH: &[Symbol] = &[sym::should_panic];
53 const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
54 const ALLOWED_TARGETS: AllowedTargets =
55 AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]);
56 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!(
57 Word, List: &[r#"expected = "reason""#], NameValueStr: "reason",
58 "https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
59 );
60 const STABILITY: AttributeStability = AttributeStability::Stable;
61
62 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
63 Some(AttributeKind::ShouldPanic {
64 reason: match args {
65 ArgParser::NoArgs => None,
66 ArgParser::NameValue(name_value) => cx.expect_string_literal(name_value),
67 ArgParser::List(list) => {
68 let single = cx.expect_single(list)?;
69 let (ident, arg) =
70 cx.expect_name_value(single, single.span(), Some(sym::expected))?;
71 if ident.name != sym::expected {
72 cx.adcx().expected_specific_argument_strings(list.span, &[sym::expected]);
73 return None;
74 }
75 cx.expect_string_literal(arg)
76 }
77 },
78 })
79 }
80}
81
82pub(crate) struct ReexportTestHarnessMainParser;
83
84impl SingleAttributeParser for ReexportTestHarnessMainParser {
85 const PATH: &[Symbol] = &[sym::reexport_test_harness_main];
86 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
87 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["name"]),
docs: None,
}template!(NameValueStr: "name");
88 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::custom_test_frameworks,
gate_check: rustc_feature::Features::custom_test_frameworks,
notes: &[],
}unstable!(custom_test_frameworks);
89
90 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
91 let nv = cx.expect_name_value(
92 args,
93 args.span().unwrap_or(cx.inner_span),
94 Some(sym::reexport_test_harness_main),
95 )?;
96
97 let name = cx.expect_string_literal(nv)?;
98
99 Some(AttributeKind::ReexportTestHarnessMain(name))
100 }
101}
102
103pub(crate) struct RustcAbiParser;
104
105impl SingleAttributeParser for RustcAbiParser {
106 const PATH: &[Symbol] = &[sym::rustc_abi];
107 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]);
108 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
109 Allow(Target::TyAlias),
110 Allow(Target::Fn),
111 Allow(Target::ForeignFn),
112 Allow(Target::Method(MethodKind::Inherent)),
113 Allow(Target::Method(MethodKind::Trait { body: true })),
114 Allow(Target::Method(MethodKind::Trait { body: false })),
115 Allow(Target::Method(MethodKind::TraitImpl)),
116 ]);
117 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
118
119 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
120 let Some(args) = args.as_list() else {
121 let attr_span = cx.attr_span;
122 cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::assert_eq, sym::debug]);
123 return None;
124 };
125
126 let arg = cx.expect_single(args)?;
127
128 let mut fail_incorrect_argument =
129 |span| cx.adcx().expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
130
131 let Some(arg) = arg.meta_item_no_args() else {
132 fail_incorrect_argument(args.span);
133 return None;
134 };
135
136 let kind: RustcAbiAttrKind = match arg.path().word_sym() {
137 Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
138 Some(sym::debug) => RustcAbiAttrKind::Debug,
139 None | Some(_) => {
140 fail_incorrect_argument(arg.span());
141 return None;
142 }
143 };
144
145 Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
146 }
147}
148
149pub(crate) struct RustcDelayedBugFromInsideQueryParser;
150
151impl NoArgsAttributeParser for RustcDelayedBugFromInsideQueryParser {
152 const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query];
153 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
154 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
155 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery;
156}
157
158pub(crate) struct RustcEvaluateWhereClausesParser;
159
160impl NoArgsAttributeParser for RustcEvaluateWhereClausesParser {
161 const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses];
162 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
163 Allow(Target::Fn),
164 Allow(Target::Method(MethodKind::Inherent)),
165 Allow(Target::Method(MethodKind::Trait { body: true })),
166 Allow(Target::Method(MethodKind::TraitImpl)),
167 Allow(Target::Method(MethodKind::Trait { body: false })),
168 ]);
169 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
170 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses;
171}
172
173pub(crate) struct TestRunnerParser;
174
175impl SingleAttributeParser for TestRunnerParser {
176 const PATH: &[Symbol] = &[sym::test_runner];
177 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
178 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["path"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["path"]);
179 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::custom_test_frameworks,
gate_check: rustc_feature::Features::custom_test_frameworks,
notes: &[],
}unstable!(custom_test_frameworks);
180
181 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
182 let single = cx.expect_single_element_list(args, cx.attr_span)?;
183
184 let Some(meta) = single.meta_item_no_args() else {
185 cx.adcx().expected_not_literal(single.span());
186 return None;
187 };
188
189 Some(AttributeKind::TestRunner(meta.path().0.clone()))
190 }
191}
192
193pub(crate) struct RustcTestMarkerParser;
194
195impl SingleAttributeParser for RustcTestMarkerParser {
196 const PATH: &[Symbol] = &[sym::rustc_test_marker];
197 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
198 Allow(Target::Const),
199 Allow(Target::Fn),
200 Allow(Target::Static),
201 ]);
202 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
203 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["test_path"]),
docs: None,
}template!(NameValueStr: "test_path");
204
205 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
206 let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::rustc_test_marker))?;
207 let value_str = cx.expect_string_literal(name_value)?;
208
209 if value_str.as_str().trim().is_empty() {
210 cx.adcx().expected_non_empty_string_literal(name_value.value_span);
211 return None;
212 }
213
214 Some(AttributeKind::RustcTestMarker(value_str))
215 }
216}