File: statement-boundaries.rs

package info (click to toggle)
rustc 1.85.0%2Bdfsg2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 893,176 kB
  • sloc: xml: 158,127; python: 35,830; javascript: 19,497; cpp: 19,002; sh: 17,245; ansic: 13,127; asm: 4,376; makefile: 1,051; lisp: 29; perl: 29; ruby: 19; sql: 11
file content (104 lines) | stat: -rw-r--r-- 3,160 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//@ run-pass
//@ edition:2021

// This is a test of several uses of rustc_ast::util::classify::expr_requires_semi_to_be_stmt
// by the Rust parser, which relates to the insertion of statement boundaries
// after certain kinds of expressions if they appear at the head of a statement.

#![allow(unused_braces, unused_unsafe)]

macro_rules! unit {
    () => {
        { () }
    };
}

#[derive(Copy, Clone)]
struct X;

fn main() {
    let x = X;

    // There is a statement boundary before `|x| x`, so it's a closure.
    let _: fn(X) -> X = { if true {} |x| x };
    let _: fn(X) -> X = { if true {} else {} |x| x };
    let _: fn(X) -> X = { match () { () => {} } |x| x };
    let _: fn(X) -> X = { { () } |x| x };
    let _: fn(X) -> X = { unsafe {} |x| x };
    let _: fn(X) -> X = { while false {} |x| x };
    let _: fn(X) -> X = { loop { break; } |x| x };
    let _: fn(X) -> X = { for _ in 0..0 {} |x| x };
    let _: fn(X) -> X = { const {} |x| x };
    let _: fn(X) -> X = { unit! {} |x| x };

    // No statement boundary, so `|x| x` is 2× BitOr operation.
    () = { "" |x| x };
    () = { ("") |x| x };
    () = { [""] |x| x };
    () = { unit!() |x| x };
    () = { unit![] |x| x };

    // All the same cases, but as a match arm.
    () = match x {
        // Statement boundary before `| X`, which becomes a new arm with leading vert.
        X if false => if true {} | X if false => {}
        X if false => if true {} else {} | X if false => {}
        X if false => match () { () => {} } | X if false => {}
        X if false => { () } | X if false => {}
        X if false => unsafe {} | X if false => {}
        X if false => while false {} | X if false => {}
        X if false => loop { break; } | X if false => {}
        X if false => for _ in 0..0 {} | X if false => {}
        X if false => const {} | X if false => {}

        // No statement boundary, so `| X` is BitOr.
        X if false => "" | X,
        X if false => ("") | X,
        X if false => [""] | X,
        X if false => unit! {} | X, // !! inconsistent with braced mac call in statement position
        X if false => unit!() | X,
        X if false => unit![] | X,

        X => {}
    };

    // Test how the statement boundary logic interacts with macro metavariables /
    // "invisible delimiters".
    macro_rules! assert_statement_boundary {
        ($expr:expr) => {
            let _: fn(X) -> X = { $expr |x| x };

            () = match X {
                X if false => $expr | X if false => {}
                X => {}
            };
        };
    }
    macro_rules! assert_no_statement_boundary {
        ($expr:expr) => {
            () = { $expr |x| x };

            () = match x {
                X if false => $expr | X,
                X => {}
            };
        };
    }
    assert_statement_boundary!(if true {});
    assert_no_statement_boundary!("");
}

impl std::ops::BitOr<X> for () {
    type Output = ();
    fn bitor(self, _: X) {}
}

impl std::ops::BitOr<X> for &str {
    type Output = ();
    fn bitor(self, _: X) {}
}

impl<T, const N: usize> std::ops::BitOr<X> for [T; N] {
    type Output = ();
    fn bitor(self, _: X) {}
}