Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  test_expr.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#![allow(
    clippy::needless_lifetimes,
    clippy::single_element_loop,
    clippy::uninlined_format_args
)]

#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group};
use quote::{quote, ToTokens as _};
use std::mem;
use syn::punctuated::Punctuated;
use syn::visit_mut::{self, VisitMut};
use syn::{parse_quote, token, Expr, ExprRange, ExprTuple, Stmt, Token};

#[test]
fn test_expr_parse() {
    let tokens = quote!(..100u32);
    snapshot!(tokens as Expr, @r#"
    Expr::Range {
        limits: RangeLimits::HalfOpen,
        end: Some(Expr::Lit {
            lit: 100u32,
        }),
    }
    "#);

    let tokens = quote!(..100u32);
    snapshot!(tokens as ExprRange, @r#"
    ExprRange {
        limits: RangeLimits::HalfOpen,
        end: Some(Expr::Lit {
            lit: 100u32,
        }),
    }
    "#);
}

#[test]
fn test_await() {
    // Must not parse as Expr::Field.
    let tokens = quote!(fut.await);

    snapshot!(tokens as Expr, @r#"
    Expr::Await {
        base: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "fut",
                    },
                ],
            },
        },
    }
    "#);
}

#[rustfmt::skip]
#[test]
fn test_tuple_multi_index() {
    let expected = snapshot!("tuple.0.0" as Expr, @r#"
    Expr::Field {
        base: Expr::Field {
            base: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "tuple",
                        },
                    ],
                },
            },
            member: Member::Unnamed(Index {
                index: 0,
            }),
        },
        member: Member::Unnamed(Index {
            index: 0,
        }),
    }
    "#);

    for &input in &[
        "tuple .0.0",
        "tuple. 0.0",
        "tuple.0 .0",
        "tuple.0. 0",
        "tuple . 0 . 0",
    ] {
        assert_eq!(expected, syn::parse_str(input).unwrap());
    }

    for tokens in [
        quote!(tuple.0.0),
        quote!(tuple .0.0),
        quote!(tuple. 0.0),
        quote!(tuple.0 .0),
        quote!(tuple.0. 0),
        quote!(tuple . 0 . 0),
    ] {
        assert_eq!(expected, syn::parse2(tokens).unwrap());
    }
}

#[test]
fn test_macro_variable_func() {
    // mimics the token stream corresponding to `$fn()`
    let path = Group::new(Delimiter::None, quote!(f));
    let tokens = quote!(#path());

    snapshot!(tokens as Expr, @r#"
    Expr::Call {
        func: Expr::Group {
            expr: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "f",
                        },
                    ],
                },
            },
        },
    }
    "#);

    let path = Group::new(Delimiter::None, quote! { #[inside] f });
    let tokens = quote!(#[outside] #path());

    snapshot!(tokens as Expr, @r#"
    Expr::Call {
        attrs: [
            Attribute {
                style: AttrStyle::Outer,
                meta: Meta::Path {
                    segments: [
                        PathSegment {
                            ident: "outside",
                        },
                    ],
                },
            },
        ],
        func: Expr::Group {
            expr: Expr::Path {
                attrs: [
                    Attribute {
                        style: AttrStyle::Outer,
                        meta: Meta::Path {
                            segments: [
                                PathSegment {
                                    ident: "inside",
                                },
                            ],
                        },
                    },
                ],
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "f",
                        },
                    ],
                },
            },
        },
    }
    "#);
}

#[test]
fn test_macro_variable_macro() {
    // mimics the token stream corresponding to `$macro!()`
    let mac = Group::new(Delimiter::None, quote!(m));
    let tokens = quote!(#mac!());

    snapshot!(tokens as Expr, @r#"
    Expr::Macro {
        mac: Macro {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "m",
                    },
                ],
            },
            delimiter: MacroDelimiter::Paren,
            tokens: TokenStream(``),
        },
    }
    "#);
}

