Skip to main content

rustc_attr_parsing/attributes/
test_attrs.rs

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