Skip to main content

rustc_attr_parsing/attributes/
test_attrs.rs

1use 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}