1 use crate::common
::{CompareMode, Config, Debugger}
;
2 use crate::header
::IgnoreDecision
;
3 use std
::collections
::HashSet
;
5 const EXTRA_ARCHS
: &[&str] = &["spirv"];
7 pub(super) fn handle_ignore(config
: &Config
, line
: &str) -> IgnoreDecision
{
8 let parsed
= parse_cfg_name_directive(config
, line
, "ignore");
10 MatchOutcome
::NoMatch
=> IgnoreDecision
::Continue
,
11 MatchOutcome
::Match
=> IgnoreDecision
::Ignore
{
12 reason
: match parsed
.comment
{
13 Some(comment
) => format
!("ignored {} ({comment})", parsed
.pretty_reason
.unwrap()),
14 None
=> format
!("ignored {}", parsed
.pretty_reason
.unwrap()),
17 MatchOutcome
::Invalid
=> IgnoreDecision
::Error { message: format!("invalid line: {line}
") },
18 MatchOutcome::External => IgnoreDecision::Continue,
19 MatchOutcome::NotADirective => IgnoreDecision::Continue,
23 pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision {
24 let parsed = parse_cfg_name_directive(config, line, "only
");
25 match parsed.outcome {
26 MatchOutcome::Match => IgnoreDecision::Continue,
27 MatchOutcome::NoMatch => IgnoreDecision::Ignore {
28 reason: match parsed.comment {
30 format!("only executed {}
({comment}
)", parsed.pretty_reason.unwrap())
32 None => format!("only executed {}
", parsed.pretty_reason.unwrap()),
35 MatchOutcome::Invalid => IgnoreDecision::Error { message: format!("invalid line: {line}") },
36 MatchOutcome
::External
=> IgnoreDecision
::Continue
,
37 MatchOutcome
::NotADirective
=> IgnoreDecision
::Continue
,
41 /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
42 /// or `normalize-stderr-32bit`.
43 pub(super) fn parse_cfg_name_directive
<'a
>(
47 ) -> ParsedNameDirective
<'a
> {
48 if !line
.as_bytes().starts_with(prefix
.as_bytes()) {
49 return ParsedNameDirective
::not_a_directive();
51 if line
.as_bytes().get(prefix
.len()) != Some(&b'
-'
) {
52 return ParsedNameDirective
::not_a_directive();
54 let line
= &line
[prefix
.len() + 1..];
57 line
.split_once(&['
:'
, ' '
]).map(|(l
, c
)| (l
, Some(c
))).unwrap_or((line
, None
));
59 // Some of the matchers might be "" depending on what the target information is. To avoid
60 // problems we outright reject empty directives.
62 return ParsedNameDirective
::not_a_directive();
65 let mut outcome
= MatchOutcome
::Invalid
;
66 let mut message
= None
;
68 macro_rules
! condition
{
71 $
(allowed_names
: $allowed_names
:expr
,)?
72 $
(condition
: $condition
:expr
,)?
73 message
: $
($message
:tt
)*
75 // This is not inlined to avoid problems with macro repetitions.
76 let format_message
= || format
!($
($message
)*);
78 if outcome
!= MatchOutcome
::Invalid
{
79 // Ignore all other matches if we already found one
80 } else if $name
.custom_matches(name
) {
81 message
= Some(format_message());
82 if true $
(&& $condition
)?
{
83 outcome
= MatchOutcome
::Match
;
85 outcome
= MatchOutcome
::NoMatch
;
88 $
(else if $allowed_names
.custom_contains(name
) {
89 message
= Some(format_message());
90 outcome
= MatchOutcome
::NoMatch
;
95 let target_cfgs
= config
.target_cfgs();
96 let target_cfg
= config
.target_cfg();
103 name
: &config
.target
,
104 allowed_names
: &target_cfgs
.all_targets
,
105 message
: "when the target is {name}"
109 Some(&*target_cfg
.os
),
110 // If something is ignored for emscripten, it likely also needs to be
111 // ignored for wasm32-unknown-unknown.
112 (config
.target
== "wasm32-unknown-unknown").then_some("emscripten"),
114 allowed_names
: &target_cfgs
.all_oses
,
115 message
: "when the operating system is {name}"
118 name
: &target_cfg
.env
,
119 allowed_names
: &target_cfgs
.all_envs
,
120 message
: "when the target environment is {name}"
123 name
: &target_cfg
.os_and_env(),
124 allowed_names
: &target_cfgs
.all_oses_and_envs
,
125 message
: "when the operating system and target environment are {name}"
128 name
: &target_cfg
.abi
,
129 allowed_names
: &target_cfgs
.all_abis
,
130 message
: "when the ABI is {name}"
133 name
: &target_cfg
.arch
,
134 allowed_names
: ContainsEither { a: &target_cfgs.all_archs, b: &EXTRA_ARCHS }
,
135 message
: "when the architecture is {name}"
138 name
: format
!("{}bit", target_cfg
.pointer_width
),
139 allowed_names
: &target_cfgs
.all_pointer_widths
,
140 message
: "when the pointer width is {name}"
143 name
: &*target_cfg
.families
,
144 allowed_names
: &target_cfgs
.all_families
,
145 message
: "when the target family is {name}"
148 // `wasm32-bare` is an alias to refer to just wasm32-unknown-unknown
149 // (in contrast to `wasm32` which also matches non-bare targets)
152 condition
: config
.target
== "wasm32-unknown-unknown",
153 message
: "when the target is WASM"
158 condition
: config
.target
.starts_with("thumb"),
159 message
: "when the architecture is part of the Thumb family"
162 // Technically the locally built compiler uses the "dev" channel rather than the "nightly"
163 // channel, even though most people don't know or won't care about it. To avoid confusion, we
164 // treat the "dev" channel as the "nightly" channel when processing the directive.
166 name
: if config
.channel
== "dev" { "nightly" }
else { &config.channel }
,
167 allowed_names
: &["stable", "beta", "nightly"],
168 message
: "when the release channel is {name}",
172 name
: "cross-compile",
173 condition
: config
.target
!= config
.host
,
174 message
: "when cross-compiling"
178 condition
: config
.is_big_endian(),
179 message
: "on big-endian targets",
182 name
: config
.stage_id
.split('
-'
).next().unwrap(),
183 allowed_names
: &["stage0", "stage1", "stage2"],
184 message
: "when the bootstrapping stage is {name}",
188 condition
: config
.remote_test_client
.is_some(),
189 message
: "when running tests remotely",
193 condition
: config
.with_debug_assertions
,
194 message
: "when running tests with `ignore-debug` header",
197 name
: config
.debugger
.as_ref().map(|d
| d
.to_str()),
198 allowed_names
: &Debugger
::STR_VARIANTS
,
199 message
: "when the debugger is {name}",
202 name
: config
.compare_mode
204 .map(|d
| format
!("compare-mode-{}", d
.to_str())),
205 allowed_names
: ContainsPrefixed
{
206 prefix
: "compare-mode-",
207 inner
: CompareMode
::STR_VARIANTS
,
209 message
: "when comparing with {name}",
212 if prefix
== "ignore" && outcome
== MatchOutcome
::Invalid
{
213 // Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.
214 if name
.starts_with("tidy-") {
215 outcome
= MatchOutcome
::External
;
218 // Don't error out for ignore-pass, as that is handled elsewhere.
220 outcome
= MatchOutcome
::External
;
223 // Don't error out for ignore-llvm-version, that has a custom syntax and is handled
225 if name
== "llvm-version" {
226 outcome
= MatchOutcome
::External
;
229 // Don't error out for ignore-llvm-version, that has a custom syntax and is handled
231 if name
== "gdb-version" {
232 outcome
= MatchOutcome
::External
;
236 ParsedNameDirective
{
238 comment
: comment
.map(|c
| c
.trim().trim_start_matches('
-'
).trim()),
240 pretty_reason
: message
,
244 /// The result of parse_cfg_name_directive.
245 #[derive(Clone, PartialEq, Debug)]
246 pub(super) struct ParsedNameDirective
<'a
> {
247 pub(super) name
: Option
<&'a
str>,
248 pub(super) pretty_reason
: Option
<String
>,
249 pub(super) comment
: Option
<&'a
str>,
250 pub(super) outcome
: MatchOutcome
,
253 impl ParsedNameDirective
<'_
> {
254 fn not_a_directive() -> Self {
259 outcome
: MatchOutcome
::NotADirective
,
264 #[derive(Clone, Copy, PartialEq, Debug)]
265 pub(super) enum MatchOutcome
{
270 /// The directive was invalid.
272 /// The directive is handled by other parts of our tooling.
274 /// The line is not actually a directive.
278 trait CustomContains
{
279 fn custom_contains(&self, item
: &str) -> bool
;
282 impl CustomContains
for HashSet
<String
> {
283 fn custom_contains(&self, item
: &str) -> bool
{
288 impl CustomContains
for &[&str] {
289 fn custom_contains(&self, item
: &str) -> bool
{
294 impl<const N
: usize> CustomContains
for [&str; N
] {
295 fn custom_contains(&self, item
: &str) -> bool
{
300 struct ContainsPrefixed
<T
: CustomContains
> {
301 prefix
: &'
static str,
305 impl<T
: CustomContains
> CustomContains
for ContainsPrefixed
<T
> {
306 fn custom_contains(&self, item
: &str) -> bool
{
307 match item
.strip_prefix(self.prefix
) {
308 Some(stripped
) => self.inner
.custom_contains(stripped
),
314 struct ContainsEither
<'a
, A
: CustomContains
, B
: CustomContains
> {
319 impl<A
: CustomContains
, B
: CustomContains
> CustomContains
for ContainsEither
<'_
, A
, B
> {
320 fn custom_contains(&self, item
: &str) -> bool
{
321 self.a
.custom_contains(item
) || self.b
.custom_contains(item
)
325 trait CustomMatches
{
326 fn custom_matches(&self, name
: &str) -> bool
;
329 impl CustomMatches
for &str {
330 fn custom_matches(&self, name
: &str) -> bool
{
335 impl CustomMatches
for String
{
336 fn custom_matches(&self, name
: &str) -> bool
{
341 impl<T
: CustomMatches
> CustomMatches
for &[T
] {
342 fn custom_matches(&self, name
: &str) -> bool
{
343 self.iter().any(|m
| m
.custom_matches(name
))
347 impl<const N
: usize, T
: CustomMatches
> CustomMatches
for [T
; N
] {
348 fn custom_matches(&self, name
: &str) -> bool
{
349 self.iter().any(|m
| m
.custom_matches(name
))
353 impl<T
: CustomMatches
> CustomMatches
for Option
<T
> {
354 fn custom_matches(&self, name
: &str) -> bool
{
356 Some(inner
) => inner
.custom_matches(name
),