Skip to main content

syn_canon/
lib.rs

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/// The canonical token stream of `file`, equal across formatting drift and local binder names.
16/// `file` is taken to compile, so an unneeded `mut` and the semicolon of a `()` tail fold too.
17#[must_use]
18pub fn canonicalize(file: syn::File) -> TokenStream {
19    canonical(file, true)
20}
21
22/// `canonicalize` for a `file` meant to fail compilation, a `compile_fail` doctest for instance,
23/// keeping the `mut` and the semicolons that may be the error it shows.
24#[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
37/// The std macros that accept an optional trailing comma and treat it as nothing.
38const 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
62/// The built-in attributes whose arguments a trailing comma doesn't change.
63const COMMA_BLIND_ATTRIBUTES: [&str; 10] = [
64    "allow", "cfg", "cfg_attr", "deny", "derive", "expect", "feature", "forbid", "repr", "warn",
65];
66
67/// Drop each group's trailing comma unless `keep_commas`, set inside the tokens of a macro or an
68/// attribute that may match on it, and respell literals outside `opaque` macro and attribute tokens.
69fn fold_tokens(stream: TokenStream, opaque: bool, keep_commas: bool) -> Vec<TokenTree> {
70    let mut out: Vec<TokenTree> = Vec::new();
71    // A proc macro derive was seen, then the `struct`, `enum` or `union` whose body it reads.
72    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                    // A one-tuple keeps its comma, the paren fold already told it apart.
101                    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
117/// Whether `group` is a `[derive(…)]` naming a proc macro. Only an attribute puts one right
118/// before an item keyword, so the `#` ahead of it needs no check.
119fn 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
132/// Whether `group`, following the tokens in `before`, holds the input of a macro call or an
133/// attribute that may match on a trailing comma.
134fn 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    /// Canonical text of one test body.
165    struct Canon {
166        text: String,
167    }
168
169    /// `code` parsed the way a doctest compiles, panicking when it does not parse.
170    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    /// `code` canonicalized the way a doctest compiles.
180    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        // In decimal `0b0buu` reads as a binary prefix, `0o7e3` as a float,
350        // `0b0x1` and `0b0x0` as hex literals.
351        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        // rustfmt keeps these commas for the same reason, the macro sees them.
507        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        // clap reads field docs as help text, num_enum tests a derive that ignores extra attributes.
562        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        // docify and document-features compute docs at compile time, and the call can fail.
604        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            // In a fn pointer or `Fn` sugar elision is higher ranked, not `'static`.
1265            ("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            // An impl header may not elide a path lifetime (E0726).
1271            (
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        // The inner use resolves to the inner binder and the trailing
1433        // use to the outer one, so both canonical names appear.
1434        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        // `€` is three bytes, the width name `w` starts after all of them.
1919        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        // Before edition 2021 a lone `panic!("{x}")` literal is no format string.
2004        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        // A leading `#[...]` line is kept, so the attribute sits inline
2204        // to reach the parser.
2205        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        // rustc returns 1 from the first body and 2 from the second.
2231        // return_tail_folds is sound only where the block tail is the fn body.
2232        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        // The first body returns on the first pass, the second never
2247        // terminates.
2248        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        // A tail match arm value is the fn result, `return 1;` and `1`
2256        // return the same value, verified equivalent with rustc.
2257        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        // The semicolon drops the match value while an early return still
2265        // escapes the fn, so that arm must not fold.
2266        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        // An `if` without an `else` discards its then value and rustc
2291        // rejects a valued return there (`E0317`), so this shape cannot
2292        // compile and must not fold.
2293        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        // rustc, verified, accepts an attribute on a named fn pointer
2315        // parameter, the strip lives in `visit_named_arg_mut`.
2316        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        // rustc, verified, accepts an attribute on a method receiver.
2324        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        // The arm block unwrap lives in `visit_arm_mut`, not in the tail
2331        // fold chain, so a match whose value is discarded must unwrap too.
2332        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        // Folding the tail `return` exposes the value as the new tail,
2340        // its own tail return folds too, rustc verified equivalent.
2341        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        // rustc verified, label `'a` and `for<'a>` are distinct namespaces,
2349        // the `break` must keep pointing at the label.
2350        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        // rustc verified, `'a` as a label and `'a` as a lifetime parameter
2362        // are separate namespaces, the reference keeps the parameter and
2363        // the break keeps the label.
2364        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        // rustc verified, a parameter named `s` and the alias `s` are
2376        // separate namespaces, the return annotation keeps the alias.
2377        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        // rustc verified, the bound of the local struct resolves through
2385        // the type namespace even with a same-named parameter in scope.
2386        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        // rustc verified, `s` names the alias in type position and the
2408        // const item inside the argument braces.
2409        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        // The alias is printed once, the annotation must carry the very
2430        // canon the use statement declares, twin tests cannot see a split
2431        // because both sides split alike.
2432        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        // A top level value sharing the trait name must not hijack the
2439        // impl trait path, a value-first lookup breaks the twin
2440        // identically on both sides, so the check is that the impl names
2441        // the declared trait.
2442        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        // rustc, verified: with the cfg off the fn falls through to 2,
2468        // folding the return away would change the result.
2469        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        // An attribute on an untyped closure parameter lands on the pattern
2486        // and each kind reaching `strip_pat_inert_attrs` merges.
2487        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        // Only a tree built in code, a proc macro's output for one, attributes these.
2519        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        // rustc reads an async block's `return` as its tail, so this fold is
2552        // sound here as it is in fn and closure bodies.
2553        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        // rustc accepts attributes on arms and inert lint attributes carry no
2561        // program meaning, as expect_attr_merges pins for items.
2562        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        // rustc accepts `fn f(#[...] x: u8)` and the attribute is inert.
2577        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        // rustc accepts let-chains in arm guards and scopes the binder over
2592        // guard and arm body. skills/dejadoc/SKILL.md declares local names
2593        // never separate two bodies.
2594        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        // alpha_renames_closure_higher_ranked_lifetimes pins the closure
2602        // binder. SKILL.md makes no distinction for one in a type.
2603        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        // A fn-pointer argument name is decorative, rustc resolves nothing
2618        // through it, so every spelling names one type.
2619        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    /// Asserts each pair in `pairs` canonicalizes alike.
2629    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    /// Asserts each pair in `pairs` canonicalizes apart.
2636    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            // Under a reference, a nested `mut` resets the binding mode to by-value before 2024.
2691            ("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}