1use std::collections::HashMap;
2use std::sync::{Arc, LazyLock};
3
4use crate::common::Config;
5use crate::directives::{
6 DirectiveLine, NormalizeKind, NormalizeRule, TestProps, parse_and_update_aux,
7 parse_edition_range, split_flags,
8};
9use crate::errors::ErrorKind;
10
11pub(crate) static DIRECTIVE_HANDLERS_MAP: LazyLock<HashMap<&str, Handler>> =
12 LazyLock::new(make_directive_handlers_map);
13
14#[derive(Clone)]
15pub(crate) struct Handler {
16 handler_fn: Arc<dyn Fn(HandlerArgs<'_>) + Send + Sync>,
17}
18
19impl Handler {
20 pub(crate) fn handle(&self, config: &Config, line: &DirectiveLine<'_>, props: &mut TestProps) {
21 (self.handler_fn)(HandlerArgs { config, line, props })
22 }
23}
24
25struct HandlerArgs<'a> {
26 config: &'a Config,
27 line: &'a DirectiveLine<'a>,
28 props: &'a mut TestProps,
29}
30
31struct NamedHandler {
33 names: Vec<&'static str>,
34 handler: Handler,
35}
36
37fn handler(
40 name: &'static str,
41 handler_fn: impl Fn(&Config, &DirectiveLine<'_>, &mut TestProps) + Send + Sync + 'static,
42) -> NamedHandler {
43 multi_handler(&[name], handler_fn)
44}
45
46fn multi_handler(
48 names: &[&'static str],
49 handler_fn: impl Fn(&Config, &DirectiveLine<'_>, &mut TestProps) + Send + Sync + 'static,
50) -> NamedHandler {
51 NamedHandler {
52 names: names.to_owned(),
53 handler: Handler {
54 handler_fn: Arc::new(move |args| handler_fn(args.config, args.line, args.props)),
55 },
56 }
57}
58
59fn make_directive_handlers_map() -> HashMap<&'static str, Handler> {
60 use crate::directives::directives::*;
61
62 let handlers: Vec<NamedHandler> = vec![
67 handler(ERROR_PATTERN, |config, ln, props| {
68 config.push_name_value_directive(ln, ERROR_PATTERN, &mut props.error_patterns, |r| r);
69 }),
70 handler(REGEX_ERROR_PATTERN, |config, ln, props| {
71 config.push_name_value_directive(
72 ln,
73 REGEX_ERROR_PATTERN,
74 &mut props.regex_error_patterns,
75 |r| r,
76 );
77 }),
78 handler(DOC_FLAGS, |config, ln, props| {
79 config.push_name_value_directive(ln, DOC_FLAGS, &mut props.doc_flags, |r| r);
80 }),
81 handler(COMPILE_FLAGS, |config, ln, props| {
82 if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
83 let flags = split_flags(&flags);
84 for (i, flag) in flags.iter().enumerate() {
87 if flag == "--edition" || flag.starts_with("--edition=") {
88 panic!("you must use `//@ edition` to configure the edition");
89 }
90 if (flag == "-C"
91 && flags.get(i + 1).is_some_and(|v| v.starts_with("incremental=")))
92 || flag.starts_with("-Cincremental=")
93 {
94 panic!("you must use `//@ incremental` to enable incremental compilation");
95 }
96 }
97 props.compile_flags.extend(flags);
98 }
99 }),
100 handler("edition", |config, ln, props| {
101 if let Some(range) = parse_edition_range(config, ln) {
102 props.edition = Some(range.edition_to_test(config.edition));
103 }
104 }),
105 handler("revisions", |config, ln, props| {
106 config.parse_and_update_revisions(ln, &mut props.revisions);
107 }),
108 handler(RUN_FLAGS, |config, ln, props| {
109 if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) {
110 props.run_flags.extend(split_flags(&flags));
111 }
112 }),
113 handler("pp-exact", |config, ln, props| {
114 if props.pp_exact.is_none() {
115 props.pp_exact = config.parse_pp_exact(ln);
116 }
117 }),
118 handler(SHOULD_ICE, |config, ln, props| {
119 config.set_name_directive(ln, SHOULD_ICE, &mut props.should_ice);
120 }),
121 handler(BUILD_AUX_DOCS, |config, ln, props| {
122 config.set_name_directive(ln, BUILD_AUX_DOCS, &mut props.build_aux_docs);
123 }),
124 handler(UNIQUE_DOC_OUT_DIR, |config, ln, props| {
125 config.set_name_directive(ln, UNIQUE_DOC_OUT_DIR, &mut props.unique_doc_out_dir);
126 }),
127 handler(FORCE_HOST, |config, ln, props| {
128 config.set_name_directive(ln, FORCE_HOST, &mut props.force_host);
129 }),
130 handler(CHECK_STDOUT, |config, ln, props| {
131 config.set_name_directive(ln, CHECK_STDOUT, &mut props.check_stdout);
132 }),
133 handler(CHECK_RUN_RESULTS, |config, ln, props| {
134 config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut props.check_run_results);
135 }),
136 handler(DONT_CHECK_COMPILER_STDOUT, |config, ln, props| {
137 config.set_name_directive(
138 ln,
139 DONT_CHECK_COMPILER_STDOUT,
140 &mut props.dont_check_compiler_stdout,
141 );
142 }),
143 handler(DONT_CHECK_COMPILER_STDERR, |config, ln, props| {
144 config.set_name_directive(
145 ln,
146 DONT_CHECK_COMPILER_STDERR,
147 &mut props.dont_check_compiler_stderr,
148 );
149 }),
150 handler(NO_PREFER_DYNAMIC, |config, ln, props| {
151 config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut props.no_prefer_dynamic);
152 }),
153 handler(PRETTY_MODE, |config, ln, props| {
154 if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) {
155 props.pretty_mode = m;
156 }
157 }),
158 handler(PRETTY_COMPARE_ONLY, |config, ln, props| {
159 config.set_name_directive(ln, PRETTY_COMPARE_ONLY, &mut props.pretty_compare_only);
160 }),
161 multi_handler(
162 &[AUX_BUILD, AUX_BIN, AUX_CRATE, PROC_MACRO, AUX_CODEGEN_BACKEND],
163 |config, ln, props| {
164 parse_and_update_aux(config, ln, &mut props.aux);
166 },
167 ),
168 handler(EXEC_ENV, |config, ln, props| {
169 config.push_name_value_directive(ln, EXEC_ENV, &mut props.exec_env, Config::parse_env);
170 }),
171 handler(UNSET_EXEC_ENV, |config, ln, props| {
172 config.push_name_value_directive(ln, UNSET_EXEC_ENV, &mut props.unset_exec_env, |r| {
173 r.trim().to_owned()
174 });
175 }),
176 handler(RUSTC_ENV, |config, ln, props| {
177 config.push_name_value_directive(
178 ln,
179 RUSTC_ENV,
180 &mut props.rustc_env,
181 Config::parse_env,
182 );
183 }),
184 handler(UNSET_RUSTC_ENV, |config, ln, props| {
185 config.push_name_value_directive(
186 ln,
187 UNSET_RUSTC_ENV,
188 &mut props.unset_rustc_env,
189 |r| r.trim().to_owned(),
190 );
191 }),
192 handler(FORBID_OUTPUT, |config, ln, props| {
193 config.push_name_value_directive(ln, FORBID_OUTPUT, &mut props.forbid_output, |r| r);
194 }),
195 handler(CHECK_TEST_LINE_NUMBERS_MATCH, |config, ln, props| {
196 config.set_name_directive(
197 ln,
198 CHECK_TEST_LINE_NUMBERS_MATCH,
199 &mut props.check_test_line_numbers_match,
200 );
201 }),
202 multi_handler(&["check-pass", "build-pass", "run-pass"], |config, ln, props| {
203 props.update_pass_mode(ln, config);
204 }),
205 multi_handler(
206 &["check-fail", "build-fail", "run-fail", "run-crash", "run-fail-or-crash"],
207 |config, ln, props| {
208 props.update_fail_mode(ln, config);
209 },
210 ),
211 handler(IGNORE_PASS, |config, ln, props| {
212 config.set_name_directive(ln, IGNORE_PASS, &mut props.ignore_pass);
213 }),
214 multi_handler(
215 &[
216 "normalize-stdout",
217 "normalize-stderr",
218 "normalize-stderr-32bit",
219 "normalize-stderr-64bit",
220 ],
221 |config, ln, props| {
222 if let Some(NormalizeRule { kind, regex, replacement }) =
223 config.parse_custom_normalization(ln)
224 {
225 let rule_tuple = (regex, replacement);
226 match kind {
227 NormalizeKind::Stdout => props.normalize_stdout.push(rule_tuple),
228 NormalizeKind::Stderr => props.normalize_stderr.push(rule_tuple),
229 NormalizeKind::Stderr32bit => {
230 if config.target_cfg().pointer_width == 32 {
231 props.normalize_stderr.push(rule_tuple);
232 }
233 }
234 NormalizeKind::Stderr64bit => {
235 if config.target_cfg().pointer_width == 64 {
236 props.normalize_stderr.push(rule_tuple);
237 }
238 }
239 }
240 }
241 },
242 ),
243 handler(FAILURE_STATUS, |config, ln, props| {
244 if let Some(code) = config
245 .parse_name_value_directive(ln, FAILURE_STATUS)
246 .and_then(|code| code.trim().parse::<i32>().ok())
247 {
248 props.failure_status = Some(code);
249 }
250 }),
251 handler(DONT_CHECK_FAILURE_STATUS, |config, ln, props| {
252 config.set_name_directive(
253 ln,
254 DONT_CHECK_FAILURE_STATUS,
255 &mut props.dont_check_failure_status,
256 );
257 }),
258 handler(RUN_RUSTFIX, |config, ln, props| {
259 config.set_name_directive(ln, RUN_RUSTFIX, &mut props.run_rustfix);
260 }),
261 handler(RUSTFIX_ONLY_MACHINE_APPLICABLE, |config, ln, props| {
262 config.set_name_directive(
263 ln,
264 RUSTFIX_ONLY_MACHINE_APPLICABLE,
265 &mut props.rustfix_only_machine_applicable,
266 );
267 }),
268 handler(ASSEMBLY_OUTPUT, |config, ln, props| {
269 config.set_name_value_directive(ln, ASSEMBLY_OUTPUT, &mut props.assembly_output, |r| {
270 r.trim().to_string()
271 });
272 }),
273 handler(STDERR_PER_BITWIDTH, |config, ln, props| {
274 config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut props.stderr_per_bitwidth);
275 }),
276 handler(INCREMENTAL, |config, ln, props| {
277 config.set_name_directive(ln, INCREMENTAL, &mut props.incremental);
278 }),
279 handler(KNOWN_BUG, |config, ln, props| {
280 if let Some(known_bug) = config.parse_name_value_directive(ln, KNOWN_BUG) {
283 let known_bug = known_bug.trim();
284 if known_bug == "unknown"
285 || known_bug.split(',').all(|issue_ref| {
286 issue_ref
287 .trim()
288 .split_once('#')
289 .filter(|(_, number)| number.chars().all(|digit| digit.is_numeric()))
290 .is_some()
291 })
292 {
293 props.known_bug = true;
294 } else {
295 panic!(
296 "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
297 );
298 }
299 } else if config.parse_name_directive(ln, KNOWN_BUG) {
300 panic!(
301 "Invalid known-bug attribute, requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
302 );
303 }
304 }),
305 handler(TEST_MIR_PASS, |config, ln, props| {
306 config.set_name_value_directive(ln, TEST_MIR_PASS, &mut props.mir_unit_test, |s| {
307 s.trim().to_string()
308 });
309 }),
310 handler(REMAP_SRC_BASE, |config, ln, props| {
311 config.set_name_directive(ln, REMAP_SRC_BASE, &mut props.remap_src_base);
312 }),
313 handler(LLVM_COV_FLAGS, |config, ln, props| {
314 if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
315 props.llvm_cov_flags.extend(split_flags(&flags));
316 }
317 }),
318 handler(FILECHECK_FLAGS, |config, ln, props| {
319 if let Some(flags) = config.parse_name_value_directive(ln, FILECHECK_FLAGS) {
320 props.filecheck_flags.extend(split_flags(&flags));
321 }
322 }),
323 handler(NO_AUTO_CHECK_CFG, |config, ln, props| {
324 config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut props.no_auto_check_cfg);
325 }),
326 handler(ADD_MINICORE, |config, ln, props| {
327 props.update_add_minicore(ln, config);
328 }),
329 handler(MINICORE_COMPILE_FLAGS, |config, ln, props| {
330 if let Some(flags) = config.parse_name_value_directive(ln, MINICORE_COMPILE_FLAGS) {
331 let flags = split_flags(&flags);
332 for flag in &flags {
335 if flag == "--edition" || flag.starts_with("--edition=") {
336 panic!("you must use `//@ edition` to configure the edition");
337 }
338 }
339 props.minicore_compile_flags.extend(flags);
340 }
341 }),
342 handler(DONT_REQUIRE_ANNOTATIONS, |config, ln, props| {
343 if let Some(err_kind) = config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS)
344 {
345 props
346 .dont_require_annotations
347 .insert(ErrorKind::expect_from_user_str(err_kind.trim()));
348 }
349 }),
350 handler(DISABLE_GDB_PRETTY_PRINTERS, |config, ln, props| {
351 config.set_name_directive(
352 ln,
353 DISABLE_GDB_PRETTY_PRINTERS,
354 &mut props.disable_gdb_pretty_printers,
355 );
356 }),
357 handler(COMPARE_OUTPUT_BY_LINES, |config, ln, props| {
358 config.set_name_directive(
359 ln,
360 COMPARE_OUTPUT_BY_LINES,
361 &mut props.compare_output_by_lines,
362 );
363 }),
364 ];
365
366 handlers
367 .into_iter()
368 .flat_map(|NamedHandler { names, handler }| {
369 names.into_iter().map(move |name| (name, Handler::clone(&handler)))
370 })
371 .collect()
372}