//! Completion tests for expressions.
use expect_test::{expect, Expect};

use crate::{
    config::AutoImportExclusionType,
    tests::{
        check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE,
        TEST_CONFIG,
    },
    CompletionConfig,
};

fn check(ra_fixture: &str, expect: Expect) {
    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
    expect.assert_eq(&actual)
}

fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) {
    let actual = completion_list_with_config(
        config,
        &format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"),
        true,
        None,
    );
    expect.assert_eq(&actual)
}

#[test]
fn complete_literal_struct_with_a_private_field() {
    // `FooDesc.bar` is private, the completion should not be triggered.
    check(
        r#"
mod _69latrick {
    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, bar: bool }
    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
}

fn baz() {
    use _69latrick::*;

    let foo = create_foo(&$0);
}
            "#,
        // This should not contain `FooDesc {…}`.
        expect![[r#"
            ct CONST                   Unit
            en Enum                    Enum
            fn baz()                   fn()
            fn create_foo(…)   fn(&FooDesc)
            fn function()              fn()
            ma makro!(…) macro_rules! makro
            md _69latrick
            md module
            sc STATIC                  Unit
            st FooDesc              FooDesc
            st Record                Record
            st Tuple                  Tuple
            st Unit                    Unit
            un Union                  Union
            ev TupleV(…)        TupleV(u32)
            bt u32                      u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw mut
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    )
}

#[test]
fn completes_various_bindings() {
    check_empty(
        r#"
fn func(param0 @ (param1, param2): (i32, i32)) {
    let letlocal = 92;
    if let ifletlocal = 100 {
        match 0 {
            matcharm => 1 + $0,
            otherwise => (),
        }
    }
    let letlocal2 = 44;
}
"#,
        expect![[r#"
            fn func(…) fn((i32, i32))
            lc ifletlocal         i32
            lc letlocal           i32
            lc matcharm           i32
            lc param0      (i32, i32)
            lc param1             i32
            lc param2             i32
            bt u32                u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
            ex ifletlocal
            ex letlocal
            ex matcharm
            ex param1
            ex param2
        "#]],
    );
}

#[test]
fn completes_all_the_things_in_fn_body() {
    check(
        r#"
use non_existent::Unresolved;
mod qualified { pub enum Enum { Variant } }

impl Unit {
    fn foo<'lifetime, TypeParam, const CONST_PARAM: usize>(self) {
        fn local_func() {}
        $0
    }
}
"#,
        // `self` is in here twice, once as the module, once as the local
        expect![[r#"
            ct CONST                   Unit
            cp CONST_PARAM
            en Enum                    Enum
            fn function()              fn()
            fn local_func()            fn()
            me self.foo()          fn(self)
            lc self                    Unit
            ma makro!(…) macro_rules! makro
            md module
            md qualified
            sp Self                    Unit
            sc STATIC                  Unit
            st Record                Record
            st Tuple                  Tuple
            st Unit                    Unit
            tp TypeParam
            un Union                  Union
            ev TupleV(…)        TupleV(u32)
            bt u32                      u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
            ?? Unresolved
        "#]],
    );
    check(
        r#"
use non_existent::Unresolved;
mod qualified { pub enum Enum { Variant } }

impl Unit {
    fn foo<'lifetime, TypeParam, const CONST_PARAM: usize>(self) {
        fn local_func() {}
        self::$0
    }
}
"#,
        expect![[r#"
            ct CONST                   Unit
            en Enum                    Enum
            fn function()              fn()
            ma makro!(…) macro_rules! makro
            md module
            md qualified
            sc STATIC                  Unit
            st Record                Record
            st Tuple                  Tuple
            st Unit                    Unit
            tt Trait
            un Union                  Union
            ev TupleV(…)        TupleV(u32)
            ?? Unresolved
        "#]],
    );
}

#[test]
fn complete_in_block() {
    check_empty(
        r#"
    fn foo() {
        if true {
            $0
        }
    }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
            ex false
            ex true
        "#]],
    )
}

#[test]
fn complete_after_if_expr() {
    check_empty(
        r#"
    fn foo() {
        if true {}
        $0
    }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw else
            kw else if
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    )
}

#[test]
fn complete_in_match_arm() {
    check_empty(
        r#"
    fn foo() {
        match () {
            () => $0
        }
    }
"#,
        expect![[r#"
            fn foo() fn()
            bt u32    u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    )
}

#[test]
fn completes_in_loop_ctx() {
    check_empty(
        r"fn my() { loop { $0 } }",
        expect![[r#"
            fn my()   fn()
            bt u32     u32
            kw async
            kw break
            kw const
            kw continue
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
    check_empty(
        r"fn my() { loop { foo.$0 } }",
        expect![[r#"
            sn box  Box::new(expr)
            sn break    break expr
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn if       if expr {}
            sn let             let
            sn letm        let mut
            sn match match expr {}
            sn not           !expr
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
            sn while while expr {}
        "#]],
    );
}

#[test]
fn completes_in_let_initializer() {
    check_empty(
        r#"fn main() { let _ = $0 }"#,
        expect![[r#"
            fn main() fn()
            bt u32     u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    )
}

#[test]
fn struct_initializer_field_expr() {
    check_empty(
        r#"
struct Foo {
    pub f: i32,
}
fn foo() {
    Foo {
        f: $0
    }
}
"#,
        expect![[r#"
            fn foo() fn()
            st Foo    Foo
            bt u32    u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
}

#[test]
fn shadowing_shows_single_completion() {
    cov_mark::check!(shadowing_shows_single_completion);

    check_empty(
        r#"
fn foo() {
    let bar = 92;
    {
        let bar = 62;
        drop($0)
    }
}
"#,
        expect![[r#"
            fn foo() fn()
            lc bar    i32
            bt u32    u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
}

#[test]
fn in_macro_expr_frag() {
    check_empty(
        r#"
macro_rules! m { ($e:expr) => { $e } }
fn quux(x: i32) {
    m!($0);
}
"#,
        expect![[r#"
            fn quux(…)      fn(i32)
            lc x                i32
            ma m!(…) macro_rules! m
            bt u32              u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
    check_empty(
        r"
macro_rules! m { ($e:expr) => { $e } }
fn quux(x: i32) {
    m!(x$0);
}
",
        expect![[r#"
            fn quux(…)      fn(i32)
            lc x                i32
            ma m!(…) macro_rules! m
            bt u32              u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
    check_empty(
        r#"
macro_rules! m { ($e:expr) => { $e } }
fn quux(x: i32) {
    let y = 92;
    m!(x$0
}
"#,
        expect![[r#"
            fn quux(…)      fn(i32)
            lc x                i32
            lc y                i32
            ma m!(…) macro_rules! m
            bt u32              u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
}

#[test]
fn enum_qualified() {
    check(
        r#"
impl Enum {
    type AssocType = ();
    const ASSOC_CONST: () = ();
    fn assoc_fn() {}
}
fn func() {
    Enum::$0
}
"#,
        expect![[r#"
            ct ASSOC_CONST  const ASSOC_CONST: ()
            fn assoc_fn()                    fn()
            ta AssocType      type AssocType = ()
            ev RecordV {…} RecordV { field: u32 }
            ev TupleV(…)              TupleV(u32)
            ev UnitV                        UnitV
        "#]],
    );
}

#[test]
fn ty_qualified_no_drop() {
    check_empty(
        r#"
//- minicore: drop
struct Foo;
impl Drop for Foo {
    fn drop(&mut self) {}
}
fn func() {
    Foo::$0
}
"#,
        expect![[r#""#]],
    );
}

#[test]
fn with_parens() {
    check_empty(
        r#"
enum Enum {
    Variant()
}
impl Enum {
    fn variant() -> Self { Enum::Variant() }
}
fn func() {
    Enum::$0()
}
"#,
        expect![[r#"
            fn variant fn() -> Enum
            ev Variant      Variant
        "#]],
    );
}

#[test]
fn detail_impl_trait_in_return_position() {
    check_empty(
        r"
//- minicore: sized
trait Trait<T> {}
fn foo<U>() -> impl Trait<U> {}
fn main() {
    self::$0
}
",
        expect![[r#"
            fn foo() fn() -> impl Trait<U>
            fn main()                 fn()
            tt Trait
        "#]],
    );
}

#[test]
fn detail_async_fn() {
    check_empty(
        r#"
//- minicore: future, sized
trait Trait<T> {}
async fn foo() -> u8 {}
async fn bar<U>() -> impl Trait<U> {}
fn main() {
    self::$0
}
"#,
        expect![[r#"
            fn bar() async fn() -> impl Trait<U>
            fn foo()            async fn() -> u8
            fn main()                       fn()
            tt Trait
        "#]],
    );
}

#[test]
fn detail_impl_trait_in_argument_position() {
    check_empty(
        r"
//- minicore: sized
trait Trait<T> {}
struct Foo;
impl Foo {
    fn bar<U>(_: impl Trait<U>) {}
}
fn main() {
    Foo::$0
}
",
        expect![[r#"
            fn bar(…) fn(impl Trait<U>)
        "#]],
    );
}

#[test]
fn complete_record_expr_path() {
    check(
        r#"
struct Zulu;
impl Zulu {
    fn test() -> Self { }
}
fn boi(val: Zulu) { }
fn main() {
    boi(Zulu:: $0 {});
}
"#,
        expect![[r#"
            fn test() fn() -> Zulu
            ex Zulu
            ex Zulu::test()
        "#]],
    );
}

#[test]
fn variant_with_struct() {
    check_empty(
        r#"
pub struct YoloVariant {
    pub f: usize
}

pub enum HH {
    Yolo(YoloVariant),
}

fn brr() {
    let t = HH::Yolo(Y$0);
}
"#,
        expect![[r#"
            en HH                                    HH
            fn brr()                               fn()
            st YoloVariant                  YoloVariant
            st YoloVariant {…} YoloVariant { f: usize }
            bt u32                                  u32
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
}

#[test]
fn return_unit_block() {
    cov_mark::check!(return_unit_block);
    check_edit("return", r#"fn f() { if true { $0 } }"#, r#"fn f() { if true { return; } }"#);
}

#[test]
fn return_unit_no_block() {
    cov_mark::check!(return_unit_no_block);
    check_edit(
        "return",
        r#"fn f() { match () { () => $0 } }"#,
        r#"fn f() { match () { () => return } }"#,
    );
}

#[test]
fn return_value_block() {
    cov_mark::check!(return_value_block);
    check_edit(
        "return",
        r#"fn f() -> i32 { if true { $0 } }"#,
        r#"fn f() -> i32 { if true { return $0; } }"#,
    );
}

#[test]
fn return_value_no_block() {
    cov_mark::check!(return_value_no_block);
    check_edit(
        "return",
        r#"fn f() -> i32 { match () { () => $0 } }"#,
        r#"fn f() -> i32 { match () { () => return $0 } }"#,
    );
}

#[test]
fn else_completion_after_if() {
    check_empty(
        r#"
fn foo() { if foo {} $0 }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw else
            kw else if
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
    check_empty(
        r#"
fn foo() { if foo {} el$0 }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw else
            kw else if
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
    check_empty(
        r#"
fn foo() { bar(if foo {} $0) }
"#,
        expect![[r#"
            fn foo() fn()
            bt u32    u32
            kw crate::
            kw else
            kw else if
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
    check_empty(
        r#"
fn foo() { bar(if foo {} el$0) }
"#,
        expect![[r#"
            fn foo() fn()
            bt u32    u32
            kw crate::
            kw else
            kw else if
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
    check_empty(
        r#"
fn foo() { if foo {} $0 let x = 92; }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw else
            kw else if
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
    check_empty(
        r#"
fn foo() { if foo {} el$0 let x = 92; }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw else
            kw else if
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
    check_empty(
        r#"
fn foo() { if foo {} el$0 { let x = 92; } }
"#,
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw else
            kw else if
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn expr_no_unstable_item_on_stable() {
    check_empty(
        r#"
//- /main.rs crate:main deps:std
use std::*;
fn main() {
    $0
}
//- /std.rs crate:std
#[unstable]
pub struct UnstableThisShouldNotBeListed;
"#,
        expect![[r#"
            fn main() fn()
            md std
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn expr_unstable_item_on_nightly() {
    check_empty(
        r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
use std::*;
fn main() {
    $0
}
//- /std.rs crate:std
#[unstable]
pub struct UnstableButWeAreOnNightlyAnyway;
"#,
        expect![[r#"
            fn main()                                                     fn()
            md std
            st UnstableButWeAreOnNightlyAnyway UnstableButWeAreOnNightlyAnyway
            bt u32                                                         u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn inside_format_args_completions_work() {
    check_empty(
        r#"
//- minicore: fmt
struct Foo;
impl Foo {
    fn foo(&self) {}
}

fn main() {
    format_args!("{}", Foo.$0);
}
"#,
        expect![[r#"
            me foo()     fn(&self)
            sn box  Box::new(expr)
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn match match expr {}
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
        "#]],
    );
    check_empty(
        r#"
//- minicore: fmt
struct Foo;
impl Foo {
    fn foo(&self) {}
}

fn main() {
    format_args!("{}", Foo.f$0);
}
"#,
        expect![[r#"
            me foo()     fn(&self)
            sn box  Box::new(expr)
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn match match expr {}
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
        "#]],
    );
}

#[test]
fn inside_faulty_format_args_completions_work() {
    check_empty(
        r#"
//- minicore: fmt
struct Foo;
impl Foo {
    fn foo(&self) {}
}

fn main() {
    format_args!("", Foo.$0);
}
"#,
        expect![[r#"
            me foo()     fn(&self)
            sn box  Box::new(expr)
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn match match expr {}
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
        "#]],
    );
    check_empty(
        r#"
//- minicore: fmt
struct Foo;
impl Foo {
    fn foo(&self) {}
}

fn main() {
    format_args!("", Foo.f$0);
}
"#,
        expect![[r#"
            me foo()     fn(&self)
            sn box  Box::new(expr)
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn match match expr {}
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
        "#]],
    );
    check_empty(
        r#"
//- minicore: fmt
struct Foo;
impl Foo {
    fn foo(&self) {}
}

fn main() {
    format_args!("{} {named} {captured} {named} {}", a, named = c, Foo.f$0);
}
"#,
        expect![[r#"
            me foo()     fn(&self)
            sn box  Box::new(expr)
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn match match expr {}
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
        "#]],
    );
    check_empty(
        r#"
//- minicore: fmt
struct Foo;
impl Foo {
    fn foo(&self) {}
}

fn main() {
    format_args!("{", Foo.f$0);
}
"#,
        expect![[r#"
            sn box  Box::new(expr)
            sn call function(expr)
            sn dbg      dbg!(expr)
            sn dbgr    dbg!(&expr)
            sn deref         *expr
            sn if       if expr {}
            sn match match expr {}
            sn not           !expr
            sn ref           &expr
            sn refm      &mut expr
            sn return  return expr
            sn unsafe    unsafe {}
            sn while while expr {}
        "#]],
    );
}

#[test]
fn macro_that_ignores_completion_marker() {
    check(
        r#"
macro_rules! helper {
    ($v:ident) => {};
}

macro_rules! m {
    ($v:ident) => {{
        helper!($v);
        $v
    }};
}

fn main() {
    let variable = "test";
    m!(v$0);
}
    "#,
        expect![[r#"
            ct CONST                     Unit
            en Enum                      Enum
            fn function()                fn()
            fn main()                    fn()
            lc variable                  &str
            ma helper!(…) macro_rules! helper
            ma m!(…)           macro_rules! m
            ma makro!(…)   macro_rules! makro
            md module
            sc STATIC                    Unit
            st Record                  Record
            st Tuple                    Tuple
            st Unit                      Unit
            un Union                    Union
            ev TupleV(…)          TupleV(u32)
            bt u32                        u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn excluded_trait_method_is_excluded() {
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

struct Foo;
impl Foo {
    fn inherent(&self) {}
}

fn foo() {
    Foo.$0
}
        "#,
        expect![[r#"
            me bar() (as ExcludedTrait) fn(&self)
            me baz() (as ExcludedTrait) fn(&self)
            me foo() (as ExcludedTrait) fn(&self)
            me inherent()               fn(&self)
            sn box                 Box::new(expr)
            sn call                function(expr)
            sn dbg                     dbg!(expr)
            sn dbgr                   dbg!(&expr)
            sn deref                        *expr
            sn let                            let
            sn letm                       let mut
            sn match                match expr {}
            sn ref                          &expr
            sn refm                     &mut expr
            sn return                 return expr
            sn unsafe                   unsafe {}
        "#]],
    );
}

#[test]
fn excluded_trait_not_excluded_when_inherent() {
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

fn foo(v: &dyn ExcludedTrait) {
    v.$0
}
        "#,
        expect![[r#"
            me bar() (as ExcludedTrait) fn(&self)
            me baz() (as ExcludedTrait) fn(&self)
            me foo() (as ExcludedTrait) fn(&self)
            sn box                 Box::new(expr)
            sn call                function(expr)
            sn dbg                     dbg!(expr)
            sn dbgr                   dbg!(&expr)
            sn deref                        *expr
            sn let                            let
            sn letm                       let mut
            sn match                match expr {}
            sn ref                          &expr
            sn refm                     &mut expr
            sn return                 return expr
            sn unsafe                   unsafe {}
        "#]],
    );
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

fn foo(v: impl ExcludedTrait) {
    v.$0
}
        "#,
        expect![[r#"
            me bar() (as ExcludedTrait) fn(&self)
            me baz() (as ExcludedTrait) fn(&self)
            me foo() (as ExcludedTrait) fn(&self)
            sn box                 Box::new(expr)
            sn call                function(expr)
            sn dbg                     dbg!(expr)
            sn dbgr                   dbg!(&expr)
            sn deref                        *expr
            sn let                            let
            sn letm                       let mut
            sn match                match expr {}
            sn ref                          &expr
            sn refm                     &mut expr
            sn return                 return expr
            sn unsafe                   unsafe {}
        "#]],
    );
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

fn foo<T: ExcludedTrait>(v: T) {
    v.$0
}
        "#,
        expect![[r#"
            me bar() (as ExcludedTrait) fn(&self)
            me baz() (as ExcludedTrait) fn(&self)
            me foo() (as ExcludedTrait) fn(&self)
            sn box                 Box::new(expr)
            sn call                function(expr)
            sn dbg                     dbg!(expr)
            sn dbgr                   dbg!(&expr)
            sn deref                        *expr
            sn let                            let
            sn letm                       let mut
            sn match                match expr {}
            sn ref                          &expr
            sn refm                     &mut expr
            sn return                 return expr
            sn unsafe                   unsafe {}
        "#]],
    );
}

#[test]
fn excluded_trait_method_is_excluded_from_flyimport() {
    check_with_config(
        CompletionConfig {
            exclude_traits: &["test::module2::ExcludedTrait".to_owned()],
            ..TEST_CONFIG
        },
        r#"
mod module2 {
    pub trait ExcludedTrait {
        fn foo(&self) {}
        fn bar(&self) {}
        fn baz(&self) {}
    }

    impl<T> ExcludedTrait for T {}
}

struct Foo;
impl Foo {
    fn inherent(&self) {}
}

fn foo() {
    Foo.$0
}
        "#,
        expect![[r#"
            me bar() (use module2::ExcludedTrait) fn(&self)
            me baz() (use module2::ExcludedTrait) fn(&self)
            me foo() (use module2::ExcludedTrait) fn(&self)
            me inherent()                         fn(&self)
            sn box                           Box::new(expr)
            sn call                          function(expr)
            sn dbg                               dbg!(expr)
            sn dbgr                             dbg!(&expr)
            sn deref                                  *expr
            sn let                                      let
            sn letm                                 let mut
            sn match                          match expr {}
            sn ref                                    &expr
            sn refm                               &mut expr
            sn return                           return expr
            sn unsafe                             unsafe {}
        "#]],
    );
}

#[test]
fn flyimport_excluded_trait_method_is_excluded_from_flyimport() {
    check_with_config(
        CompletionConfig {
            exclude_flyimport: vec![(
                "test::module2::ExcludedTrait".to_owned(),
                AutoImportExclusionType::Methods,
            )],
            ..TEST_CONFIG
        },
        r#"
mod module2 {
    pub trait ExcludedTrait {
        fn foo(&self) {}
        fn bar(&self) {}
        fn baz(&self) {}
    }

    impl<T> ExcludedTrait for T {}
}

struct Foo;
impl Foo {
    fn inherent(&self) {}
}

fn foo() {
    Foo.$0
}
        "#,
        expect![[r#"
            me bar() (use module2::ExcludedTrait) fn(&self)
            me baz() (use module2::ExcludedTrait) fn(&self)
            me foo() (use module2::ExcludedTrait) fn(&self)
            me inherent()                         fn(&self)
            sn box                           Box::new(expr)
            sn call                          function(expr)
            sn dbg                               dbg!(expr)
            sn dbgr                             dbg!(&expr)
            sn deref                                  *expr
            sn let                                      let
            sn letm                                 let mut
            sn match                          match expr {}
            sn ref                                    &expr
            sn refm                               &mut expr
            sn return                           return expr
            sn unsafe                             unsafe {}
        "#]],
    );
}

#[test]
fn excluded_trait_method_is_excluded_from_path_completion() {
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
pub trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

struct Foo;
impl Foo {
    fn inherent(&self) {}
}

fn foo() {
    Foo::$0
}
        "#,
        expect![[r#"
            me bar(…) (as ExcludedTrait) fn(&self)
            me baz(…) (as ExcludedTrait) fn(&self)
            me foo(…) (as ExcludedTrait) fn(&self)
            me inherent(…)               fn(&self)
        "#]],
    );
}

#[test]
fn excluded_trait_method_is_not_excluded_when_trait_is_specified() {
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
pub trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

struct Foo;
impl Foo {
    fn inherent(&self) {}
}

fn foo() {
    ExcludedTrait::$0
}
        "#,
        expect![[r#"
                me bar(…) (as ExcludedTrait) fn(&self)
                me baz(…) (as ExcludedTrait) fn(&self)
                me foo(…) (as ExcludedTrait) fn(&self)
            "#]],
    );
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
pub trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

struct Foo;
impl Foo {
    fn inherent(&self) {}
}

fn foo() {
    <Foo as ExcludedTrait>::$0
}
        "#,
        expect![[r#"
                me bar(…) (as ExcludedTrait) fn(&self)
                me baz(…) (as ExcludedTrait) fn(&self)
                me foo(…) (as ExcludedTrait) fn(&self)
            "#]],
    );
}

#[test]
fn excluded_trait_not_excluded_when_inherent_path() {
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

fn foo() {
    <dyn ExcludedTrait>::$0
}
        "#,
        expect![[r#"
            me bar(…) (as ExcludedTrait) fn(&self)
            me baz(…) (as ExcludedTrait) fn(&self)
            me foo(…) (as ExcludedTrait) fn(&self)
        "#]],
    );
    check_with_config(
        CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG },
        r#"
trait ExcludedTrait {
    fn foo(&self) {}
    fn bar(&self) {}
    fn baz(&self) {}
}

impl<T> ExcludedTrait for T {}

fn foo<T: ExcludedTrait>() {
    T::$0
}
        "#,
        expect![[r#"
            me bar(…) (as ExcludedTrait) fn(&self)
            me baz(…) (as ExcludedTrait) fn(&self)
            me foo(…) (as ExcludedTrait) fn(&self)
        "#]],
    );
}

#[test]
fn hide_ragennew_synthetic_identifiers() {
    check_empty(
        r#"
//- minicore: iterator
fn bar() {
    for i in [0; 10] {
        r$0
    }
}
        "#,
        expect![[r#"
            en Option                             Option<{unknown}>
            en Result                  Result<{unknown}, {unknown}>
            fn bar()                                           fn()
            lc i                                                i32
            ma const_format_args!(…) macro_rules! const_format_args
            ma format_args!(…)             macro_rules! format_args
            ma format_args_nl!(…)       macro_rules! format_args_nl
            ma panic!(…)                         macro_rules! panic
            ma print!(…)                         macro_rules! print
            md core
            md result (use core::result)
            md rust_2015 (use core::prelude::rust_2015)
            md rust_2018 (use core::prelude::rust_2018)
            md rust_2021 (use core::prelude::rust_2021)
            tt Clone
            tt Copy
            tt IntoIterator
            tt Iterator
            ta Result (use core::fmt::Result)
            ev Err(…)                                        Err(E)
            ev None                                            None
            ev Ok(…)                                          Ok(T)
            ev Some(…)                                      Some(T)
            bt u32                                              u32
            kw async
            kw break
            kw const
            kw continue
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw let
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}
