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 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().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            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 SingleAttributeParser 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<'_, '_>, 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 SingleAttributeParser 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<'_, '_>, 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 NoArgsAttributeParser 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 NoArgsAttributeParser 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 SingleAttributeParser 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<'_, '_>, 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 SingleAttributeParser 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<'_, '_>, 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}