#[test]
fn test_macro_variable_struct() {
    // mimics the token stream corresponding to `$struct {}`
    let s = Group::new(Delimiter::None, quote! { S });
    let tokens = quote!(#s {});

    snapshot!(tokens as Expr, @r#"
    Expr::Struct {
        path: Path {
            segments: [
                PathSegment {
                    ident: "S",
                },
            ],
        },
    }
    "#);
}

#[test]
fn test_macro_variable_unary() {
    // mimics the token stream corresponding to `$expr.method()` where expr is `&self`
    let inner = Group::new(Delimiter::None, quote!(&self));
    let tokens = quote!(#inner.method());
    snapshot!(tokens as Expr, @r#"
    Expr::MethodCall {
        receiver: Expr::Group {
            expr: Expr::Reference {
                expr: Expr::Path {
                    path: Path {
                        segments: [
                            PathSegment {
                                ident: "self",
                            },
                        ],
                    },
                },
            },
        },
        method: "method",
    }
    "#);
}

#[test]
fn test_macro_variable_match_arm() {
    // mimics the token stream corresponding to `match v { _ => $expr }`
    let expr = Group::new(Delimiter::None, quote! { #[a] () });
    let tokens = quote!(match v { _ => #expr });
    snapshot!(tokens as Expr, @r#"
    Expr::Match {
        expr: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "v",
                    },
                ],
            },
        },
        arms: [
            Arm {
                pat: Pat::Wild,
                body: Expr::Group {
                    expr: Expr::Tuple {
                        attrs: [
                            Attribute {
                                style: AttrStyle::Outer,
                                meta: Meta::Path {
                                    segments: [
                                        PathSegment {
                                            ident: "a",
                                        },
                                    ],
                                },
                            },
                        ],
                    },
                },
            },
        ],
    }
    "#);

    let expr = Group::new(Delimiter::None, quote!(loop {} + 1));
    let tokens = quote!(match v { _ => #expr });
    snapshot!(tokens as Expr, @r#"
    Expr::Match {
        expr: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "v",
                    },
                ],
            },
        },
        arms: [
            Arm {
                pat: Pat::Wild,
                body: Expr::Group {
                    expr: Expr::Binary {
                        left: Expr::Loop {
                            body: Block {
                                stmts: [],
                            },
                        },
                        op: BinOp::Add,
                        right: Expr::Lit {
                            lit: 1,
                        },
                    },
                },
            },
        ],
    }
    "#);
}

// https://github.com/dtolnay/syn/issues/1019
#[test]
fn test_closure_vs_rangefull() {
    #[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/4808
    let tokens = quote!(|| .. .method());
    snapshot!(tokens as Expr, @r#"
    Expr::MethodCall {
        receiver: Expr::Closure {
            output: ReturnType::Default,
            body: Expr::Range {
                limits: RangeLimits::HalfOpen,
            },
        },
        method: "method",
    }
    "#);
}

#[test]
fn test_postfix_operator_after_cast() {
    syn::parse_str::<Expr>("|| &x as T[0]").unwrap_err();
    syn::parse_str::<Expr>("|| () as ()()").unwrap_err();
}

#[test]
fn test_range_kinds() {
    syn::parse_str::<Expr>("..").unwrap();
    syn::parse_str::<Expr>("..hi").unwrap();
    syn::parse_str::<Expr>("lo..").unwrap();
    syn::parse_str::<Expr>("lo..hi").unwrap();

    syn::parse_str::<Expr>("..=").unwrap_err();
    syn::parse_str::<Expr>("..=hi").unwrap();
    syn::parse_str::<Expr>("lo..=").unwrap_err();
    syn::parse_str::<Expr>("lo..=hi").unwrap();

    syn::parse_str::<Expr>("...").unwrap_err();
    syn::parse_str::<Expr>("...hi").unwrap_err();
    syn::parse_str::<Expr>("lo...").unwrap_err();
    syn::parse_str::<Expr>("lo...hi").unwrap_err();
}

#[test]
fn test_range_precedence() {
    snapshot!(".. .." as Expr, @r#"
    Expr::Range {
        limits: RangeLimits::HalfOpen,
        end: Some(Expr::Range {
            limits: RangeLimits::HalfOpen,
        }),
    }
    "#);

    snapshot!(".. .. ()" as Expr, @r#"
    Expr::Range {
        limits: RangeLimits::HalfOpen,
        end: Some(Expr::Range {
            limits: RangeLimits::HalfOpen,
            end: Some(Expr::Tuple),
        }),
    }
    "#);

    snapshot!("() .. .." as Expr, @r#"
    Expr::Range {
        start: Some(Expr::Tuple),
        limits: RangeLimits::HalfOpen,
        end: Some(Expr::Range {
            limits: RangeLimits::HalfOpen,
        }),
    }
    "#);

    // A range with a lower bound cannot be the upper bound of another range,
    // and a range with an upper bound cannot be the lower bound of another
    // range.
    syn::parse_str::<Expr>(".. x ..").unwrap_err();
    syn::parse_str::<Expr>("x .. x ..").unwrap_err();
}

#[test]
fn test_ambiguous_label() {
    for stmt in [
        quote! {
            return 'label: loop { break 'label 42; };
        },
        quote! {
            break ('label: loop { break 'label 42; });
        },
        quote! {
            break 1 + 'label: loop { break 'label 42; };
        },
        quote! {
            break 'outer 'inner: loop { break 'inner 42; };
        },
    ] {
        syn::parse2::<Stmt>(stmt).unwrap();
    }

    for stmt in [
        // Parentheses required. See https://github.com/rust-lang/rust/pull/87026.
        quote! {
            break 'label: loop { break 'label 42; };
        },
    ] {
        syn::parse2::<Stmt>(stmt).unwrap_err();
    }
}

#[test]
fn test_extended_interpolated_path() {
    let path = Group::new(Delimiter::None, quote!(a::b));

    let tokens = quote!(if #path {});
    snapshot!(tokens as Expr, @r#"
    Expr::If {
        cond: Expr::Group {
            expr: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "a",
                        },
                        Token![::],
                        PathSegment {
                            ident: "b",
                        },
                    ],
                },
            },
        },
        then_branch: Block {
            stmts: [],
        },
    }
    "#);

    let tokens = quote!(#path {});
    snapshot!(tokens as Expr, @r#"
    Expr::Struct {
        path: Path {
            segments: [
                PathSegment {
                    ident: "a",
                },
                Token![::],
                PathSegment {
                    ident: "b",
                },
            ],
        },
    }
    "#);

    let tokens = quote!(#path :: c);
    snapshot!(tokens as Expr, @r#"
    Expr::Path {
        path: Path {
            segments: [
                PathSegment {
                    ident: "a",
                },
                Token![::],
                PathSegment {
                    ident: "b",
                },
                Token![::],
                PathSegment {
                    ident: "c",
                },
            ],
        },
    }
    "#);

    let nested = Group::new(Delimiter::None, quote!(a::b || true));
    let tokens = quote!(if #nested && false {});
    snapshot!(tokens as Expr, @r#"
    Expr::If {
        cond: Expr::Binary {
            left: Expr::Group {
                expr: Expr::Binary {
                    left: Expr::Path {
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "a",
                                },
                                Token![::],
                                PathSegment {
                                    ident: "b",
                                },
                            ],
                        },
                    },
                    op: BinOp::Or,
                    right: Expr::Lit {
                        lit: Lit::Bool {
                            value: true,
                        },
                    },
                },
            },
            op: BinOp::And,
            right: Expr::Lit {
                lit: Lit::Bool {
                    value: false,
                },
            },
        },
        then_branch: Block {
            stmts: [],
        },
    }
    "#);
}

#[test]
fn test_tuple_comma() {
    let mut expr = ExprTuple {
        attrs: Vec::new(),
        paren_token: token::Paren::default(),
        elems: Punctuated::new(),
    };
    snapshot!(expr.to_token_stream() as Expr, @"Expr::Tuple");

    expr.elems.push_value(parse_quote!(continue));
    // Must not parse to Expr::Paren
    snapshot!(expr.to_token_stream() as Expr, @r#"
    Expr::Tuple {
        elems: [
            Expr::Continue,
            Token![,],
        ],
    }
    "#);

    expr.elems.push_punct(<Token![,]>::default());
    snapshot!(expr.to_token_stream() as Expr, @r#"
    Expr::Tuple {
        elems: [
            Expr::Continue,
            Token![,],
        ],
    }
    "#);

    expr.elems.push_value(parse_quote!(continue));
    snapshot!(expr.to_token_stream() as Expr, @r#"
    Expr::Tuple {
        elems: [
            Expr::Continue,
            Token![,],
            Expr::Continue,
        ],
    }
    "#);

    expr.elems.push_punct(<Token![,]>::default());
    snapshot!(expr.to_token_stream() as Expr, @r#"
    Expr::Tuple {
        elems: [
            Expr::Continue,
            Token![,],
            Expr::Continue,
            Token![,],
        ],
    }
    "#);
}

#[test]
fn test_binop_associativity() {
    // Left to right.
    snapshot!("() + () + ()" as Expr, @r#"
    Expr::Binary {
        left: Expr::Binary {
            left: Expr::Tuple,
            op: BinOp::Add,
            right: Expr::Tuple,
        },
        op: BinOp::Add,
        right: Expr::Tuple,
    }
    "#);

    // Right to left.
    snapshot!("() += () += ()" as Expr, @r#"
    Expr::Binary {
        left: Expr::Tuple,
        op: BinOp::AddAssign,
        right: Expr::Binary {
            left: Expr::Tuple,
            op: BinOp::AddAssign,
            right: Expr::Tuple,
        },
    }
    "#);

    // Parenthesization is required.
    syn::parse_str::<Expr>("() == () == ()").unwrap_err();
}

#[test]
fn test_assign_range_precedence() {
    // Range has higher precedence as the right-hand of an assignment, but
    // ambiguous precedence as the left-hand of an assignment.
    snapshot!("() = () .. ()" as Expr, @r#"
    Expr::Assign {
        left: Expr::Tuple,
        right: Expr::Range {
            start: Some(Expr::Tuple),
            limits: RangeLimits::HalfOpen,
            end: Some(Expr::Tuple),
        },
    }
    "#);

    snapshot!("() += () .. ()" as Expr, @r#"
    Expr::Binary {
        left: Expr::Tuple,
        op: BinOp::AddAssign,
        right: Expr::Range {
            start: Some(Expr::Tuple),
            limits: RangeLimits::HalfOpen,
            end: Some(Expr::Tuple),
        },
    }
    "#);

    syn::parse_str::<Expr>("() .. () = ()").unwrap_err();
    syn::parse_str::<Expr>("() .. () += ()").unwrap_err();
}

#[test]
fn test_chained_comparison() {
    // https://github.com/dtolnay/syn/issues/1738
    let _ = syn::parse_str::<Expr>("a = a < a <");
    let _ = syn::parse_str::<Expr>("a = a .. a ..");
    let _ = syn::parse_str::<Expr>("a = a .. a +=");

    let err = syn::parse_str::<Expr>("a < a < a").unwrap_err();
    assert_eq!("comparison operators cannot be chained", err.to_string());

    let err = syn::parse_str::<Expr>("a .. a .. a").unwrap_err();
    assert_eq!("unexpected token", err.to_string());

    let err = syn::parse_str::<Expr>("a .. a += a").unwrap_err();
    assert_eq!("unexpected token", err.to_string());
}

#[test]
fn test_fixup() {
    struct FlattenParens;

    impl VisitMut for FlattenParens {
        fn visit_expr_mut(&mut self, e: &mut Expr) {
            while let Expr::Paren(paren) = e {
                *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER);
            }
            visit_mut::visit_expr_mut(self, e);
        }
    }

    for tokens in [
        quote! { 2 * (1 + 1) },
        quote! { 0 + (0 + 0) },
        quote! { (a = b) = c },
        quote! { (x as i32) < 0 },
        quote! { 1 + (x as i32) < 0 },
        quote! { (1 + 1).abs() },
        quote! { (lo..hi)[..] },
        quote! { (a..b)..(c..d) },
        quote! { (&mut fut).await },
        quote! { &mut (x as i32) },
        quote! { -(x as i32) },
        quote! { if (S {} == 1) {} },
        quote! { { (m! {}) - 1 } },
        quote! { match m { _ => ({}) - 1 } },
        quote! { if let _ = (a && b) && c {} },
        quote! { if let _ = (S {}) {} },
        quote! { break ('a: loop { break 'a 1 } + 1) },
        quote! { a + (|| b) + c },
        quote! { if let _ = ((break) - 1 || true) {} },
        quote! { if let _ = (break + 1 || true) {} },
        quote! { (break)() },
        quote! { (..) = () },
        quote! { (..) += () },
        quote! { (1 < 2) == (3 < 4) },
        quote! { { (let _ = ()) } },
    ] {
        let original: Expr = syn::parse2(tokens).unwrap();

        let mut flat = original.clone();
        FlattenParens.visit_expr_mut(&mut flat);
        let reconstructed: Expr = match syn::parse2(flat.to_token_stream()) {
            Ok(reconstructed) => reconstructed,
            Err(err) => panic!("failed to parse `{}`: {}", flat.to_token_stream(), err),
        };

        assert!(
            original == reconstructed,
            "original: {}\nreconstructed: {}",
            original.to_token_stream(),
            reconstructed.to_token_stream(),
        );
    }
}

[Dauer der Verarbeitung: 0.6 Sekunden, vorverarbeitet 2026-04-26]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge