1#![doc = include_str!("../README.md")]
2#![no_std]
3
4extern crate alloc;
5#[cfg(test)]
6extern crate std;
7
8mod alpha;
9mod drift;
10
11use alloc::vec::Vec;
12use proc_macro2::{TokenStream, TokenTree};
13use quote::ToTokens;
14
15#[must_use]
18pub fn canonicalize(file: syn::File) -> TokenStream {
19 canonical(file, true)
20}
21
22#[must_use]
25pub fn canonicalize_failing(file: syn::File) -> TokenStream {
26 canonical(file, false)
27}
28
29fn canonical(mut file: syn::File, compiles: bool) -> TokenStream {
30 drift::normalize_file(&mut file, compiles);
31 alpha::normalize_file(&mut file);
32 fold_tokens(file.to_token_stream(), false, false)
33 .into_iter()
34 .collect()
35}
36
37const COMMA_BLIND_MACROS: [&str; 21] = [
39 "assert",
40 "assert_eq",
41 "assert_ne",
42 "dbg",
43 "debug_assert",
44 "debug_assert_eq",
45 "debug_assert_ne",
46 "eprint",
47 "eprintln",
48 "format",
49 "format_args",
50 "matches",
51 "panic",
52 "print",
53 "println",
54 "todo",
55 "unimplemented",
56 "unreachable",
57 "vec",
58 "write",
59 "writeln",
60];
61
62const COMMA_BLIND_ATTRIBUTES: [&str; 10] = [
64 "allow", "cfg", "cfg_attr", "deny", "derive", "expect", "feature", "forbid", "repr", "warn",
65];
66
67fn fold_tokens(stream: TokenStream, opaque: bool, keep_commas: bool) -> Vec<TokenTree> {
70 let mut out: Vec<TokenTree> = Vec::new();
71 let (mut derived, mut derived_item) = (false, false);
73 for tree in stream {
74 let tree = match tree {
75 TokenTree::Ident(ident) => {
76 derived_item |= derived && ["struct", "enum", "union"].iter().any(|kw| ident == kw);
77 TokenTree::Ident(ident)
78 }
79 TokenTree::Punct(punct) => {
80 if punct.as_char() == ';' {
81 (derived, derived_item) = (false, false);
82 }
83 TokenTree::Punct(punct)
84 }
85 TokenTree::Group(group) => {
86 let shut = opaque
87 || matches!(out.last(), Some(TokenTree::Punct(p)) if matches!(p.as_char(), '!' | '#'));
88 derived |= macro_derive_attribute(&group);
89 let body = derived_item
90 && matches!(
91 group.delimiter(),
92 proc_macro2::Delimiter::Brace | proc_macro2::Delimiter::Parenthesis
93 );
94 if body {
95 (derived, derived_item) = (false, false);
96 }
97 let keep = keep_commas || body || reads_commas(&out, &group);
98 drift::map_group(&group, |inner| {
99 let mut inner = fold_tokens(inner, shut, keep);
100 if !keep
102 && matches!(inner.last(), Some(TokenTree::Punct(p)) if p.as_char() == ',')
103 {
104 inner.pop();
105 }
106 inner.into_iter().collect()
107 })
108 }
109 TokenTree::Literal(lit) if !opaque => drift::canonical_literal(lit),
110 TokenTree::Literal(lit) => TokenTree::Literal(lit),
111 };
112 out.push(tree);
113 }
114 out
115}
116
117fn macro_derive_attribute(group: &proc_macro2::Group) -> bool {
120 let mut inner = group.stream().into_iter();
121 match (inner.next(), inner.next()) {
122 (Some(TokenTree::Ident(name)), Some(TokenTree::Group(list))) if name == "derive" => {
123 list.stream().into_iter().any(|tree| {
124 matches!(tree, TokenTree::Ident(name)
125 if !drift::STD_DERIVES.iter().any(|std| name == std))
126 })
127 }
128 _ => false,
129 }
130}
131
132fn reads_commas(before: &[TokenTree], group: &proc_macro2::Group) -> bool {
135 let back = |n: usize| before.len().checked_sub(n).and_then(|i| before.get(i));
136 let punct = |tree: Option<&TokenTree>, c: char| matches!(tree, Some(TokenTree::Punct(p)) if p.as_char() == c);
137 if punct(back(1), '!')
138 && let Some(TokenTree::Ident(name)) = back(2)
139 {
140 return !COMMA_BLIND_MACROS.iter().any(|blind| name == blind);
141 }
142 let attribute = group.delimiter() == proc_macro2::Delimiter::Bracket
143 && (punct(back(1), '#') || (punct(back(1), '!') && punct(back(2), '#')));
144 if !attribute {
145 return false;
146 }
147 let mut inner = group.stream().into_iter();
148 let builtin = match (inner.next(), inner.next()) {
149 (Some(TokenTree::Ident(name)), next) => {
150 !punct(next.as_ref(), ':') && COMMA_BLIND_ATTRIBUTES.iter().any(|blind| name == blind)
151 }
152 _ => false,
153 };
154 !builtin
155}
156
157#[cfg(test)]
158mod tests {
159 use super::canonicalize as canon;
160 use alloc::format;
161 use alloc::string::{String, ToString};
162 use alloc::vec::Vec;
163
164 struct Canon {
166 text: String,
167 }
168
169 fn parse(code: &str) -> syn::File {
171 let is_main = |item: &syn::Item| matches!(item, syn::Item::Fn(f) if f.sig.ident == "main");
172 match syn::parse_str::<syn::File>(code) {
173 Ok(file) if file.items.iter().any(is_main) => file,
174 _ => syn::parse_str::<syn::File>(&format!("fn main() {{ {code}\n}}"))
175 .unwrap_or_else(|e| panic!("{code}: {e}")),
176 }
177 }
178
179 fn canonicalize(code: &str) -> Canon {
181 Canon {
182 text: canon(parse(code)).to_string(),
183 }
184 }
185
186 #[test]
187 fn a_runtime_relative_path_is_its_own_content() {
188 let a = r#"let f = File::open("../data.csv").unwrap();"#;
189 let b = r#"let f = File::open("data.csv").unwrap();"#;
190 assert_ne!(canonicalize(a).text, canonicalize(b).text);
191 }
192
193 #[test]
194 fn macro_call_bracket_and_paren_merge() {
195 let a = canonicalize("vec![1, 2]");
196 let b = canonicalize("vec!(1, 2)");
197 assert_eq!(a.text, b.text);
198 }
199
200 #[test]
201 fn macro_call_all_three_delimiters_merge() {
202 let bracket = canonicalize("vec![1, 2];\nf();");
203 let paren = canonicalize("vec!(1, 2);\nf();");
204 let brace = canonicalize("vec! {1, 2}\nf();");
205 assert_eq!(bracket.text, paren.text);
206 assert_eq!(paren.text, brace.text);
207 }
208
209 #[test]
210 fn statement_macro_brace_merges_with_paren() {
211 let a = canonicalize("println! {\"hello\"}\nf();");
212 let b = canonicalize("println!(\"hello\");\nf();");
213 assert_eq!(a.text, b.text);
214 }
215
216 #[test]
217 fn a_tail_macro_without_a_semicolon_stays() {
218 let tail = canonicalize("macro_rules! five { () => { 5 } }\nfn f() -> i32 { five! {} }");
219 let discarded =
220 canonicalize("macro_rules! five { () => { 5 } }\nfn f() -> i32 { five!(); }");
221 assert_ne!(tail.text, discarded.text);
222 }
223
224 #[test]
225 fn a_non_tail_macro_statement_merges_either_delimiter() {
226 let a = canonicalize("m! {1}\nf();");
227 let b = canonicalize("m!(1);\nf();");
228 assert_eq!(a.text, b.text);
229 }
230
231 #[test]
232 fn macro_in_type_position_delimiter_merges() {
233 let a = canonicalize("type T = my_type![u32];");
234 let b = canonicalize("type T = my_type!(u32);");
235 assert_eq!(a.text, b.text);
236 }
237
238 #[test]
239 fn macro_in_pattern_position_delimiter_merges() {
240 let a = canonicalize("match 42u8 { my_pat![42] => 1u8, _ => 2u8 }");
241 let b = canonicalize("match 42u8 { my_pat!(42) => 1u8, _ => 2u8 }");
242 assert_eq!(a.text, b.text);
243 }
244
245 #[test]
246 fn macro_in_an_impl_item_delimiter_merges() {
247 let a = canonicalize("struct S;\nimpl S { my_items! {1} }");
248 let b = canonicalize("struct S;\nimpl S { my_items!(1); }");
249 assert_eq!(a.text, b.text);
250 }
251
252 #[test]
253 fn macro_in_a_trait_item_delimiter_merges() {
254 let a = canonicalize("trait T { my_items! {1} }");
255 let b = canonicalize("trait T { my_items!(1); }");
256 assert_eq!(a.text, b.text);
257 }
258
259 #[test]
260 fn item_level_macro_without_ident_merges() {
261 let a = canonicalize("fn main() {}\nfoo! { x }");
262 let b = canonicalize("fn main() {}\nfoo!(x);");
263 assert_eq!(a.text, b.text);
264 }
265
266 #[test]
267 fn nested_macro_outer_delimiter_merges() {
268 let a = canonicalize("outer![inner!(x)]");
269 let b = canonicalize("outer!(inner!(x))");
270 assert_eq!(a.text, b.text);
271 }
272
273 #[test]
274 fn macro_argument_bracket_group_survives() {
275 let a = canonicalize("foo![arr[0]]");
276 let b = canonicalize("foo!(arr[0])");
277 assert_eq!(a.text, b.text);
278 }
279
280 #[test]
281 fn macro_rules_inner_delimiter_stays_distinct() {
282 let a = canonicalize("fn main() {}\nmacro_rules! m { ([$a:expr]) => { $a }; }");
283 let b = canonicalize("fn main() {}\nmacro_rules! m { (($a:expr)) => { $a }; }");
284 assert_ne!(a.text, b.text);
285 }
286
287 #[test]
288 fn macro_calls_with_different_args_stay_distinct() {
289 let a = canonicalize("foo!(1)");
290 let b = canonicalize("foo!(2)");
291 assert_ne!(a.text, b.text);
292 }
293
294 #[test]
295 fn macro_call_vs_plain_call_stays_distinct() {
296 let a = canonicalize("foo!(x)");
297 let b = canonicalize("foo(x)");
298 assert_ne!(a.text, b.text);
299 }
300
301 #[test]
302 fn macro_rules_body_delimiter_stays_distinct() {
303 let a = canonicalize("fn main() {}\nmacro_rules! m { ($a:expr) => { [$a] }; }");
304 let b = canonicalize("fn main() {}\nmacro_rules! m { ($a:expr) => { ($a) }; }");
305 assert_ne!(a.text, b.text);
306 }
307
308 #[test]
309 fn integer_underscore_and_plain_agree() {
310 assert_eq!(canonicalize("1_000").text, canonicalize("1000").text);
311 }
312
313 #[test]
314 fn integer_hex_and_decimal_agree() {
315 assert_eq!(canonicalize("0x1F").text, canonicalize("31").text);
316 }
317
318 #[test]
319 fn a_stringified_literal_keeps_its_spelling() {
320 let a = canonicalize("assert_eq!(stringify!(0x10), \"0x10\");");
321 let b = canonicalize("assert_eq!(stringify!(16), \"0x10\");");
322 assert_ne!(a.text, b.text);
323 }
324
325 #[test]
326 fn a_matcher_literal_keeps_its_spelling() {
327 let a = canonicalize("macro_rules! m { (0x10) => { 1 } }\nm!(0x10);");
328 let b = canonicalize("macro_rules! m { (16) => { 1 } }\nm!(16);");
329 assert_ne!(a.text, b.text);
330 }
331
332 #[test]
333 fn integer_binary_and_decimal_agree() {
334 assert_eq!(canonicalize("0b1010").text, canonicalize("10").text);
335 }
336
337 #[test]
338 fn integer_octal_and_decimal_agree() {
339 assert_eq!(canonicalize("0o17").text, canonicalize("15").text);
340 }
341
342 #[test]
343 fn integer_suffixed_across_radices_agree() {
344 assert_eq!(canonicalize("0x1u8").text, canonicalize("1u8").text);
345 }
346
347 #[test]
348 fn a_radix_literal_whose_suffix_misreads_in_decimal_keeps_its_spelling() {
349 for literal in ["0b0buu", "0o7e3", "0b0x1", "0b0x0"] {
352 assert!(canonicalize(literal).text.contains(literal), "{literal}");
353 }
354 }
355
356 #[test]
357 fn float_trailing_dot_and_dot_zero_agree() {
358 assert_eq!(canonicalize("1.").text, canonicalize("1.0").text);
359 }
360
361 #[test]
362 fn float_trailing_zeros_agree() {
363 assert_eq!(canonicalize("1.50").text, canonicalize("1.500").text);
364 assert_eq!(canonicalize("1.50").text, canonicalize("1.5").text);
365 }
366
367 #[test]
368 fn a_float_canonicalizes_to_its_text_form() {
369 assert_eq!(canonicalize("1_000.50").text, "fn _canon_0 () { 1000.5 }");
370 assert_eq!(canonicalize("1.0E+03").text, "fn _canon_0 () { 1.0e3 }");
371 }
372
373 #[test]
374 fn float_exponent_form_stays_distinct() {
375 assert_ne!(canonicalize("1e3").text, canonicalize("1000.0").text);
376 assert_eq!(canonicalize("1.0E+03").text, canonicalize("1.0e3").text);
377 }
378
379 #[test]
380 fn char_unicode_escape_and_literal_agree() {
381 assert_eq!(canonicalize(r"'\u{41}'").text, canonicalize("'A'").text);
382 }
383
384 #[test]
385 fn exponent_sign_and_padding_agree() {
386 assert_eq!(canonicalize("1.0e-03").text, canonicalize("1.0e-3").text);
387 assert_eq!(canonicalize("1.0e+3").text, canonicalize("1.0e3").text);
388 assert_ne!(canonicalize("1.0e-3").text, canonicalize("1.0e3").text);
389 }
390
391 #[test]
392 fn byte_and_c_literals_agree_with_their_escapes() {
393 assert_eq!(canonicalize(r"b'\x41'").text, canonicalize("b'A'").text);
394 assert_eq!(canonicalize(r#"br"a""#).text, canonicalize(r#"b"a""#).text);
395 assert_eq!(canonicalize(r#"cr"a""#).text, canonicalize(r#"c"a""#).text);
396 assert_ne!(canonicalize(r#"b"a""#).text, canonicalize(r#"b"b""#).text);
397 }
398
399 #[test]
400 fn char_longer_unicode_escape_and_literal_agree() {
401 assert_eq!(canonicalize(r"'\u{0041}'").text, canonicalize("'A'").text);
402 }
403
404 #[test]
405 fn raw_string_and_escaped_string_agree() {
406 assert_eq!(
407 canonicalize(r#"r"hello""#).text,
408 canonicalize(r#""hello""#).text
409 );
410 }
411
412 #[test]
413 fn macro_argument_literals_stay_opaque() {
414 let a = canonicalize("assert_eq!(1_000, 1000);");
415 let b = canonicalize("assert_eq!(1000, 1000);");
416 assert_ne!(a.text, b.text);
417 let c = canonicalize("vec![vec![0x01, 0x02]]");
418 let d = canonicalize("vec![vec![1, 2]]");
419 assert_ne!(c.text, d.text);
420 }
421
422 #[test]
423 fn string_literal_in_attribute_value_stays_opaque() {
424 let a = canonicalize(r#"#[my_attr(label = r"same")] fn f() {}"#);
425 let b = canonicalize(r#"#[my_attr(label = "same")] fn f() {}"#);
426 assert_ne!(a.text, b.text);
427 }
428
429 #[test]
430 fn an_attribute_argument_literal_stays_opaque() {
431 let attr = canonicalize("#[my_attr(label = 0x10)]\nfn f() {}");
432 assert_ne!(
433 attr.text,
434 canonicalize("#[my_attr(label = 16)]\nfn f() {}").text
435 );
436 let call = canonicalize("my_attr!(label = 0x10);");
437 assert_ne!(call.text, canonicalize("my_attr!(label = 16);").text);
438 }
439
440 #[test]
441 fn different_integers_stay_distinct() {
442 assert_ne!(canonicalize("1").text, canonicalize("2").text);
443 }
444
445 #[test]
446 fn different_hex_values_stay_distinct() {
447 assert_ne!(canonicalize("0x10").text, canonicalize("0x20").text);
448 }
449
450 #[test]
451 fn different_strings_stay_distinct() {
452 assert_ne!(
453 canonicalize(r#""hello""#).text,
454 canonicalize(r#""world""#).text
455 );
456 }
457
458 #[test]
459 fn different_chars_stay_distinct() {
460 assert_ne!(canonicalize("'a'").text, canonicalize("'b'").text);
461 }
462
463 #[test]
464 fn suffixed_and_unsuffixed_integer_stay_distinct() {
465 assert_ne!(canonicalize("1u8").text, canonicalize("1").text);
466 }
467
468 #[test]
469 fn different_float_values_stay_distinct() {
470 assert_ne!(canonicalize("1.0").text, canonicalize("2.0").text);
471 }
472
473 #[test]
474 fn trailing_comma_struct_literal_merges() {
475 let a = canonicalize("let _p = P { x: 1, y: 2 };");
476 let b = canonicalize("let _p = P { x: 1, y: 2, };");
477 assert_eq!(a.text, b.text);
478 }
479
480 #[test]
481 fn trailing_comma_call_merges() {
482 let a = canonicalize("foo(1, 2);");
483 let b = canonicalize("foo(1, 2,);");
484 assert_eq!(a.text, b.text);
485 }
486
487 #[test]
488 fn one_tuple_after_a_keyword_keeps_its_comma() {
489 let a = canonicalize("struct S;\ntrait T {}\nimpl T for (S,) {}");
490 let b = canonicalize("struct S;\ntrait T {}\nimpl T for (S) {}");
491 assert_ne!(a.text, b.text);
492 let c = canonicalize("fn f<T>() where (T,): Copy {}");
493 let d = canonicalize("fn f<T>() where (T): Copy {}");
494 assert_ne!(c.text, d.text);
495 }
496
497 #[test]
498 fn trailing_comma_vec_macro_merges() {
499 let a = canonicalize("vec![1, 2]");
500 let b = canonicalize("vec![1, 2,]");
501 assert_eq!(a.text, b.text);
502 }
503
504 #[test]
505 fn a_trailing_comma_a_macro_may_match_on_stays() {
506 for (with, without) in [
508 ("my_macro!(a, b,);", "my_macro!(a, b);"),
509 ("my_macro!((a, b,));", "my_macro!((a, b));"),
510 (
511 "tree! { 'a' => { 'b', 'c', } };",
512 "tree! { 'a' => { 'b', 'c' } };",
513 ),
514 (
515 "#[my_attr(a, b,)]\nfn f() {}",
516 "#[my_attr(a, b)]\nfn f() {}",
517 ),
518 ] {
519 assert_ne!(
520 canonicalize(with).text,
521 canonicalize(without).text,
522 "{with}"
523 );
524 }
525 }
526
527 #[test]
528 fn a_trailing_comma_a_std_macro_or_attribute_ignores_merges() {
529 for (with, without) in [
530 ("assert_eq!(a, b,);", "assert_eq!(a, b);"),
531 ("std::println!(\"{}\", x,);", "std::println!(\"{}\", x);"),
532 (
533 "assert!(matches!(x, Some(1),));",
534 "assert!(matches!(x, Some(1)));",
535 ),
536 (
537 "#[cfg(any(unix, windows,))]\nfn f() {}",
538 "#[cfg(any(unix, windows))]\nfn f() {}",
539 ),
540 ("let a = [1, 2,];", "let a = [1, 2];"),
541 ("let a = ![true, false,];", "let a = ![true, false];"),
542 (
543 "#[repr(C)]\nstruct S {\n x: u8,\n}",
544 "#[repr(C)]\nstruct S {\n x: u8\n}",
545 ),
546 (
547 "#[derive(serde::Serialize)]\nstruct U;\nfn f(a: u8,) {}",
548 "#[derive(serde::Serialize)]\nstruct U;\nfn f(a: u8) {}",
549 ),
550 ] {
551 assert_eq!(
552 canonicalize(with).text,
553 canonicalize(without).text,
554 "{with}"
555 );
556 }
557 }
558
559 #[test]
560 fn the_input_of_a_proc_macro_derive_keeps_its_docs_lints_and_commas() {
561 for (one, two) in [
563 (
564 "#[derive(clap::Parser)]\nstruct A {\n /// Help.\n x: u8,\n}",
565 "#[derive(clap::Parser)]\nstruct A {\n x: u8,\n}",
566 ),
567 (
568 "#[derive(TryFromPrimitive)]\nenum E {\n Zero,\n #[allow(unused)]\n One,\n}",
569 "#[derive(TryFromPrimitive)]\nenum E {\n Zero,\n One,\n}",
570 ),
571 (
572 "#[derive(serde::Serialize)]\nstruct P {\n x: u8,\n}",
573 "#[derive(serde::Serialize)]\nstruct P {\n x: u8\n}",
574 ),
575 (
576 "/// About.\n#[derive(Clone, clap::Parser)]\nstruct A(u8);",
577 "#[derive(Clone, clap::Parser)]\nstruct A(u8);",
578 ),
579 (
580 "#[derive(serde::Serialize)]\npub(crate) struct P {\n x: u8,\n}",
581 "#[derive(serde::Serialize)]\npub(crate) struct P {\n x: u8\n}",
582 ),
583 (
584 "#[derive(serde::Serialize)]\nstruct P<T> {\n x: T,\n}",
585 "#[derive(serde::Serialize)]\nstruct P<T> {\n x: T\n}",
586 ),
587 ] {
588 assert_ne!(canonicalize(one).text, canonicalize(two).text, "{one}");
589 }
590 }
591
592 #[test]
593 fn the_input_of_std_derives_keeps_every_fold() {
594 let a = canonicalize(
595 "#[derive(Clone, Debug)]\nstruct P {\n /// Doc.\n #[allow(dead_code)]\n x: u8,\n}",
596 );
597 let b = canonicalize("#[derive(Debug, Clone)]\nstruct P {\n x: u8\n}");
598 assert_eq!(a.text, b.text);
599 }
600
601 #[test]
602 fn a_doc_attribute_holding_a_macro_call_stays() {
603 assert_ne!(
605 canonicalize("#[doc = docify::embed!(\"a.rs\", x)]\npub struct S;").text,
606 canonicalize("pub struct S;").text
607 );
608 assert_ne!(
609 canonicalize("#[doc = include_str!(\"a.md\")]\npub struct S;").text,
610 canonicalize("#[doc = include_str!(\"b.md\")]\npub struct S;").text
611 );
612 assert_eq!(
613 canonicalize("#[doc = \"Text.\"]\npub struct S;").text,
614 canonicalize("pub struct S;").text
615 );
616 }
617
618 #[test]
619 fn trailing_comma_nested_struct_in_call_merges() {
620 let a = canonicalize("foo(P { x: 1, y: 2 })");
621 let b = canonicalize("foo(P { x: 1, y: 2, },)");
622 assert_eq!(a.text, b.text);
623 }
624
625 #[test]
626 fn one_tuple_stays_distinct() {
627 let a = canonicalize("let t = (1,);");
628 let b = canonicalize("let t = (1);");
629 assert_ne!(a.text, b.text);
630 }
631
632 #[test]
633 fn two_tuple_trailing_comma_merges() {
634 let a = canonicalize("let t = (1, 2,);");
635 let b = canonicalize("let t = (1, 2);");
636 assert_eq!(a.text, b.text);
637 }
638
639 #[test]
640 fn match_arm_block_single_expr_merges() {
641 let a = canonicalize("match v { Some(x) => { foo(x) }, None => 0, }");
642 let b = canonicalize("match v { Some(x) => foo(x), None => 0, }");
643 assert_eq!(a.text, b.text);
644 }
645
646 #[test]
647 fn labelled_or_attributed_arm_block_is_kept() {
648 let plain = canonicalize("match v { Some(x) => { foo(x) }, None => 0, }");
649 let labelled = canonicalize("match v { Some(x) => 'a: { foo(x) }, None => 0, }");
650 let attributed = canonicalize("match v { Some(x) => #[allow(x)] { foo(x) }, None => 0, }");
651 assert_ne!(labelled.text, plain.text);
652 assert_ne!(attributed.text, plain.text);
653 }
654
655 #[test]
656 fn a_statement_arm_block_keeps_its_statement() {
657 let a = canonicalize("match v { Some(x) => { foo(x); } None => {} }");
658 let b = canonicalize("match v { Some(x) => { bar(x); } None => {} }");
659 assert_ne!(a.text, b.text);
660 let c = canonicalize("match v { Some(x) => { m!(x); } None => {} }");
661 let d = canonicalize("match v { Some(x) => { n!(x); } None => {} }");
662 assert_ne!(c.text, d.text);
663 }
664
665 #[test]
666 fn match_arm_block_with_let_stays_distinct() {
667 let a = canonicalize("match v { Some(x) => { let y = x; foo(y) }, None => 0, }");
668 let b = canonicalize("match v { Some(x) => foo(x), None => 0, }");
669 assert_ne!(a.text, b.text);
670 }
671
672 #[test]
673 fn match_arm_block_with_semicolon_stays_distinct() {
674 let a = canonicalize("match v { Some(x) => { foo(x); }, None => 0, }");
675 let b = canonicalize("match v { Some(x) => foo(x), None => 0, }");
676 assert_ne!(a.text, b.text);
677 }
678
679 #[test]
680 fn a_block_arm_comma_merges() {
681 let a = canonicalize("match 1 { 1 => {}, _ => {} }");
682 let b = canonicalize("match 1 { 1 => {} _ => {} }");
683 assert_eq!(a.text, b.text);
684 }
685
686 #[test]
687 fn an_unwrapped_arm_block_merges_with_the_comma_form() {
688 let a = canonicalize("match v { 1 => { foo() } _ => 0 }");
689 let b = canonicalize("match v { 1 => foo(), _ => 0 }");
690 assert_eq!(a.text, b.text);
691 }
692
693 #[test]
694 fn a_tail_return_arm_block_merges_with_the_comma_form() {
695 let a = canonicalize("fn f(v: u8) -> u8 { match v { 1 => { return 2; } _ => 0 } }");
696 let b = canonicalize("fn f(v: u8) -> u8 { match v { 1 => 2, _ => 0 } }");
697 assert_eq!(a.text, b.text);
698 }
699
700 #[test]
701 fn doc_comment_on_local_fn_merges() {
702 let a = canonicalize("/// Doc comment.\nfn f() -> u8 { 1 }");
703 let b = canonicalize("fn f() -> u8 { 1 }");
704 assert_eq!(a.text, b.text);
705 }
706
707 #[test]
708 fn doc_comments_on_every_item_kind_merge() {
709 let body = |doc: &str| {
710 [
711 "fn main() {}",
712 "const C: u8 = 1;",
713 "enum E { A }",
714 "#[macro_use]\nextern crate foo;",
715 "extern \"C\" { fn cf(); }",
716 "impl S {}",
717 "m! {}",
718 "mod md {}",
719 "static ST: u8 = 1;",
720 "struct S;",
721 "trait T {}",
722 "trait TA = T;",
723 "type Ty = u8;",
724 "union U { n: u8 }",
725 "use a::b;",
726 ]
727 .iter()
728 .fold(String::new(), |mut out, item| {
729 out.push_str(doc);
730 out.push_str(item);
731 out.push('\n');
732 out
733 })
734 };
735 assert_eq!(
736 canonicalize(&body("/// Doc\n")).text,
737 canonicalize(&body("")).text
738 );
739 }
740
741 #[test]
742 fn doc_comments_on_statements_merge() {
743 let a = canonicalize("/// Doc\nlet x = 1;\n/// Doc\nm!(x);\n");
744 let b = canonicalize("let x = 1;\nm!(x);\n");
745 assert_eq!(a.text, b.text);
746 }
747
748 #[test]
749 fn doc_comments_on_impl_and_trait_items_merge() {
750 let body = |doc: &str| {
751 format!(
752 "trait T {{ {doc}const C: u8; {doc}fn f(&self); {doc}type A; {doc}m!(); }}\n\
753 struct S;\nimpl T for S {{ {doc}const C: u8 = 1; {doc}fn f(&self) {{}} {doc}type A = u8; {doc}m!(); }}\n"
754 )
755 };
756 assert_eq!(
757 canonicalize(&body("#[doc = \"d\"] ")).text,
758 canonicalize(&body("")).text
759 );
760 }
761
762 #[test]
763 fn doc_comment_on_variant_merges() {
764 let a = canonicalize("enum E {\n /// Doc\n A,\n}");
765 let b = canonicalize("enum E { A }");
766 assert_eq!(a.text, b.text);
767 }
768
769 #[test]
770 fn lint_attr_on_fn_merges() {
771 let a = canonicalize("#[allow(unused)]\nfn f() {}");
772 let b = canonicalize("fn f() {}");
773 assert_eq!(a.text, b.text);
774 }
775
776 #[test]
777 fn deny_and_forbid_stay() {
778 let plain = canonicalize("fn f() { let x = 1; }");
779 assert_ne!(
780 canonicalize("#[deny(unused)]\nfn f() { let x = 1; }").text,
781 plain.text
782 );
783 assert_ne!(
784 canonicalize("#[forbid(unused)]\nfn f() { let x = 1; }").text,
785 plain.text
786 );
787 assert_eq!(
788 canonicalize("#[warn(unused)]\nfn f() { let x = 1; }").text,
789 plain.text
790 );
791 }
792
793 #[test]
794 fn lint_attr_on_struct_field_merges() {
795 let a = canonicalize("struct S {\n #[allow(dead_code)]\n x: u8,\n}");
796 let b = canonicalize("struct S { x: u8 }");
797 assert_eq!(a.text, b.text);
798 }
799
800 #[test]
801 fn lint_attr_on_stmt_merges() {
802 let a = canonicalize("#[allow(unused_variables)]\nlet x = 1;");
803 let b = canonicalize("let x = 1;");
804 assert_eq!(a.text, b.text);
805 }
806
807 #[test]
808 fn lint_attr_on_impl_item_merges() {
809 let a = canonicalize(
810 "struct S;\nimpl S {\n #[allow(clippy::unused_self)]\n fn f(&self) {}\n}",
811 );
812 let b = canonicalize("struct S;\nimpl S { fn f(&self) {} }");
813 assert_eq!(a.text, b.text);
814 }
815
816 #[test]
817 fn two_lint_attrs_on_one_item_merges() {
818 let a = canonicalize("#[allow(unused)]\n#[warn(dead_code)]\nfn f() {}");
819 let b = canonicalize("fn f() {}");
820 assert_eq!(a.text, b.text);
821 }
822
823 #[test]
824 fn expect_attr_merges() {
825 let a = canonicalize(
826 r#"#[expect(unused, reason = "demo")]
827 fn f() {}"#,
828 );
829 let b = canonicalize("fn f() {}");
830 assert_eq!(a.text, b.text);
831 }
832
833 #[test]
834 fn cfg_attr_stays_distinct() {
835 let a = canonicalize("#[cfg(unix)]\nfn f() {}");
836 let b = canonicalize("fn f() {}");
837 assert_ne!(a.text, b.text);
838 }
839
840 #[test]
841 fn derive_attr_stays_distinct() {
842 let a = canonicalize("#[derive(Debug)]\nstruct S;");
843 let b = canonicalize("struct S;");
844 assert_ne!(a.text, b.text);
845 }
846
847 #[test]
848 fn repr_attr_stays_distinct() {
849 let a = canonicalize("#[repr(C)]\nstruct S { x: u8 }");
850 let b = canonicalize("struct S { x: u8 }");
851 assert_ne!(a.text, b.text);
852 }
853
854 #[test]
855 fn unknown_attr_stays_distinct() {
856 let a = canonicalize("#[my_attr]\nfn f() {}");
857 let b = canonicalize("fn f() {}");
858 assert_ne!(a.text, b.text);
859 }
860
861 #[test]
862 fn struct_declared_after_use_merges() {
863 let a = canonicalize("let _p = P;\nstruct P;");
864 let b = canonicalize("struct P;\nlet _p = P;");
865 assert_eq!(a.text, b.text);
866 }
867
868 #[test]
869 fn every_hoistable_item_kind_moves() {
870 const ITEMS: &[&str] = &[
871 "const C: u8 = 1;",
872 "enum E { A }",
873 "extern crate foo as bar;",
874 "fn g() {}",
875 "extern \"C\" { fn cf(); }",
876 "impl S {}",
877 "mod md {}",
878 "static ST: u8 = 1;",
879 "struct S;",
880 "trait T {}",
881 "trait TA = T;",
882 "type Ty = u8;",
883 "union U { n: u8 }",
884 "use a::b;",
885 ];
886 for item in ITEMS {
887 let front = canonicalize(&format!("#[allow(unused)]\n{item}\nlet _x = 1;\n"));
888 let back = canonicalize(&format!("let _x = 1;\n#[allow(unused)]\n{item}\n"));
889 assert_eq!(front.text, back.text, "{item}");
890 }
891 }
892
893 #[test]
894 fn an_item_with_a_live_attribute_keeps_its_place() {
895 const ITEMS: &[&str] = &[
896 "const C: u8 = 1;",
897 "enum E { A }",
898 "extern crate foo as bar;",
899 "fn g() {}",
900 "extern \"C\" { fn cf(); }",
901 "impl S {}",
902 "m! {}",
903 "mod md {}",
904 "static ST: u8 = 1;",
905 "struct S;",
906 "trait T {}",
907 "trait TA = T;",
908 "type Ty = u8;",
909 "union U { n: u8 }",
910 ];
911 for item in ITEMS {
912 let front = canonicalize(&format!("#[cfg(unix)]\n{item}\nlet _x = 1;\n"));
913 let back = canonicalize(&format!("let _x = 1;\n#[cfg(unix)]\n{item}\n"));
914 assert_ne!(front.text, back.text, "{item}");
915 }
916 }
917
918 #[test]
919 fn fn_declared_after_call_merges() {
920 let a = canonicalize("f();\nfn f() {}");
921 let b = canonicalize("fn f() {}\nf();");
922 assert_eq!(a.text, b.text);
923 }
924
925 #[test]
926 fn two_items_reversed_merges() {
927 let a = canonicalize("struct B;\nstruct A;");
928 let b = canonicalize("struct A;\nstruct B;");
929 assert_eq!(a.text, b.text);
930 }
931
932 #[test]
933 fn use_and_item_either_order_merges() {
934 let a = canonicalize("use core::fmt;\nstruct S;");
935 let b = canonicalize("struct S;\nuse core::fmt;");
936 assert_eq!(a.text, b.text);
937 }
938
939 #[test]
940 fn macro_rules_not_hoisted_past() {
941 let a = canonicalize("m!();\nmacro_rules! m { () => {} }");
942 let b = canonicalize("macro_rules! m { () => {} }\nm!();");
943 assert_ne!(a.text, b.text);
944 }
945
946 #[test]
947 fn doc_comment_on_struct_field_merges() {
948 let a = canonicalize("struct S {\n /// Field doc.\n x: u8,\n}");
949 let b = canonicalize("struct S {\n x: u8,\n}");
950 assert_eq!(a.text, b.text);
951 }
952
953 #[test]
954 fn inner_doc_in_fn_body_merges() {
955 let a = canonicalize("fn f() -> u8 {\n //! Inner doc.\n 1\n}");
956 let b = canonicalize("fn f() -> u8 {\n 1\n}");
957 assert_eq!(a.text, b.text);
958 }
959
960 #[test]
961 fn use_single_vs_group_merges() {
962 let a = canonicalize("use a::b;\nb();\n");
963 let b = canonicalize("use a::{b};\nb();\n");
964 assert_eq!(a.text, b.text);
965 }
966
967 #[test]
968 fn use_two_lines_vs_tree_merges() {
969 let a = canonicalize("use a::b;\nuse a::c;\nb(); c();\n");
970 let b = canonicalize("use a::{b, c};\nb(); c();\n");
971 assert_eq!(a.text, b.text);
972 }
973
974 #[test]
975 fn use_reversed_order_merges() {
976 let a = canonicalize("use a::c;\nuse a::b;\nc(); b();\n");
977 let b = canonicalize("use a::b;\nuse a::c;\nc(); b();\n");
978 assert_eq!(a.text, b.text);
979 }
980
981 #[test]
982 fn use_glob_in_group_merges() {
983 let a = canonicalize("use a::*;\nfoo();\n");
984 let b = canonicalize("use a::{*};\nfoo();\n");
985 assert_eq!(a.text, b.text);
986 }
987
988 #[test]
989 fn use_self_leaf_is_the_module() {
990 let a = canonicalize("use a::{self};\nfoo();\n");
991 let b = canonicalize("use a;\nfoo();\n");
992 assert_eq!(a.text, b.text);
993 }
994
995 #[test]
996 fn pub_use_stays_distinct() {
997 let a = canonicalize("pub use a::b;\n");
998 let b = canonicalize("use a::b;\n");
999 assert_ne!(a.text, b.text);
1000 }
1001
1002 #[test]
1003 fn different_use_leaf_sets_stay_distinct() {
1004 let a = canonicalize("use a::b;\nuse a::c;\n");
1005 let b = canonicalize("use a::b;\nuse a::d;\n");
1006 assert_ne!(a.text, b.text);
1007 }
1008
1009 #[test]
1010 fn fn_with_different_name_stays_distinct_from_bare() {
1011 let with_run = canonicalize("fn run() {}");
1012 let bare = canonicalize("()");
1013 assert_ne!(with_run.text, bare.text);
1014 }
1015
1016 #[test]
1017 fn paren_expr_folds() {
1018 let a = canonicalize("(1 + 2)");
1019 let b = canonicalize("1 + 2");
1020 assert_eq!(a.text, b.text);
1021 }
1022
1023 #[test]
1024 fn double_paren_folds() {
1025 let a = canonicalize("((1 + 2))");
1026 let b = canonicalize("1 + 2");
1027 assert_eq!(a.text, b.text);
1028 }
1029
1030 #[test]
1031 fn paren_precedence_preserved() {
1032 let a = canonicalize("(1 + 2) * 3");
1033 let b = canonicalize("1 + 2 * 3");
1034 assert_ne!(a.text, b.text);
1035 }
1036
1037 #[test]
1038 fn expr_paren_vs_one_tuple_stays_distinct() {
1039 let a = canonicalize("let _ = (1,);");
1040 let b = canonicalize("let _ = (1);");
1041 assert_ne!(a.text, b.text);
1042 }
1043
1044 #[test]
1045 fn paren_in_pattern_folds() {
1046 let a = canonicalize("let (x) = 1;");
1047 let b = canonicalize("let x = 1;");
1048 assert_eq!(a.text, b.text);
1049 }
1050
1051 #[test]
1052 fn one_tuple_pattern_stays_distinct() {
1053 let a = canonicalize("let (x,) = (1,);");
1054 let b = canonicalize("let (x) = (1,);");
1055 assert_ne!(a.text, b.text);
1056 }
1057
1058 #[test]
1059 fn paren_in_type_folds() {
1060 let a = canonicalize("fn f(x: (u8)) -> (u8) { x }");
1061 let b = canonicalize("fn f(x: u8) -> u8 { x }");
1062 assert_eq!(a.text, b.text);
1063 }
1064
1065 #[test]
1066 fn one_tuple_type_stays_distinct() {
1067 let a = canonicalize("fn f(_: (u8,)) {}");
1068 let b = canonicalize("fn f(_: (u8)) {}");
1069 assert_ne!(a.text, b.text);
1070 }
1071
1072 #[test]
1073 fn closure_body_block_unwraps() {
1074 let a = canonicalize("|x| { x + 1 }");
1075 let b = canonicalize("|x| x + 1");
1076 assert_eq!(a.text, b.text);
1077 }
1078
1079 #[test]
1080 fn nested_closure_block_unwraps() {
1081 let a = canonicalize("|| { || { 1 + 2 } }");
1082 let b = canonicalize("|| || 1 + 2");
1083 assert_eq!(a.text, b.text);
1084 }
1085
1086 #[test]
1087 fn closure_two_stmts_block_stays() {
1088 let a = canonicalize("|x| { let y = x; y }");
1089 let b = canonicalize("|x| x");
1090 assert_ne!(a.text, b.text);
1091 }
1092
1093 #[test]
1094 fn return_tail_folds() {
1095 let a = canonicalize("fn f() { return 1; }");
1096 let b = canonicalize("fn f() { 1 }");
1097 assert_eq!(a.text, b.text);
1098 }
1099
1100 #[test]
1101 fn return_tail_in_closure_block_folds() {
1102 let a = canonicalize("|x| { return x + 1; }");
1103 let b = canonicalize("|x| x + 1");
1104 assert_eq!(a.text, b.text);
1105 }
1106
1107 #[test]
1108 fn early_return_stays() {
1109 let a = canonicalize("fn f() { return 1; g(); }");
1110 let b = canonicalize("fn f() { 1; g(); }");
1111 assert_ne!(a.text, b.text);
1112 }
1113
1114 #[test]
1115 fn a_trailing_unit_return_folds_away() {
1116 let plain = canonicalize("fn f() { g(); }");
1117 assert_eq!(canonicalize("fn f() { g(); return; }").text, plain.text);
1118 assert_eq!(canonicalize("fn f() { g(); return }").text, plain.text);
1119 assert_eq!(
1120 canonicalize("fn f() { return; }").text,
1121 canonicalize("fn f() {}").text
1122 );
1123 assert_eq!(
1124 canonicalize("fn f(c: bool) { if c { g(); return; } else { h(); } }").text,
1125 canonicalize("fn f(c: bool) { if c { g(); } else { h(); } }").text
1126 );
1127 }
1128
1129 #[test]
1130 fn an_attributed_or_early_unit_return_stays() {
1131 let plain = canonicalize("fn f() { g(); }");
1132 assert_ne!(
1133 canonicalize("fn f() { g(); #[cfg(unix)] return; }").text,
1134 plain.text
1135 );
1136 assert_ne!(canonicalize("fn f() { return; g(); }").text, plain.text);
1137 }
1138
1139 #[test]
1140 fn a_return_arm_of_a_tail_match_folds() {
1141 let plain = canonicalize("fn f(v: u8) -> u8 { match v { 1 => 2, _ => 0 } }");
1142 for arm in ["1 => return 2,", "1 => { return 2 }"] {
1143 let source = format!("fn f(v: u8) -> u8 {{ match v {{ {arm} _ => 0 }} }}");
1144 assert_eq!(canonicalize(&source).text, plain.text, "{arm}");
1145 }
1146 }
1147
1148 #[test]
1149 fn an_attributed_return_arm_stays() {
1150 let plain = canonicalize("fn f(v: u8) -> u8 { match v { 1 => 2, _ => 0 } }");
1151 let attributed =
1152 canonicalize("fn f(v: u8) -> u8 { match v { 1 => { #[cfg(unix)] return 2 } _ => 0 } }");
1153 assert_ne!(attributed.text, plain.text);
1154 }
1155
1156 #[test]
1157 fn an_else_block_holding_only_an_if_collapses() {
1158 assert_eq!(
1159 canonicalize("let x = if a { 1 } else { if b { 2 } else { 3 } };").text,
1160 canonicalize("let x = if a { 1 } else if b { 2 } else { 3 };").text
1161 );
1162 assert_eq!(
1163 canonicalize("if a { f(); } else { if b { g(); } }").text,
1164 canonicalize("if a { f(); } else if b { g(); }").text
1165 );
1166 }
1167
1168 #[test]
1169 fn an_attributed_if_in_an_else_block_stays() {
1170 assert_ne!(
1171 canonicalize("if a { f(); } else { #[cfg(unix)] if b { g(); } }").text,
1172 canonicalize("if a { f(); } else if b { g(); }").text
1173 );
1174 }
1175
1176 #[test]
1177 fn rustfmt_attributes_are_inert() {
1178 let plain = canonicalize("fn f() {}");
1179 assert_eq!(canonicalize("#[rustfmt::skip]\nfn f() {}").text, plain.text);
1180 assert_eq!(
1181 canonicalize("#[rustfmt::skip::macros(vec)]\nfn f() {}").text,
1182 plain.text
1183 );
1184 }
1185
1186 #[test]
1187 fn other_tool_or_lookalike_attributes_stay() {
1188 let plain = canonicalize("async fn f() {}");
1189 for attr in ["#[rustfmt_skip]", "#[tokio::main]"] {
1190 let source = format!("{attr}\nasync fn f() {{}}");
1191 assert_ne!(canonicalize(&source).text, plain.text, "{attr}");
1192 }
1193 }
1194
1195 #[test]
1196 fn inline_bounds_merge_with_their_where_form() {
1197 let pairs = [
1198 (
1199 "fn f<T: Clone>(t: T) -> T { t.clone() }",
1200 "fn f<T>(t: T) -> T where T: Clone { t.clone() }",
1201 ),
1202 (
1203 "trait Tr {}\nstruct S<T>(T);\nimpl<T: Clone> Tr for S<T> {}",
1204 "trait Tr {}\nstruct S<T>(T);\nimpl<T> Tr for S<T> where T: Clone {}",
1205 ),
1206 ("struct S<T: Clone>(T);", "struct S<T>(T) where T: Clone;"),
1207 (
1208 "fn f<'a, 'b: 'a>(x: &'a u8, y: &'b u8) {}",
1209 "fn f<'a, 'b>(x: &'a u8, y: &'b u8) where 'b: 'a {}",
1210 ),
1211 (
1212 "fn f<T: Clone>(t: T) where T: Copy {}",
1213 "fn f<T>(t: T) where T: Clone, T: Copy {}",
1214 ),
1215 ];
1216 for (inline, clause) in pairs {
1217 assert_eq!(
1218 canonicalize(inline).text,
1219 canonicalize(clause).text,
1220 "{inline}"
1221 );
1222 }
1223 }
1224
1225 #[test]
1226 fn an_impl_trait_argument_stays_apart_from_a_generic_parameter() {
1227 assert_ne!(
1228 canonicalize("fn f(x: impl Clone) {}").text,
1229 canonicalize("fn f<T: Clone>(x: T) {}").text
1230 );
1231 }
1232
1233 #[test]
1234 fn elidable_lifetimes_merge_with_their_elided_form() {
1235 let pairs = [
1236 ("const S: &'static str = \"a\";", "const S: &str = \"a\";"),
1237 (
1238 "static S: &'static [&'static str] = &[];",
1239 "static S: &[&str] = &[];",
1240 ),
1241 (
1242 "const F: &'static dyn Fn(&'static str) = &g;",
1243 "const F: &dyn Fn(&'static str) = &g;",
1244 ),
1245 (
1246 "fn f(x: &'_ str) -> usize { x.len() }",
1247 "fn f(x: &str) -> usize { x.len() }",
1248 ),
1249 ("fn f(x: Wrap<'_>) {}", "fn f(x: Wrap) {}"),
1250 ("fn f(x: Wrap<'_, u8>) {}", "fn f(x: Wrap<u8>) {}"),
1251 ];
1252 for (written, elided) in pairs {
1253 assert_eq!(
1254 canonicalize(written).text,
1255 canonicalize(elided).text,
1256 "{written}"
1257 );
1258 }
1259 }
1260
1261 #[test]
1262 fn lifetimes_whose_elision_means_something_else_stay() {
1263 let pairs = [
1264 ("const F: fn(&'static str) = g;", "const F: fn(&str) = g;"),
1266 (
1267 "const F: &dyn Fn(&'static str) = &g;",
1268 "const F: &dyn Fn(&str) = &g;",
1269 ),
1270 (
1272 "trait Tr {}\nimpl Tr for Wrap<'_> {}",
1273 "trait Tr {}\nimpl Tr for Wrap {}",
1274 ),
1275 ("fn f<'a>(x: &'a str) {}", "fn f(x: &str) {}"),
1276 ];
1277 for (written, other) in pairs {
1278 assert_ne!(
1279 canonicalize(written).text,
1280 canonicalize(other).text,
1281 "{written}"
1282 );
1283 }
1284 }
1285
1286 #[test]
1287 fn std_derives_merge_in_any_order_or_split() {
1288 let pairs = [
1289 (
1290 "#[derive(Debug, Clone)]\nstruct S;",
1291 "#[derive(Clone, Debug)]\nstruct S;",
1292 ),
1293 (
1294 "#[derive(Debug)]\n#[derive(Clone)]\nstruct S;",
1295 "#[derive(Clone, Debug)]\nstruct S;",
1296 ),
1297 (
1298 "#[derive(Debug)]\n#[repr(C)]\n#[derive(Clone)]\nstruct S;",
1299 "#[derive(Clone, Debug)]\n#[repr(C)]\nstruct S;",
1300 ),
1301 (
1302 "#[derive(PartialEq, Eq, Debug)]\nenum E { A }",
1303 "#[derive(Debug, Eq, PartialEq)]\nenum E { A }",
1304 ),
1305 ];
1306 for (written, sorted) in pairs {
1307 assert_eq!(
1308 canonicalize(written).text,
1309 canonicalize(sorted).text,
1310 "{written}"
1311 );
1312 }
1313 }
1314
1315 #[test]
1316 fn a_derive_list_with_a_proc_macro_keeps_its_order() {
1317 assert_ne!(
1318 canonicalize("#[derive(Serialize, Debug)]\nstruct S;").text,
1319 canonicalize("#[derive(Debug, Serialize)]\nstruct S;").text
1320 );
1321 }
1322
1323 #[test]
1324 fn a_leading_colon_on_an_unbound_crate_path_folds() {
1325 let pairs = [
1326 (
1327 "let v = ::std::vec::Vec::<u8>::new();",
1328 "let v = std::vec::Vec::<u8>::new();",
1329 ),
1330 (
1331 "let v: ::std::vec::Vec<u8> = Vec::new();",
1332 "let v: std::vec::Vec<u8> = Vec::new();",
1333 ),
1334 (
1335 "use ::std::fmt;\nlet _ = fmt::Error;",
1336 "use std::fmt;\nlet _ = fmt::Error;",
1337 ),
1338 ("::std::println!(\"x\");", "std::println!(\"x\");"),
1339 ];
1340 for (rooted, plain) in pairs {
1341 assert_eq!(
1342 canonicalize(rooted).text,
1343 canonicalize(plain).text,
1344 "{rooted}"
1345 );
1346 }
1347 }
1348
1349 #[test]
1350 fn a_leading_colon_past_a_local_item_of_that_name_stays() {
1351 assert_ne!(
1352 canonicalize("mod std { pub fn f() {} }\n::std::mem::drop(1);").text,
1353 canonicalize("mod std { pub fn f() {} }\nstd::mem::drop(1);").text
1354 );
1355 }
1356
1357 #[test]
1358 fn unit_return_type_folds_fn() {
1359 let a = canonicalize("fn f() -> () {}");
1360 let b = canonicalize("fn f() {}");
1361 assert_eq!(a.text, b.text);
1362 }
1363
1364 #[test]
1365 fn closure_unit_return_type_stays() {
1366 let a = canonicalize("|| -> () { 1 }");
1367 let b = canonicalize("|| 1");
1368 assert_ne!(a.text, b.text);
1369 }
1370
1371 #[test]
1372 fn non_unit_return_type_stays() {
1373 let a = canonicalize("fn f() -> u8 { 1 }");
1374 let b = canonicalize("fn f() { 1 }");
1375 assert_ne!(a.text, b.text);
1376 }
1377
1378 #[test]
1379 fn double_semicolon_at_end_folds() {
1380 let a = canonicalize("fn f() { g();; }");
1381 let b = canonicalize("fn f() { g(); }");
1382 assert_eq!(a.text, b.text);
1383 }
1384
1385 #[test]
1386 fn double_semicolon_in_middle_folds() {
1387 let a = canonicalize("fn f() { g();; h(); }");
1388 let b = canonicalize("fn f() { g(); h(); }");
1389 assert_eq!(a.text, b.text);
1390 }
1391
1392 #[test]
1393 fn alpha_renames_let_bindings() {
1394 let a = canonicalize("let pino = 1;\npino + 1\n");
1395 let b = canonicalize("let abete = 1;\nabete + 1\n");
1396 assert_eq!(a.text, b.text);
1397 }
1398
1399 #[test]
1400 fn alpha_renames_struct_shorthand_bindings() {
1401 let a = canonicalize("let P { pino } = make();\npino\n");
1402 let b = canonicalize("let P { pino: abete } = make();\nabete\n");
1403 assert_eq!(a.text, b.text);
1404 }
1405
1406 #[test]
1407 fn alpha_renames_bindings_inside_an_at_subpattern() {
1408 let a = canonicalize("match Some(1) { pino @ Some(abete) => abete, None => 0 }");
1409 let b = canonicalize("match Some(1) { x @ Some(y) => y, None => 0 }");
1410 assert_eq!(a.text, b.text);
1411 }
1412
1413 #[test]
1414 fn alpha_renames_closure_params() {
1415 let a = canonicalize("let f = |pino| pino + 1;\nf(2);\n");
1416 let b = canonicalize("let f = |abete| abete + 1;\nf(2);\n");
1417 assert_eq!(a.text, b.text);
1418 }
1419
1420 #[test]
1421 fn alpha_free_idents_stay_distinct() {
1422 let a = canonicalize("foo();\n");
1423 let b = canonicalize("bar();\n");
1424 assert_ne!(a.text, b.text);
1425 }
1426
1427 #[test]
1428 fn alpha_shadowing_resolves_to_nearest_binder() {
1429 let a = canonicalize("let x = 1;\n{ let x = 3; x }\nx\n");
1430 let b = canonicalize("let y = 1;\n{ let y = 3; y }\ny\n");
1431 assert_eq!(a.text, b.text);
1432 assert!(a.text.contains("_canon_0"));
1435 assert!(a.text.contains("_canon_1"));
1436 let c = canonicalize("let y = 2;\n{ let y = 1; y }\ny\n");
1437 assert_ne!(a.text, c.text);
1438 }
1439
1440 #[test]
1441 fn alpha_fn_forward_ref_collapses() {
1442 let a = canonicalize("fn main() { helper(); }\nfn helper() {}\n");
1443 let b = canonicalize("fn main() { aux(); }\nfn aux() {}\n");
1444 assert_eq!(a.text, b.text);
1445 }
1446
1447 #[test]
1448 fn alpha_struct_forward_ref_collapses() {
1449 let a = canonicalize("fn main() { drop(Pino { n: 1 }); }\nstruct Pino { n: u8 }\n");
1450 let b = canonicalize("fn main() { drop(Abete { n: 1 }); }\nstruct Abete { n: u8 }\n");
1451 assert_eq!(a.text, b.text);
1452 }
1453
1454 #[test]
1455 fn alpha_impl_constructor_forward_ref_collapses() {
1456 let a = canonicalize(
1457 "fn main() { let _ = Pino::new(); }\nstruct Pino { n: u8 }\nimpl Pino { fn new() -> Self { Pino { n: 0 } } }\n",
1458 );
1459 let b = canonicalize(
1460 "fn main() { let _ = Abete::new(); }\nstruct Abete { n: u8 }\nimpl Abete { fn new() -> Self { Abete { n: 0 } } }\n",
1461 );
1462 assert_eq!(a.text, b.text);
1463 }
1464
1465 #[test]
1466 fn alpha_const_forward_ref_collapses() {
1467 let a = canonicalize("fn main() { let _ = FOO; }\nconst FOO: u8 = 1;\n");
1468 let b = canonicalize("fn main() { let _ = BAR; }\nconst BAR: u8 = 1;\n");
1469 assert_eq!(a.text, b.text);
1470 }
1471
1472 #[test]
1473 fn alpha_every_item_kind_forward_ref_collapses() {
1474 let body = |s: &str, e: &str, u: &str, t: &str, tr: &str| {
1475 format!(
1476 "fn main() {{ let _ = {s}; let _ = {e}::A; let _ = {u} {{ n: 1 }}; let _: {t} = 1; }}\n\
1477 fn g<X: {tr}>() {{}}\n\
1478 static {s}: u8 = 1;\nenum {e} {{ A }}\nunion {u} {{ n: u8 }}\ntype {t} = u8;\ntrait {tr} {{}}\n"
1479 )
1480 };
1481 let a = canonicalize(&body("S1", "E1", "U1", "T1", "R1"));
1482 let b = canonicalize(&body("S2", "E2", "U2", "T2", "R2"));
1483 assert_eq!(a.text, b.text);
1484 }
1485
1486 #[test]
1487 fn alpha_nested_fn_forward_ref_collapses() {
1488 let a = canonicalize("fn main() { helper(); fn helper() {} }\n");
1489 let b = canonicalize("fn main() { aux(); fn aux() {} }\n");
1490 assert_eq!(a.text, b.text);
1491 }
1492
1493 #[test]
1494 fn alpha_mutual_recursion_collapses() {
1495 let a = canonicalize(
1496 "fn even(n: u32) -> bool { if n == 0 { true } else { odd(n - 1) } }\nfn odd(n: u32) -> bool { if n == 0 { false } else { even(n - 1) } }\n",
1497 );
1498 let b = canonicalize(
1499 "fn par(n: u32) -> bool { if n == 0 { true } else { impar(n - 1) } }\nfn impar(n: u32) -> bool { if n == 0 { false } else { par(n - 1) } }\n",
1500 );
1501 assert_eq!(a.text, b.text);
1502 }
1503
1504 #[test]
1505 fn alpha_forward_ref_free_name_stays_distinct() {
1506 let a = canonicalize("fn main() { foreign(); }\n");
1507 let b = canonicalize("fn main() { other_fn(); }\n");
1508 assert_ne!(a.text, b.text);
1509 }
1510
1511 #[test]
1512 fn alpha_inline_mod_items_renamed() {
1513 let a = canonicalize("mod m { pub fn f() {} }\nfn main() { m::f(); }\n");
1514 let b = canonicalize("mod n { pub fn g() {} }\nfn main() { n::g(); }\n");
1515 assert_eq!(a.text, b.text);
1516 }
1517
1518 #[test]
1519 fn alpha_module_used_above_its_definition() {
1520 let a = canonicalize("fn main() { m::f(); }\nmod m { pub fn f() {} }\n");
1521 let b = canonicalize("fn main() { n::g(); }\nmod n { pub fn g() {} }\n");
1522 assert_eq!(a.text, b.text);
1523 }
1524
1525 #[test]
1526 fn alpha_nested_module_used_above_its_definition() {
1527 let a = canonicalize(
1528 "fn main() { m::inner::f(); }\nmod m { pub mod inner { pub fn f() {} } }\n",
1529 );
1530 let b =
1531 canonicalize("fn main() { n::deep::g(); }\nmod n { pub mod deep { pub fn g() {} } }\n");
1532 assert_eq!(a.text, b.text);
1533 }
1534
1535 #[test]
1536 fn alpha_inline_mod_partial_call_collapses() {
1537 let a = canonicalize("mod m { pub fn f() {} pub fn h() {} }\nfn main() { m::f(); }\n");
1538 let b = canonicalize("mod n { pub fn g() {} pub fn k() {} }\nfn main() { n::g(); }\n");
1539 assert_eq!(a.text, b.text);
1540 }
1541
1542 #[test]
1543 fn alpha_inline_mod_distinct_structures_differ() {
1544 let a = canonicalize(
1545 "mod m { pub fn f() {} }\nmod n { pub fn g() {} }\nfn main() { m::f(); n::g(); }\n",
1546 );
1547 let b =
1548 canonicalize("mod r { pub fn p() {} pub fn q() {} }\nfn main() { r::p(); r::q(); }\n");
1549 assert_ne!(a.text, b.text);
1550 }
1551
1552 #[test]
1553 fn alpha_renames_generics_and_lifetimes() {
1554 let a = canonicalize("fn f<'a, T>(x: &'a T) -> T { x.clone() }\n");
1555 let b = canonicalize("fn g<'b, U>(y: &'b U) -> U { y.clone() }\n");
1556 assert_eq!(a.text, b.text);
1557 }
1558
1559 #[test]
1560 fn alpha_renames_closure_higher_ranked_lifetimes() {
1561 let a = canonicalize("let f = for<'a> |x: &'a i32| x;");
1562 let b = canonicalize("let f = for<'b> |x: &'b i32| x;");
1563 assert_eq!(a.text, b.text);
1564 let c = canonicalize("let f = for<'a> |x: &'static i32| x;");
1565 assert_ne!(a.text, c.text);
1566 }
1567
1568 #[test]
1569 fn alpha_where_clause_two_params_lifetime_bound() {
1570 let a = canonicalize(
1571 "fn f<'a, T, U>(x: &'a T, y: U) -> T where T: Clone, U: Into<T> + 'a { x.clone() }\n",
1572 );
1573 let b = canonicalize(
1574 "fn g<'b, V, W>(p: &'b V, q: W) -> V where V: Clone, W: Into<V> + 'b { p.clone() }\n",
1575 );
1576 assert_eq!(a.text, b.text);
1577 }
1578
1579 #[test]
1580 fn alpha_where_clause_on_impl_block() {
1581 let a = canonicalize(
1582 "struct Pino;\nimpl<T: Clone> Pino where T: Default { fn f(&self, _: T) {} }\n",
1583 );
1584 let b = canonicalize(
1585 "struct Abete;\nimpl<U: Clone> Abete where U: Default { fn f(&self, _: U) {} }\n",
1586 );
1587 assert_eq!(a.text, b.text);
1588 }
1589
1590 #[test]
1591 fn alpha_where_clause_on_struct() {
1592 let a = canonicalize("struct Wrap<T>(T) where T: Clone;\n");
1593 let b = canonicalize("struct Pack<U>(U) where U: Clone;\n");
1594 assert_eq!(a.text, b.text);
1595 }
1596
1597 #[test]
1598 fn alpha_where_free_trait_stays_distinct() {
1599 let a = canonicalize("fn f<T>() where T: Clone {}\n");
1600 let b = canonicalize("fn f<T>() where T: Default {}\n");
1601 assert_ne!(a.text, b.text);
1602 }
1603
1604 #[test]
1605 fn alpha_impl_local_trait_collapses() {
1606 let a = canonicalize("trait Pino {}\nimpl Pino for u8 {}\n");
1607 let b = canonicalize("trait Abete {}\nimpl Abete for u8 {}\n");
1608 assert_eq!(a.text, b.text);
1609 }
1610
1611 #[test]
1612 fn alpha_impl_local_trait_with_generic() {
1613 let a = canonicalize("trait Pino<T> {}\nstruct S;\nimpl<T> Pino<T> for S {}\n");
1614 let b = canonicalize("trait Abete<U> {}\nstruct S;\nimpl<U> Abete<U> for S {}\n");
1615 assert_eq!(a.text, b.text);
1616 }
1617
1618 #[test]
1619 fn alpha_closure_typed_two_params_return_type() {
1620 let a = canonicalize("struct Pino;\nlet f = |x: Pino, y: Pino| -> Pino { x };\n");
1621 let b = canonicalize("struct Abete;\nlet f = |p: Abete, q: Abete| -> Abete { p };\n");
1622 assert_eq!(a.text, b.text);
1623 }
1624
1625 #[test]
1626 fn alpha_let_chain_three_lets_bool_middle() {
1627 let a = canonicalize(concat!(
1628 "if let Some(pino) = a() && cond",
1629 " && let Some(qno) = b() && let Some(rno) = c()",
1630 " { use_it(pino, qno, rno) }\n",
1631 ));
1632 let b = canonicalize(concat!(
1633 "if let Some(abete) = a() && cond",
1634 " && let Some(ebano) = b() && let Some(faggio) = c()",
1635 " { use_it(abete, ebano, faggio) }\n",
1636 ));
1637 assert_eq!(a.text, b.text);
1638 }
1639
1640 #[test]
1641 fn alpha_let_chain_shadow_outer_else_uses_outer() {
1642 let a = canonicalize(
1643 "let pino = 0;\nif let Some(pino) = get() { use_it(pino) } else { pino }\n",
1644 );
1645 let b = canonicalize(
1646 "let abete = 0;\nif let Some(abete) = get() { use_it(abete) } else { abete }\n",
1647 );
1648 assert_eq!(a.text, b.text);
1649 }
1650
1651 #[test]
1652 fn alpha_nested_labelled_loops_inner_break_outer() {
1653 let a = canonicalize("'outer: loop { 'inner: loop { break 'outer; } break 'outer; }\n");
1654 let b = canonicalize("'x: loop { 'y: loop { break 'x; } break 'x; }\n");
1655 assert_eq!(a.text, b.text);
1656 }
1657
1658 #[test]
1659 fn alpha_labelled_block_expression() {
1660 let a = canonicalize("let v = 'pino: { break 'pino 1; };\n");
1661 let b = canonicalize("let v = 'abete: { break 'abete 1; };\n");
1662 assert_eq!(a.text, b.text);
1663 }
1664
1665 #[test]
1666 fn alpha_unlabelled_break_stays_distinct() {
1667 let a = canonicalize("loop { break; }\n");
1668 let b = canonicalize("'x: loop { break 'x; }\n");
1669 assert_ne!(a.text, b.text);
1670 }
1671
1672 #[test]
1673 fn alpha_struct_expr_shorthand_one_local_one_free() {
1674 let a = canonicalize("let pino = 1;\nP { pino, free_field }\n");
1675 let b = canonicalize("let abete = 1;\nP { pino: abete, free_field }\n");
1676 assert_eq!(a.text, b.text);
1677 }
1678
1679 #[test]
1680 fn alpha_struct_expr_shorthand_nested() {
1681 let a = canonicalize("let pino = 1;\nlet qno = P { pino };\nOuter { inner: qno }\n");
1682 let b = canonicalize(
1683 "let abete = 1;\nlet ebano = P { pino: abete };\nOuter { inner: ebano }\n",
1684 );
1685 assert_eq!(a.text, b.text);
1686 }
1687
1688 #[test]
1689 fn alpha_struct_expr_shorthand_free_stays_distinct() {
1690 let a = canonicalize("P { pino }\n");
1691 let b = canonicalize("P { abete }\n");
1692 assert_ne!(a.text, b.text);
1693 }
1694
1695 #[test]
1696 fn alpha_self_alias_generic_impl() {
1697 let a = canonicalize(
1698 "struct Pino<T>(T);\nimpl<T> Pino<T> { fn n(t: T) -> Self { Self(t) } }\n",
1699 );
1700 let b = canonicalize(
1701 "struct Abete<T>(T);\nimpl<T> Abete<T> { fn n(t: T) -> Abete<T> { Abete(t) } }\n",
1702 );
1703 assert_eq!(a.text, b.text);
1704 }
1705
1706 #[test]
1707 fn alpha_renames_local_types_and_self() {
1708 let a = canonicalize(
1709 "struct Pino { n: u8 }\nimpl Pino {\n fn take(&self) -> u8 { self.n }\n}\n",
1710 );
1711 let b = canonicalize(
1712 "struct Abete { n: u8 }\nimpl Abete {\n fn take(&self) -> u8 { self.n }\n}\n",
1713 );
1714 assert_eq!(a.text, b.text);
1715 }
1716
1717 #[test]
1718 fn alpha_field_names_stay_distinct() {
1719 let a = canonicalize("struct P { pino: u8 }\n");
1720 let b = canonicalize("struct P { abete: u8 }\n");
1721 assert_ne!(a.text, b.text);
1722 }
1723
1724 #[test]
1725 fn alpha_renames_macro_token_uses() {
1726 let a = canonicalize("let pino = 1;\nvec![pino]\n");
1727 let b = canonicalize("let abete = 1;\nvec![abete]\n");
1728 assert_eq!(a.text, b.text);
1729 }
1730
1731 #[test]
1732 fn alpha_renames_use_aliases() {
1733 let a = canonicalize("use std::collections::BTreeMap as Pino;\nPino::new();\n");
1734 let b = canonicalize("use std::collections::BTreeMap as Abete;\nAbete::new();\n");
1735 assert_eq!(a.text, b.text);
1736 }
1737
1738 #[test]
1739 fn alpha_use_of_different_items_stays_distinct() {
1740 let a = canonicalize("use pkg::pino;\npino();\n");
1741 let b = canonicalize("use pkg::abete;\nabete();\n");
1742 assert_ne!(a.text, b.text);
1743 }
1744
1745 #[test]
1746 fn alpha_use_name_equals_its_aliased_form() {
1747 let a = canonicalize("use pkg::{pino, sub::*};\npino();\n");
1748 let b = canonicalize("use pkg::{pino as abete, sub::*};\nabete();\n");
1749 assert_eq!(a.text, b.text);
1750 }
1751
1752 #[test]
1753 fn alpha_use_group_keeps_each_item() {
1754 let a = canonicalize("use pkg::{pino, qno};\npino(qno);\n");
1755 let b = canonicalize("use pkg::{pino, ebano};\npino(ebano);\n");
1756 assert_ne!(a.text, b.text);
1757 }
1758
1759 #[test]
1760 fn alpha_renames_tuple_pattern() {
1761 let a = canonicalize("let (pino, qno) = pair;\npino + qno\n");
1762 let b = canonicalize("let (abete, ebano) = pair;\nabete + ebano\n");
1763 assert_eq!(a.text, b.text);
1764 }
1765
1766 #[test]
1767 fn alpha_renames_slice_pattern() {
1768 let a = canonicalize("let [pino, qno] = items;\npino + qno\n");
1769 let b = canonicalize("let [abete, ebano] = items;\nabete + ebano\n");
1770 assert_eq!(a.text, b.text);
1771 }
1772
1773 #[test]
1774 fn alpha_renames_tuple_struct_pattern() {
1775 let a = canonicalize("let Pair(pino, qno) = make();\npino + qno\n");
1776 let b = canonicalize("let Pair(abete, ebano) = make();\nabete + ebano\n");
1777 assert_eq!(a.text, b.text);
1778 }
1779
1780 #[test]
1781 fn alpha_renames_or_pattern() {
1782 let a = canonicalize("match it {\n pino | qno => pino + qno,\n _ => 0,\n}\n");
1783 let b = canonicalize("match it {\n abete | ebano => abete + ebano,\n _ => 0,\n}\n");
1784 assert_eq!(a.text, b.text);
1785 }
1786
1787 #[test]
1788 fn alpha_renames_reference_pattern() {
1789 let a = canonicalize("let &pino = cell;\npino\n");
1790 let b = canonicalize("let &abete = cell;\nabete\n");
1791 assert_eq!(a.text, b.text);
1792 }
1793
1794 #[test]
1795 fn alpha_renames_paren_pattern() {
1796 let a = canonicalize("let (pino) = value;\npino\n");
1797 let b = canonicalize("let (abete) = value;\nabete\n");
1798 assert_eq!(a.text, b.text);
1799 }
1800
1801 #[test]
1802 fn alpha_renames_type_pattern() {
1803 let a = canonicalize("let pino: u8 = 1;\npino\n");
1804 let b = canonicalize("let abete: u8 = 1;\nabete\n");
1805 assert_eq!(a.text, b.text);
1806 }
1807
1808 #[test]
1809 fn alpha_renames_guard_pattern() {
1810 let a = canonicalize("match it {\n pino if pino > 0 => pino,\n _ => 0,\n}\n");
1811 let b = canonicalize("match it {\n abete if abete > 0 => abete,\n _ => 0,\n}\n");
1812 assert_eq!(a.text, b.text);
1813 }
1814
1815 #[test]
1816 fn alpha_scope_end_releases_inner_bindings() {
1817 let a = canonicalize("let pino = 1;\n{ let pino = 2; pino }\npino\n");
1818 assert_eq!(a.text.matches("_canon_1").count(), 2);
1819 assert_eq!(a.text.matches("_canon_2").count(), 2);
1820 }
1821
1822 #[test]
1823 fn alpha_macro_token_content_preserved() {
1824 let a = canonicalize("let pino = 1;\nvec![pino]\n");
1825 let b = canonicalize("let pino = 1;\nvec![pino + 1]\n");
1826 assert_ne!(a.text, b.text);
1827 }
1828
1829 #[test]
1830 fn alpha_closure_in_macro_arg_renamed() {
1831 let a = canonicalize("assert!(v.iter().map(|x| x > 0).any(|x| x))\n");
1832 let b = canonicalize("assert!(v.iter().map(|y| y > 0).any(|y| y))\n");
1833 assert_eq!(a.text, b.text);
1834 }
1835
1836 #[test]
1837 fn alpha_match_binder_in_macro_arg_renamed() {
1838 let a = canonicalize("assert!(match v { x => x > 0 })\n");
1839 let b = canonicalize("assert!(match v { y => y > 0 })\n");
1840 assert_eq!(a.text, b.text);
1841 }
1842
1843 #[test]
1844 fn alpha_block_let_in_macro_arg_renamed() {
1845 let a = canonicalize("vec![{ let x = 1; x + 1 }]\n");
1846 let b = canonicalize("vec![{ let y = 1; y + 1 }]\n");
1847 assert_eq!(a.text, b.text);
1848 }
1849
1850 #[test]
1851 fn alpha_nested_local_macro_in_macro_arg_renamed() {
1852 let a = canonicalize("macro_rules! pino { () => { 1 } }\nassert_eq!(pino!(), 1)\n");
1853 let b = canonicalize("macro_rules! abete { () => { 1 } }\nassert_eq!(abete!(), 1)\n");
1854 assert_eq!(a.text, b.text);
1855 }
1856
1857 #[test]
1858 fn alpha_vec_repeat_with_bound_count() {
1859 let a = canonicalize("let n = 5;\nvec![0; n]\n");
1860 let b = canonicalize("let m = 5;\nvec![0; m]\n");
1861 assert_eq!(a.text, b.text);
1862 }
1863
1864 #[test]
1865 fn a_binder_inside_the_repeat_element_is_renamed() {
1866 let a = canonicalize("vec![{ let x = 1; x }; 2]\n");
1867 let b = canonicalize("vec![{ let y = 1; y }; 2]\n");
1868 assert_eq!(a.text, b.text);
1869 }
1870
1871 #[test]
1872 fn a_longer_semicolon_list_keeps_the_token_walk() {
1873 let a = canonicalize("m!({ let x = 1; x }; 1; 2)\n");
1874 let b = canonicalize("m!({ let y = 1; y }; 1; 2)\n");
1875 assert_ne!(a.text, b.text);
1876 let c = canonicalize("m!({ let x = 1; x }; 1;)\n");
1877 let d = canonicalize("m!({ let y = 1; y }; 1;)\n");
1878 assert_ne!(c.text, d.text);
1879 }
1880
1881 #[test]
1882 fn alpha_field_access_not_renamed_for_unrelated_local() {
1883 let a = canonicalize("let x = 1;\nprintln!(\"{}\", s.x)\n");
1884 let b = canonicalize("let y = 1;\nprintln!(\"{}\", s.x)\n");
1885 assert_eq!(a.text, b.text);
1886 }
1887
1888 #[test]
1889 fn alpha_inline_format_arg_debug_renamed() {
1890 let a = canonicalize("let x = v;\nprintln!(\"{x:?}\")\n");
1891 let b = canonicalize("let y = v;\nprintln!(\"{y:?}\")\n");
1892 assert_eq!(a.text, b.text);
1893 }
1894
1895 #[test]
1896 fn alpha_escaped_braces_in_format_not_renamed() {
1897 let a = canonicalize("let x = 1;\nprintln!(\"{{x}}\")\n");
1898 let b = canonicalize("let y = 1;\nprintln!(\"{{y}}\")\n");
1899 assert_ne!(a.text, b.text);
1900 }
1901
1902 #[test]
1903 fn a_placeholder_between_escapes_is_renamed() {
1904 let a = canonicalize("let x = 1;\nprintln!(\"{{{x}}}\")\n");
1905 let b = canonicalize("let y = 1;\nprintln!(\"{{{y}}}\")\n");
1906 assert_eq!(a.text, b.text);
1907 }
1908
1909 #[test]
1910 fn alpha_format_width_param_renamed() {
1911 let a = canonicalize("let x = 1;\nlet w = 5;\nprintln!(\"{x:>w$}\")\n");
1912 let b = canonicalize("let y = 1;\nlet z = 5;\nprintln!(\"{y:>z$}\")\n");
1913 assert_eq!(a.text, b.text);
1914 }
1915
1916 #[test]
1917 fn a_multibyte_char_before_a_width_name_keeps_the_rename() {
1918 let a = canonicalize("let w = 5;\nprintln!(\"{:€w$}\", 1)\n");
1920 let b = canonicalize("let z = 5;\nprintln!(\"{:€z$}\", 1)\n");
1921 assert_eq!(a.text, b.text);
1922 }
1923
1924 #[test]
1925 fn alpha_positional_and_inline_format_args() {
1926 let a = canonicalize("let x = 1;\nprintln!(\"{} {x}\", x)\n");
1927 let b = canonicalize("let y = 1;\nprintln!(\"{} {y}\", y)\n");
1928 assert_eq!(a.text, b.text);
1929 }
1930
1931 #[test]
1932 fn alpha_macro_name_in_fallback_path_renamed() {
1933 let a = canonicalize("macro_rules! pino { () => { 1 } }\nouter!(pino! if foo);\n");
1934 let b = canonicalize("macro_rules! abete { () => { 1 } }\nouter!(abete! if foo);\n");
1935 assert_eq!(a.text, b.text);
1936 }
1937
1938 #[test]
1939 fn fallback_tokens_keep_their_content() {
1940 let a = canonicalize("outer!(pino! if foo);\n");
1941 let b = canonicalize("outer!(pino! if bar);\n");
1942 assert_ne!(a.text, b.text);
1943 }
1944
1945 #[test]
1946 fn alpha_free_name_in_format_string_stays_distinct() {
1947 let a = canonicalize("println!(\"{free_name}\")\n");
1948 let b = canonicalize("println!(\"{other_free}\")\n");
1949 assert_ne!(a.text, b.text);
1950 }
1951
1952 #[test]
1953 fn a_value_literal_is_not_a_format_string() {
1954 let a = canonicalize("let x = 1;\nprintln!(\"{}\", \"{x}\");\n");
1955 let b = canonicalize("let y = 1;\nprintln!(\"{}\", \"{y}\");\n");
1956 assert_ne!(a.text, b.text);
1957 }
1958
1959 #[test]
1960 fn write_format_operand_is_the_second_argument() {
1961 let a = canonicalize("let x = 1;\nwrite!(out, \"{x}\").unwrap();\n");
1962 let b = canonicalize("let y = 1;\nwrite!(out, \"{y}\").unwrap();\n");
1963 assert_eq!(a.text, b.text);
1964 }
1965
1966 #[test]
1967 fn a_positional_identifier_argument_merges_with_its_inline_form() {
1968 let a = canonicalize("let x = 1;\nprintln!(\"{}\", x);\n");
1969 let b = canonicalize("let x = 1;\nprintln!(\"{x}\");\n");
1970 assert_eq!(a.text, b.text);
1971 }
1972
1973 #[test]
1974 fn an_inlined_argument_keeps_its_spec() {
1975 let a = canonicalize("let x = 1;\nlet s = format!(\"{:?}\", x);\n");
1976 let b = canonicalize("let x = 1;\nlet s = format!(\"{x:?}\");\n");
1977 assert_eq!(a.text, b.text);
1978 }
1979
1980 #[test]
1981 fn only_identifier_arguments_inline() {
1982 let a = canonicalize("let x = 1;\nwrite!(out, \"{} {}\", x, x + 1).unwrap();\n");
1983 let b = canonicalize("let x = 1;\nwrite!(out, \"{x} {}\", x + 1).unwrap();\n");
1984 assert_eq!(a.text, b.text);
1985 }
1986
1987 #[test]
1988 fn inlining_composes_with_alpha_renaming() {
1989 let a = canonicalize("let pino = 1;\nprintln!(\"{}\", pino);\n");
1990 let b = canonicalize("let abete = 1;\nprintln!(\"{abete}\");\n");
1991 assert_eq!(a.text, b.text);
1992 }
1993
1994 #[test]
1995 fn a_format_call_inside_a_macro_argument_inlines() {
1996 let a = canonicalize("let x = 1;\nassert_eq!(format!(\"{}\", x), \"1\");\n");
1997 let b = canonicalize("let x = 1;\nassert_eq!(format!(\"{x}\"), \"1\");\n");
1998 assert_eq!(a.text, b.text);
1999 }
2000
2001 #[test]
2002 fn a_panic_message_does_not_inline() {
2003 let a = canonicalize("let x = 1;\npanic!(\"{}\", x);\n");
2005 let b = canonicalize("let x = 1;\npanic!(\"{x}\");\n");
2006 assert_ne!(a.text, b.text);
2007 }
2008
2009 #[test]
2010 fn a_width_argument_blocks_inlining() {
2011 let a = canonicalize("let x = 1;\nlet w = 4;\nprintln!(\"{:1$}\", x, w);\n");
2012 let b = canonicalize("let x = 1;\nlet w = 4;\nprintln!(\"{x:1$}\", w);\n");
2013 assert_ne!(a.text, b.text);
2014 }
2015
2016 #[test]
2017 fn a_raw_identifier_argument_stays_positional() {
2018 let a = canonicalize("let r#type = 1;\nprintln!(\"{}\", r#type);\n");
2019 assert!(a.text.contains("\"{}\""), "{}", a.text);
2020 }
2021
2022 #[test]
2023 fn inline_names_in_panic_and_assert_messages_rename() {
2024 let a = canonicalize("let x = 1;\npanic!(\"{x}\");\n");
2025 let b = canonicalize("let y = 1;\npanic!(\"{y}\");\n");
2026 assert_eq!(a.text, b.text);
2027 let a = canonicalize("let x = 1;\nassert!(x == 1, \"{x}\");\n");
2028 let b = canonicalize("let y = 1;\nassert!(y == 1, \"{y}\");\n");
2029 assert_eq!(a.text, b.text);
2030 }
2031
2032 #[test]
2033 fn a_qualified_path_argument_stays_positional() {
2034 let a = canonicalize("println!(\"{}\", <S>::C);\n");
2035 let b = canonicalize("println!(\"{C}\");\n");
2036 assert_ne!(a.text, b.text);
2037 }
2038
2039 #[test]
2040 fn an_attributed_argument_stays_positional() {
2041 let a = canonicalize("let x = 1;\nprintln!(\"{}\", #[cfg(unix)] x);\n");
2042 let b = canonicalize("let x = 1;\nprintln!(\"{x}\");\n");
2043 assert_ne!(a.text, b.text);
2044 }
2045
2046 #[test]
2047 fn escaped_braces_survive_inlining() {
2048 let a = canonicalize("let x = 1;\nprintln!(\"{{}} {}\", x);\n");
2049 let b = canonicalize("let x = 1;\nprintln!(\"{{}} {x}\");\n");
2050 assert_eq!(a.text, b.text);
2051 }
2052
2053 #[test]
2054 fn an_explicit_index_blocks_inlining() {
2055 let a = canonicalize("let x = 1;\nprintln!(\"{0} {}\", x);\n");
2056 let b = canonicalize("let x = 1;\nprintln!(\"{0} {x}\");\n");
2057 assert_ne!(a.text, b.text);
2058 }
2059
2060 #[test]
2061 fn an_unused_argument_blocks_inlining() {
2062 let a = canonicalize("let x = 1;\nlet y = 2;\nprintln!(\"{}\", x, y);\n");
2063 let b = canonicalize("let x = 1;\nlet y = 2;\nprintln!(\"{x}\", y);\n");
2064 assert_ne!(a.text, b.text);
2065 }
2066
2067 #[test]
2068 fn assert_eq_message_is_the_third_argument() {
2069 let a = canonicalize("let x = 1;\nassert_eq!(x, 1, \"{x}\");\n");
2070 let b = canonicalize("let y = 1;\nassert_eq!(y, 1, \"{y}\");\n");
2071 assert_eq!(a.text, b.text);
2072 let c = canonicalize("let x = 1;\nassert_eq!(\"{x}\", 1);\n");
2073 let d = canonicalize("let y = 1;\nassert_eq!(\"{y}\", 1);\n");
2074 assert_ne!(c.text, d.text);
2075 }
2076
2077 #[test]
2078 fn an_unknown_macro_keeps_its_literals() {
2079 let a = canonicalize("let x = 1;\nmy_log!(\"{x}\");\n");
2080 let b = canonicalize("let y = 1;\nmy_log!(\"{y}\");\n");
2081 assert_ne!(a.text, b.text);
2082 }
2083
2084 #[test]
2085 fn alpha_renames_impl_method_locals() {
2086 let a = canonicalize(
2087 "struct Pino { n: u8 }\nimpl Pino {\n fn take(&self, pino: u8) -> u8 { pino + self.n }\n}\n",
2088 );
2089 let b = canonicalize(
2090 "struct Abete { n: u8 }\nimpl Abete {\n fn take(&self, abete: u8) -> u8 { abete + self.n }\n}\n",
2091 );
2092 assert_eq!(a.text, b.text);
2093 }
2094
2095 #[test]
2096 fn alpha_renames_trait_method_locals() {
2097 let a = canonicalize("trait Pino {\n fn f(&self, pino: u8) -> u8 { pino }\n}\n");
2098 let b = canonicalize("trait Abete {\n fn f(&self, abete: u8) -> u8 { abete }\n}\n");
2099 assert_eq!(a.text, b.text);
2100 }
2101
2102 #[test]
2103 fn alpha_renames_local_enum() {
2104 let a = canonicalize("enum Pino { Leaf, Branch(u8) }\n");
2105 let b = canonicalize("enum Abete { Leaf, Branch(u8) }\n");
2106 assert_eq!(a.text, b.text);
2107 }
2108
2109 #[test]
2110 fn alpha_renames_local_union() {
2111 let a = canonicalize("union Pino { n: u8 }\n");
2112 let b = canonicalize("union Abete { n: u8 }\n");
2113 assert_eq!(a.text, b.text);
2114 }
2115
2116 #[test]
2117 fn alpha_renames_local_type_alias() {
2118 let a = canonicalize("type Pino = u8;\n");
2119 let b = canonicalize("type Abete = u8;\n");
2120 assert_eq!(a.text, b.text);
2121 }
2122
2123 #[test]
2124 fn alpha_renames_local_trait() {
2125 let a = canonicalize("trait Pino {}\n");
2126 let b = canonicalize("trait Abete {}\n");
2127 assert_eq!(a.text, b.text);
2128 }
2129
2130 #[test]
2131 fn alpha_renames_local_trait_alias() {
2132 let a = canonicalize("trait Pino = Clone;\nfn f<T: Pino>() {}\n");
2133 let b = canonicalize("trait Abete = Clone;\nfn f<T: Abete>() {}\n");
2134 assert_eq!(a.text, b.text);
2135 let c = canonicalize("trait Pino = Clone;\nfn f<T: Copy>() {}\n");
2136 assert_ne!(a.text, c.text);
2137 let forward_pino = canonicalize("fn f<T: Pino>() {}\ntrait Pino = Clone;\n");
2138 let forward_abete = canonicalize("fn f<T: Abete>() {}\ntrait Abete = Clone;\n");
2139 assert_eq!(forward_pino.text, forward_abete.text);
2140 }
2141
2142 #[test]
2143 fn alpha_renames_local_const() {
2144 let a = canonicalize("const PINO: u8 = 1;\nPINO + 1\n");
2145 let b = canonicalize("const ABETE: u8 = 1;\nABETE + 1\n");
2146 assert_eq!(a.text, b.text);
2147 }
2148
2149 #[test]
2150 fn alpha_renames_local_static() {
2151 let a = canonicalize("static PINO: u8 = 1;\nPINO + 1\n");
2152 let b = canonicalize("static ABETE: u8 = 1;\nABETE + 1\n");
2153 assert_eq!(a.text, b.text);
2154 }
2155
2156 #[test]
2157 fn alpha_renames_macro_definition() {
2158 let a = canonicalize("macro_rules! pino { () => { 1 } }\npino!()\n");
2159 let b = canonicalize("macro_rules! abete { () => { 1 } }\nabete!()\n");
2160 assert_eq!(a.text, b.text);
2161 }
2162
2163 #[test]
2164 fn alpha_keeps_unit_variant_patterns() {
2165 let a = canonicalize("match it {\n None => 0,\n Some(v) => v,\n}\n");
2166 let b = canonicalize("match it {\n Empty => 0,\n Some(v) => v,\n}\n");
2167 assert_ne!(a.text, b.text);
2168 let c = canonicalize("let Unit = make();\n");
2169 let d = canonicalize("let Other = make();\n");
2170 assert_ne!(c.text, d.text);
2171 }
2172
2173 #[test]
2174 fn alpha_renames_match_arm_locals() {
2175 let a = canonicalize("match it {\n pino => pino + 1,\n _ => 0,\n}\n");
2176 let b = canonicalize("match it {\n abete => abete + 1,\n _ => 0,\n}\n");
2177 assert_eq!(a.text, b.text);
2178 }
2179
2180 #[test]
2181 fn alpha_renames_for_loop_locals() {
2182 let a = canonicalize("for pino in items {\n use_it(pino)\n}\n");
2183 let b = canonicalize("for abete in items {\n use_it(abete)\n}\n");
2184 assert_eq!(a.text, b.text);
2185 }
2186
2187 #[test]
2188 fn alpha_renames_if_let_locals() {
2189 let a = canonicalize("if let Some(pino) = pick() {\n use_it(pino)\n}\n");
2190 let b = canonicalize("if let Some(abete) = pick() {\n use_it(abete)\n}\n");
2191 assert_eq!(a.text, b.text);
2192 }
2193
2194 #[test]
2195 fn alpha_renames_while_let_locals() {
2196 let a = canonicalize("while let Some(pino) = next() {\n use_it(pino)\n}\n");
2197 let b = canonicalize("while let Some(abete) = next() {\n use_it(abete)\n}\n");
2198 assert_eq!(a.text, b.text);
2199 }
2200
2201 #[test]
2202 fn alpha_attribute_value_locals_renamed() {
2203 let a = canonicalize("let pino = 1; #[attr = pino] fn f() {}");
2206 let b = canonicalize("let abete = 1; #[attr = abete] fn f() {}");
2207 assert_eq!(a.text, b.text);
2208 }
2209 #[test]
2210 fn alpha_self_alias_collapses_to_local_type() {
2211 let a = canonicalize(
2212 "struct Pino { n: u8 }\nimpl Pino {\n fn f(self) -> Self { self }\n}\n",
2213 );
2214 let b = canonicalize(
2215 "struct Pino { n: u8 }\nimpl Pino {\n fn f(self) -> Pino { self }\n}\n",
2216 );
2217 assert_eq!(a.text, b.text);
2218 }
2219
2220 #[test]
2221 fn alpha_self_alias_requires_plain_self_type() {
2222 let c = canonicalize(
2223 "struct Abete { }\nstruct Pino { n: u8 }\nimpl Wrap for <Pino as Inner>::Abete {\n fn f(self) { let s: Self = Self; }\n}\n",
2224 );
2225 assert!(c.text.contains("Self"));
2226 }
2227
2228 #[test]
2229 fn a_return_in_a_let_block_is_not_the_fn_return_value() {
2230 let early = canonicalize("fn f() -> u8 { let _a = { return 1; }; 2 }");
2233 let value = canonicalize("fn f() -> u8 { let _a = { 1 }; 2 }");
2234 assert_ne!(early.text, value.text);
2235 }
2236
2237 #[test]
2238 fn a_return_in_an_argument_block_is_not_the_fn_return_value() {
2239 let early = canonicalize("fn f() -> u8 { g({ return 1; }); 2 }");
2240 let value = canonicalize("fn f() -> u8 { g({ 1 }); 2 }");
2241 assert_ne!(early.text, value.text);
2242 }
2243
2244 #[test]
2245 fn a_return_in_a_loop_body_is_not_a_tail_expression() {
2246 let early = canonicalize("fn f() -> u8 { while c { return 1; } 2 }");
2249 let looped = canonicalize("fn f() -> u8 { while c { 1 } 2 }");
2250 assert_ne!(early.text, looped.text);
2251 }
2252
2253 #[test]
2254 fn a_return_in_a_tail_match_arm_folds() {
2255 let early = canonicalize("fn f() -> u8 { match v { A => { return 1; }, _ => 2 } }");
2258 let value = canonicalize("fn f() -> u8 { match v { A => { 1 }, _ => 2 } }");
2259 assert_eq!(early.text, value.text);
2260 }
2261
2262 #[test]
2263 fn a_return_in_a_non_tail_match_arm_is_not_the_discarded_value() {
2264 let early = canonicalize("fn f() -> u8 { match v { A => { return 1; }, _ => {} }; 2 }");
2267 let value = canonicalize("fn f() -> u8 { match v { A => { 1 }, _ => {} }; 2 }");
2268 assert_ne!(early.text, value.text);
2269 }
2270
2271 #[test]
2272 fn a_return_in_a_tail_unsafe_block_folds() {
2273 let a = canonicalize("fn f() -> u8 { unsafe { return 1; } }");
2274 let b = canonicalize("fn f() -> u8 { unsafe { 1 } }");
2275 assert_eq!(a.text, b.text);
2276 }
2277
2278 #[test]
2279 fn a_return_in_tail_if_else_branches_folds() {
2280 let a = canonicalize(
2281 "fn f(c: bool, d: bool) -> u8 { if c { return 1; } else if d { return 2; } else { return 3; } }",
2282 );
2283 let b =
2284 canonicalize("fn f(c: bool, d: bool) -> u8 { if c { 1 } else if d { 2 } else { 3 } }");
2285 assert_eq!(a.text, b.text);
2286 }
2287
2288 #[test]
2289 fn a_return_in_a_no_else_if_keeps_its_return() {
2290 let a = canonicalize("fn f() -> u8 { if c { return 1; } }");
2294 let b = canonicalize("fn f() -> u8 { if c { 1 } }");
2295 assert_ne!(a.text, b.text);
2296 }
2297
2298 #[test]
2299 fn an_impl_fn_tail_return_folds() {
2300 let a = canonicalize("impl T for S { fn f(&self) -> u8 { return 1; } }");
2301 let b = canonicalize("impl T for S { fn f(&self) -> u8 { 1 } }");
2302 assert_eq!(a.text, b.text);
2303 }
2304
2305 #[test]
2306 fn a_trait_default_fn_tail_return_folds() {
2307 let a = canonicalize("trait T { fn f() -> u8 { return 1; } }");
2308 let b = canonicalize("trait T { fn f() -> u8 { 1 } }");
2309 assert_eq!(a.text, b.text);
2310 }
2311
2312 #[test]
2313 fn an_inert_attr_on_a_fn_pointer_parameter_merges() {
2314 let a = canonicalize("let f: fn(#[expect(unused)] x: u8) = g;");
2317 let b = canonicalize("let f: fn(x: u8) = g;");
2318 assert_eq!(a.text, b.text);
2319 }
2320
2321 #[test]
2322 fn an_inert_attr_on_a_method_receiver_merges() {
2323 let a = canonicalize("impl S { fn m(#[expect(unused)] &self) -> u8 { 1 } }");
2325 let b = canonicalize("impl S { fn m(&self) -> u8 { 1 } }");
2326 assert_eq!(a.text, b.text);
2327 }
2328 #[test]
2329 fn a_single_expr_arm_block_unwraps_in_a_non_tail_match() {
2330 let a = canonicalize("fn f() -> u8 { match v { A => { 1 }, _ => 2 }; 2 }");
2333 let b = canonicalize("fn f() -> u8 { match v { A => 1, _ => 2 }; 2 }");
2334 assert_eq!(a.text, b.text);
2335 }
2336
2337 #[test]
2338 fn a_folded_return_expression_reenters_the_chain() {
2339 let a = canonicalize("fn f() -> u8 { return { g(); return 2; }; }");
2342 let b = canonicalize("fn f() -> u8 { { g(); 2 } }");
2343 assert_eq!(a.text, b.text);
2344 }
2345
2346 #[test]
2347 fn a_higher_ranked_lifetime_does_not_shadow_a_loop_label() {
2348 let a = canonicalize(
2351 "fn f() -> u8 { 'a: loop { let g: for<'a> fn(&'a u8) -> &'a u8 = h; break 'a 1; } }",
2352 );
2353 let b = canonicalize(
2354 "fn f() -> u8 { 'zz: loop { let g: for<'q> fn(&'q u8) -> &'q u8 = h; break 'zz 1; } }",
2355 );
2356 assert_eq!(a.text, b.text);
2357 }
2358
2359 #[test]
2360 fn a_label_and_a_lifetime_param_of_one_name_stay_distinct() {
2361 let a = canonicalize(
2365 "fn f<'a>(v: bool, x: &'a u8) -> &'a u8 { 'a: loop { if v { continue 'a; } let y: &'a u8 = x; break 'a y; } }",
2366 );
2367 let b = canonicalize(
2368 "fn f<'z>(v: bool, x: &'z u8) -> &'z u8 { 'q: loop { if v { continue 'q; } let y: &'z u8 = x; break 'q y; } }",
2369 );
2370 assert_eq!(a.text, b.text);
2371 }
2372
2373 #[test]
2374 fn a_value_binding_does_not_capture_a_type_annotation() {
2375 let a = canonicalize("type s = u8; fn f(s: u8) -> s { 1 }");
2378 let b = canonicalize("type zz = u8; fn f(q: u8) -> zz { 1 }");
2379 assert_eq!(a.text, b.text);
2380 }
2381
2382 #[test]
2383 fn a_value_binding_does_not_capture_a_trait_bound() {
2384 let a = canonicalize("trait t {} fn f(t: u8) { struct A<T: t>(T); }");
2387 let b = canonicalize("trait zz {} fn f(q: u8) { struct B<T: zz>(T); }");
2388 assert_eq!(a.text, b.text);
2389 }
2390
2391 #[test]
2392 fn an_expression_of_a_captured_name_keeps_the_value_namespace() {
2393 let a = canonicalize("type s = u8; fn f(s: u8) -> s { s }");
2394 let b = canonicalize("type zz = u8; fn f(q: u8) -> zz { q }");
2395 assert_eq!(a.text, b.text);
2396 }
2397
2398 #[test]
2399 fn a_value_use_after_a_bound_keeps_the_value_namespace() {
2400 let a = canonicalize("trait t {} fn f(t: u8) { struct A<T: t>(T); let _x = t; }");
2401 let b = canonicalize("trait zz {} fn f(q: u8) { struct B<T: zz>(T); let _x = q; }");
2402 assert_eq!(a.text, b.text);
2403 }
2404
2405 #[test]
2406 fn a_const_argument_expression_keeps_the_value_namespace() {
2407 let a = canonicalize(
2410 "type s = usize; const s: usize = 2; struct F<const N: usize, T> { n: core::marker::PhantomData<T> } const G: F<{ s }, s> = F { n: core::marker::PhantomData };",
2411 );
2412 let b = canonicalize(
2413 "type zz = usize; const q: usize = 2; struct H<const N: usize, T> { n: core::marker::PhantomData<T> } const R: H<{ q }, zz> = H { n: core::marker::PhantomData };",
2414 );
2415 assert_eq!(a.text, b.text);
2416 }
2417
2418 fn token_after(text: &str, keyword: &str) -> String {
2419 let words: Vec<&str> = text
2420 .split_whitespace()
2421 .map(|w| w.trim_end_matches([')', ',', ';']))
2422 .collect();
2423 let i = words.iter().position(|w| *w == keyword).unwrap();
2424 words[i + 1].to_string()
2425 }
2426
2427 #[test]
2428 fn a_use_alias_annotation_prints_the_declared_binder() {
2429 let a = canonicalize("use a::T;\nfn f(x: T) {}");
2433 assert_eq!(token_after(&a.text, "as"), token_after(&a.text, ":"));
2434 }
2435
2436 #[test]
2437 fn an_impl_trait_path_prints_the_declared_trait() {
2438 let a = canonicalize(
2443 "const t: u8 = 5; trait t { fn m(&self) -> u8; } struct W; impl t for W { fn m(&self) -> u8 { 1 } } fn g() { let _ = t; W.m(); }",
2444 );
2445 assert_eq!(token_after(&a.text, "trait"), token_after(&a.text, "impl"));
2446 }
2447 #[test]
2448 fn a_higher_ranked_dyn_bound_does_not_shadow_a_loop_label() {
2449 let a = canonicalize(
2450 "fn f() -> u8 { 'a: loop { let d: &dyn for<'a> Fn(&'a u8) = g; break 'a 1; } }",
2451 );
2452 let b = canonicalize(
2453 "fn f() -> u8 { 'zz: loop { let d: &dyn for<'q> Fn(&'q u8) = g; break 'zz 1; } }",
2454 );
2455 assert_eq!(a.text, b.text);
2456 }
2457
2458 #[test]
2459 fn a_tail_return_without_semicolon_folds() {
2460 let a = canonicalize("fn f() -> u8 { return 1 }");
2461 let b = canonicalize("fn f() -> u8 { 1 }");
2462 assert_eq!(a.text, b.text);
2463 }
2464
2465 #[test]
2466 fn a_cfg_attr_on_a_tail_return_keeps_the_return() {
2467 let a = canonicalize(
2470 "fn f() -> u8 { #[cfg(never_flag)] #[expect(unreachable_code)] return 1; 2 }",
2471 );
2472 let b = canonicalize("fn f() -> u8 { 1; 2 }");
2473 assert_ne!(a.text, b.text);
2474 }
2475
2476 #[test]
2477 fn an_inert_attr_on_a_tail_return_still_folds() {
2478 let a = canonicalize("fn f() -> u8 { #[expect(unreachable_code)] return 1; }");
2479 let b = canonicalize("fn f() -> u8 { 1 }");
2480 assert_eq!(a.text, b.text);
2481 }
2482
2483 #[test]
2484 fn inert_attrs_on_closure_parameter_patterns_merge() {
2485 let params = [
2488 "x",
2489 "&k",
2490 "(p, q)",
2491 "(Some(s))",
2492 "Some(r)",
2493 "[m, ..]",
2494 "P { x }",
2495 "_g",
2496 "_",
2497 "1 | 2",
2498 "None",
2499 "1",
2500 "1..=5",
2501 "..=5",
2502 "a::B",
2503 "<T>::C",
2504 "const { 1 }",
2505 "x: u8",
2506 "..",
2507 "mac!()",
2508 ];
2509 for param in params {
2510 let with = canonicalize(&format!("let c = |#[expect(unused)] {param}| 0;"));
2511 let without = canonicalize(&format!("let c = |{param}| 0;"));
2512 assert_eq!(with.text, without.text, "param pattern {param}");
2513 }
2514 }
2515
2516 #[test]
2517 fn inert_attrs_on_built_or_and_guard_patterns_merge() {
2518 use quote::ToTokens as _;
2520 use syn::visit_mut::VisitMut;
2521
2522 struct Attach;
2523 impl VisitMut for Attach {
2524 fn visit_pat_mut(&mut self, pat: &mut syn::Pat) {
2525 let attr: syn::Attribute = syn::parse_quote!(#[expect(unused)]);
2526 match pat {
2527 syn::Pat::Or(p) => p.attrs.push(attr),
2528 syn::Pat::Guard(p) => p.attrs.push(attr),
2529 _ => {}
2530 }
2531 syn::visit_mut::visit_pat_mut(self, pat);
2532 }
2533 }
2534 for src in [
2535 "fn main() { match v { A | B => 1, _ => 2 }; }",
2536 "fn main() { match v { A if c => 1, _ => 2 }; }",
2537 ] {
2538 let plain: syn::File = syn::parse_str(src).unwrap();
2539 let mut marked = plain.clone();
2540 Attach.visit_file_mut(&mut marked);
2541 assert_ne!(
2542 marked.to_token_stream().to_string(),
2543 plain.to_token_stream().to_string()
2544 );
2545 assert_eq!(canon(marked).to_string(), canon(plain).to_string(), "{src}");
2546 }
2547 }
2548
2549 #[test]
2550 fn a_return_in_an_async_block_is_its_tail_value() {
2551 let a = canonicalize("let f = async { return 1; };");
2554 let b = canonicalize("let f = async { 1 };");
2555 assert_eq!(a.text, b.text);
2556 }
2557
2558 #[test]
2559 fn an_inert_attr_on_a_match_arm_merges() {
2560 let a = canonicalize("match v { #[expect(unused_variables)] Some(_) => {}, None => {} }");
2563 let b = canonicalize("match v { Some(_) => {}, None => {} }");
2564 assert_eq!(a.text, b.text);
2565 }
2566
2567 #[test]
2568 fn a_cfg_attr_on_a_match_arm_stays_distinct() {
2569 let a = canonicalize("match v { #[cfg(unix)] Some(_) => {}, None => {} }");
2570 let b = canonicalize("match v { Some(_) => {}, None => {} }");
2571 assert_ne!(a.text, b.text);
2572 }
2573
2574 #[test]
2575 fn an_inert_attr_on_a_fn_param_merges() {
2576 let a = canonicalize("fn f(#[expect(unused_variables)] x: u8) { x }");
2578 let b = canonicalize("fn f(x: u8) { x }");
2579 assert_eq!(a.text, b.text);
2580 }
2581
2582 #[test]
2583 fn a_literal_in_a_local_macro_stays_opaque() {
2584 let a = canonicalize("m!(0x10);\nf();");
2585 let b = canonicalize("m!(16);\nf();");
2586 assert_ne!(a.text, b.text);
2587 }
2588
2589 #[test]
2590 fn alpha_renames_let_chain_binder_in_a_match_guard() {
2591 let a = canonicalize("match w { Some(v) if let Some(z) = q && z == v => z, _ => 2 }");
2595 let b = canonicalize("match w { Some(v) if let Some(t) = q && t == v => t, _ => 2 }");
2596 assert_eq!(a.text, b.text);
2597 }
2598
2599 #[test]
2600 fn alpha_renames_higher_ranked_lifetime_in_a_dyn_bound() {
2601 let a = canonicalize("let f: &dyn for<'a> Fn(&'a u8) = g;");
2604 let b = canonicalize("let f: &dyn for<'b> Fn(&'b u8) = g;");
2605 assert_eq!(a.text, b.text);
2606 }
2607
2608 #[test]
2609 fn alpha_renames_higher_ranked_lifetime_in_a_where_bound() {
2610 let a = canonicalize("fn g<T>(x: &T) where T: for<'a> Tr<'a> {}");
2611 let b = canonicalize("fn g<T>(x: &T) where T: for<'b> Tr<'b> {}");
2612 assert_eq!(a.text, b.text);
2613 }
2614
2615 #[test]
2616 fn alpha_erases_fn_pointer_parameter_names() {
2617 let named = canonicalize("let f: fn(a: u8) = g;");
2620 let other = canonicalize("let f: fn(b: u8) = g;");
2621 let discard = canonicalize("let f: fn(_: u8) = g;");
2622 let bare = canonicalize("let f: fn(u8) = g;");
2623 assert_eq!(named.text, other.text);
2624 assert_eq!(named.text, discard.text);
2625 assert_eq!(named.text, bare.text);
2626 }
2627
2628 fn assert_merge(pairs: &[(&str, &str)]) {
2630 for (a, b) in pairs {
2631 assert_eq!(canonicalize(a).text, canonicalize(b).text, "{a} vs {b}");
2632 }
2633 }
2634
2635 fn assert_split(pairs: &[(&str, &str)]) {
2637 for (a, b) in pairs {
2638 assert_ne!(canonicalize(a).text, canonicalize(b).text, "{a} vs {b}");
2639 }
2640 }
2641
2642 #[test]
2643 fn an_unneeded_binding_mut_merges() {
2644 assert_merge(&[
2645 (
2646 "let mut v = vec![1]; f(v.len());",
2647 "let v = vec![1]; f(v.len());",
2648 ),
2649 ("let mut v: u8 = 1; f(v);", "let v: u8 = 1; f(v);"),
2650 ("let h = |mut x: u8| x + 1;", "let h = |x: u8| x + 1;"),
2651 ("let h = |mut x| x + 1;", "let h = |x| x + 1;"),
2652 ("fn h(mut x: u8) -> u8 { x }", "fn h(x: u8) -> u8 { x }"),
2653 ("for mut x in v { f(x) }", "for x in v { f(x) }"),
2654 ("match v { mut x => f(x) }", "match v { x => f(x) }"),
2655 (
2656 "match v { mut x if x > 1 => f(x), _ => {} }",
2657 "match v { x if x > 1 => f(x), _ => {} }",
2658 ),
2659 ("if let mut x = v { f(x) }", "if let x = v { f(x) }"),
2660 (
2661 "let mut x @ 1..=3 = v else { return };",
2662 "let x @ 1..=3 = v else { return };",
2663 ),
2664 (
2665 "impl S { fn h(mut self) -> u8 { self.0 } }",
2666 "impl S { fn h(self) -> u8 { self.0 } }",
2667 ),
2668 (
2669 "impl S { fn h(mut self: Box<Self>) -> u8 { self.0 } }",
2670 "impl S { fn h(self: Box<Self>) -> u8 { self.0 } }",
2671 ),
2672 ]);
2673 }
2674
2675 #[test]
2676 fn a_mut_that_changes_the_type_or_the_binding_mode_stays() {
2677 assert_split(&[
2678 (
2679 "impl S { fn h(&mut self) -> u8 { self.0 } }",
2680 "impl S { fn h(&self) -> u8 { self.0 } }",
2681 ),
2682 (
2683 "let Some(ref mut x) = o else { return };",
2684 "let Some(ref x) = o else { return };",
2685 ),
2686 ("let &mut x = r;", "let &x = r;"),
2687 ("let r = &mut v; f(r);", "let r = &v; f(r);"),
2688 ("static mut S: u8 = 1;", "static S: u8 = 1;"),
2689 ("let p: *mut u8 = q;", "let p: *const u8 = q;"),
2690 ("let [mut x] = &[0_u8]; f(x);", "let [x] = &[0_u8]; f(x);"),
2692 (
2693 "let (mut a, b) = g(); f(a, b);",
2694 "let (a, b) = g(); f(a, b);",
2695 ),
2696 (
2697 "if let Some(mut x) = o { f(x) }",
2698 "if let Some(x) = o { f(x) }",
2699 ),
2700 ("fn h((mut a, b): (u8, u8)) {}", "fn h((a, b): (u8, u8)) {}"),
2701 (
2702 "match o { Some(mut x) => f(x), None => {} }",
2703 "match o { Some(x) => f(x), None => {} }",
2704 ),
2705 (
2706 "let mut x @ Some(mut y) = o else { return };",
2707 "let mut x @ Some(y) = o else { return };",
2708 ),
2709 ]);
2710 }
2711
2712 #[test]
2713 fn a_semicolon_after_a_block_statement_merges() {
2714 assert_merge(&[
2715 ("if c { f(); }; g();", "if c { f(); } g();"),
2716 ("match x { _ => f() }; g();", "match x { _ => f() } g();"),
2717 ("for i in v { f(i); }; g();", "for i in v { f(i); } g();"),
2718 ("while c { f(); }; g();", "while c { f(); } g();"),
2719 ("loop { break; }; g();", "loop { break; } g();"),
2720 ("{ f(); }; g();", "{ f(); } g();"),
2721 ("unsafe { f(); }; g();", "unsafe { f(); } g();"),
2722 (
2723 "let x = || { if c { f(); }; g() };",
2724 "let x = || { if c { f(); } g() };",
2725 ),
2726 ]);
2727 }
2728
2729 #[test]
2730 fn a_semicolon_after_a_valued_tail_block_stays() {
2731 assert_split(&[
2732 (
2733 "let a = { if c { g() } else { h() } }; f(a);",
2734 "let a = { if c { g() } else { h() }; }; f(a);",
2735 ),
2736 (
2737 "fn k() -> u8 { match x { _ => 1 } }",
2738 "fn k() -> u8 { match x { _ => 1 }; }",
2739 ),
2740 (
2741 "let a = { if c { g() } else { h() } }; f(a);",
2742 "let a = { if c { g() } else { h() }; ; }; f(a);",
2743 ),
2744 ]);
2745 }
2746
2747 #[test]
2748 fn a_unit_tail_semicolon_merges() {
2749 assert_merge(&[
2750 ("f(); g()", "f(); g();"),
2751 ("f(); println!(\"x\")", "f(); println!(\"x\");"),
2752 ("fn k() { f(); g() }", "fn k() { f(); g(); }"),
2753 ("fn k() -> () { f(); g() }", "fn k() { f(); g(); }"),
2754 (
2755 "impl S { fn k(&self) { g() } }",
2756 "impl S { fn k(&self) { g(); } }",
2757 ),
2758 (
2759 "trait T { fn k(&self) { g() } }",
2760 "trait T { fn k(&self) { g(); } }",
2761 ),
2762 (
2763 "for i in 0..3 { a[i] = b[i] } f();",
2764 "for i in 0..3 { a[i] = b[i]; } f();",
2765 ),
2766 ("while c { g() } f();", "while c { g(); } f();"),
2767 (
2768 "let v = loop { break 5 }; f(v);",
2769 "let v = loop { break 5; }; f(v);",
2770 ),
2771 (
2772 "if c { panic!(\"x\") } f();",
2773 "if c { panic!(\"x\"); } f();",
2774 ),
2775 (
2776 "if a { g() } else if b { h() } f();",
2777 "if a { g(); } else if b { h(); } f();",
2778 ),
2779 ("fn k() { f(); return; }", "fn k() { f() }"),
2780 (
2781 "fn k() { if c { return g(); } else { h() }; }",
2782 "fn k() { if c { g() } else { h() } }",
2783 ),
2784 ]);
2785 }
2786
2787 #[test]
2788 fn a_tail_semicolon_where_the_value_is_used_stays() {
2789 assert_split(&[
2790 ("fn k() -> u8 { g() }", "fn k() -> u8 { g(); }"),
2791 ("let h = || { g() };", "let h = || { g(); };"),
2792 ("let a = async { g() };", "let a = async { g(); };"),
2793 ("let a = { g() }; f(a);", "let a = { g(); }; f(a);"),
2794 (
2795 "let a = if c { g() } else { h() };",
2796 "let a = if c { g(); } else { h(); };",
2797 ),
2798 (
2799 "let a = match x { _ => { g() } };",
2800 "let a = match x { _ => { g(); } };",
2801 ),
2802 ("let a = unsafe { g() };", "let a = unsafe { g(); };"),
2803 ]);
2804 }
2805
2806 #[test]
2807 fn code_meant_to_fail_keeps_mut_and_semicolons() {
2808 let failing = |code: &str| super::canonicalize_failing(parse(code)).to_string();
2809 for (a, b) in [
2810 (
2811 "let mut v = Vec::new(); v.push(1);",
2812 "let v = Vec::new(); v.push(1);",
2813 ),
2814 ("impl S { fn h(mut self) {} }", "impl S { fn h(self) {} }"),
2815 ("match x { _ => 5 }; g();", "match x { _ => 5 } g();"),
2816 ("f(); 5", "f(); 5;"),
2817 ("for i in v { i }", "for i in v { i; }"),
2818 ] {
2819 assert_ne!(failing(a), failing(b), "{a} vs {b}");
2820 assert_eq!(canonicalize(a).text, canonicalize(b).text, "{a} vs {b}");
2821 }
2822 assert_eq!(
2823 failing("fn f(x: u8) -> u8 { return (x); }"),
2824 failing("fn g(y: u8) -> u8 { y }")
2825 );
2826 }
2827
2828 #[test]
2829 fn an_associated_item_after_a_bare_qself_is_never_a_local() {
2830 assert_split(&[
2831 (
2832 "let new = 1; let s = <S>::new(); f(s, new);",
2833 "let default = 1; let s = <S>::default(); f(s, default);",
2834 ),
2835 (
2836 "let len = 2; f(<&str>::len(\"abc\") + len);",
2837 "let n = 2; f(<&str>::n(\"abc\") + n);",
2838 ),
2839 (
2840 "let x = 1; let s = <S>::x { a: x };",
2841 "let y = 1; let s = <S>::y { a: y };",
2842 ),
2843 (
2844 "let a = 1; let <S>::a(v) = g(a);",
2845 "let b = 1; let <S>::b(v) = g(b);",
2846 ),
2847 (
2848 "let a = 1; let <S>::a { v } = g(a);",
2849 "let b = 1; let <S>::b { v } = g(b);",
2850 ),
2851 (
2852 "let a = 1; let v: <<S>::a>::Out = g(a);",
2853 "let b = 1; let v: <<S>::b>::Out = g(b);",
2854 ),
2855 ]);
2856 }
2857
2858 #[test]
2859 fn a_bare_qself_path_keeps_its_separator() {
2860 for code in [
2861 "let n = <&str>::len(\"abc\");",
2862 "let v = <Vec<u8>>::new();",
2863 "let v: <<S>::A>::B = g();",
2864 "let <S>::A(v) = g();",
2865 ] {
2866 let text = canonicalize(code).text;
2867 assert!(
2868 syn::parse_str::<syn::File>(&text).is_ok(),
2869 "{code} → {text}"
2870 );
2871 }
2872 }
2873
2874 #[test]
2875 fn binders_around_a_bare_qself_path_still_rename() {
2876 assert_merge(&[(
2877 "let alpha = 1; let s = <S>::new(alpha); f(<S as T>::g(alpha));",
2878 "let beta = 1; let s = <S>::new(beta); f(<S as T>::g(beta));",
2879 )]);
2880 }
2881}