use std::{iter, mem::discriminant};

use crate::{
    doc_links::token_as_doc_comment,
    navigation_target::{self, ToNav},
    FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
};
use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
use ide_db::{
    base_db::{AnchoredPath, FileLoader, SourceDatabase},
    defs::{Definition, IdentClass},
    helpers::pick_best_token,
    RootDatabase, SymbolKind,
};
use itertools::Itertools;

use span::{Edition, FileId};
use syntax::{
    ast::{self, HasLoopBody},
    match_ast, AstNode, AstToken,
    SyntaxKind::*,
    SyntaxNode, SyntaxToken, TextRange, T,
};

// Feature: Go to Definition
//
// Navigates to the definition of an identifier.
//
// For outline modules, this will navigate to the source file of the module.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[F12]
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
pub(crate) fn goto_definition(
    db: &RootDatabase,
    FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = &Semantics::new(db);
    let file = sema.parse_guess_edition(file_id).syntax().clone();
    let edition =
        sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT
        | INT_NUMBER
        | LIFETIME_IDENT
        | T![self]
        | T![super]
        | T![crate]
        | T![Self]
        | COMMENT => 4,
        // index and prefix ops
        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
        kind if kind.is_keyword(edition) => 2,
        T!['('] | T![')'] => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;
    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| {
            let nav = def.try_to_nav(db)?;
            Some(RangeInfo::new(link_range, nav.collect()))
        });
    }

    if let Some((range, resolution)) =
        sema.check_for_format_args_template(original_token.clone(), offset)
    {
        return Some(RangeInfo::new(
            range,
            match resolution {
                Some(res) => def_to_nav(db, Definition::from(res)),
                None => vec![],
            },
        ));
    }

    if let Some(navs) = handle_control_flow_keywords(sema, &original_token) {
        return Some(RangeInfo::new(original_token.text_range(), navs));
    }

    let navs = sema
        .descend_into_macros(original_token.clone())
        .into_iter()
        .filter_map(|token| {
            let parent = token.parent()?;

            if let Some(token) = ast::String::cast(token.clone()) {
                if let Some(x) = try_lookup_include_path(sema, token, file_id) {
                    return Some(vec![x]);
                }
            }

            if ast::TokenTree::can_cast(parent.kind()) {
                if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) {
                    return Some(vec![x]);
                }
            }
            Some(
                IdentClass::classify_node(sema, &parent)?
                    .definitions()
                    .into_iter()
                    .flat_map(|def| {
                        if let Definition::ExternCrateDecl(crate_def) = def {
                            return crate_def
                                .resolved_crate(db)
                                .map(|it| it.root_module().to_nav(sema.db))
                                .into_iter()
                                .flatten()
                                .collect();
                        }
                        try_filter_trait_item_definition(sema, &def)
                            .unwrap_or_else(|| def_to_nav(sema.db, def))
                    })
                    .collect(),
            )
        })
        .flatten()
        .unique()
        .collect::<Vec<NavigationTarget>>();

    Some(RangeInfo::new(original_token.text_range(), navs))
}

fn try_lookup_include_path(
    sema: &Semantics<'_, RootDatabase>,
    token: ast::String,
    file_id: FileId,
) -> Option<NavigationTarget> {
    let file = sema.hir_file_for(&token.syntax().parent()?).macro_file()?;
    if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file())
        // Check that we are in the eager argument expansion of an include macro
        .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none())
    {
        return None;
    }
    let path = token.value().ok()?;

    let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
    let size = sema.db.file_text(file_id).len().try_into().ok()?;
    Some(NavigationTarget {
        file_id,
        full_range: TextRange::new(0.into(), size),
        name: path.into(),
        alias: None,
        focus_range: None,
        kind: None,
        container_name: None,
        description: None,
        docs: None,
    })
}

fn try_lookup_macro_def_in_macro_use(
    sema: &Semantics<'_, RootDatabase>,
    token: SyntaxToken,
) -> Option<NavigationTarget> {
    let extern_crate = token.parent()?.ancestors().find_map(ast::ExternCrate::cast)?;
    let extern_crate = sema.to_def(&extern_crate)?;
    let krate = extern_crate.resolved_crate(sema.db)?;

    for mod_def in krate.root_module().declarations(sema.db) {
        if let ModuleDef::Macro(mac) = mod_def {
            if mac.name(sema.db).as_str() == token.text() {
                if let Some(nav) = mac.try_to_nav(sema.db) {
                    return Some(nav.call_site);
                }
            }
        }
    }

    None
}

/// finds the trait definition of an impl'd item, except function
/// e.g.
/// ```rust
/// trait A { type a; }
/// struct S;
/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait
/// ```
fn try_filter_trait_item_definition(
    sema: &Semantics<'_, RootDatabase>,
    def: &Definition,
) -> Option<Vec<NavigationTarget>> {
    let db = sema.db;
    let assoc = def.as_assoc_item(db)?;
    match assoc {
        AssocItem::Function(..) => None,
        AssocItem::Const(..) | AssocItem::TypeAlias(..) => {
            let trait_ = assoc.implemented_trait(db)?;
            let name = def.name(db)?;
            let discriminant_value = discriminant(&assoc);
            trait_
                .items(db)
                .iter()
                .filter(|itm| discriminant(*itm) == discriminant_value)
                .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
                .map(|it| it.collect())
        }
    }
}

fn handle_control_flow_keywords(
    sema: &Semantics<'_, RootDatabase>,
    token: &SyntaxToken,
) -> Option<Vec<NavigationTarget>> {
    match token.kind() {
        // For `fn` / `loop` / `while` / `for` / `async`, return the keyword it self,
        // so that VSCode will find the references when using `ctrl + click`
        T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token),
        T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token),
        T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {
            nav_for_break_points(sema, token)
        }
        _ => None,
    }
}

pub(crate) fn find_fn_or_blocks(
    sema: &Semantics<'_, RootDatabase>,
    token: &SyntaxToken,
) -> Vec<SyntaxNode> {
    let find_ancestors = |token: SyntaxToken| {
        let token_kind = token.kind();

        for anc in sema.token_ancestors_with_macros(token) {
            let node = match_ast! {
                match anc {
                    ast::Fn(fn_) => fn_.syntax().clone(),
                    ast::ClosureExpr(c) => c.syntax().clone(),
                    ast::BlockExpr(blk) => {
                        match blk.modifier() {
                            Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),
                            Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => blk.syntax().clone(),
                            _ => continue,
                        }
                    },
                    _ => continue,
                }
            };

            return Some(node);
        }
        None
    };

    sema.descend_into_macros(token.clone()).into_iter().filter_map(find_ancestors).collect_vec()
}

fn nav_for_exit_points(
    sema: &Semantics<'_, RootDatabase>,
    token: &SyntaxToken,
) -> Option<Vec<NavigationTarget>> {
    let db = sema.db;
    let token_kind = token.kind();

    let navs = find_fn_or_blocks(sema, token)
        .into_iter()
        .filter_map(|node| {
            let file_id = sema.hir_file_for(&node);

            match_ast! {
                match node {
                    ast::Fn(fn_) => {
                        let mut nav = sema.to_def(&fn_)?.try_to_nav(db)?;
                        // For async token, we navigate to itself, which triggers
                        // VSCode to find the references
                        let focus_token = if matches!(token_kind, T![async]) {
                            fn_.async_token()?
                        } else {
                            fn_.fn_token()?
                        };

                        let focus_frange = InFile::new(file_id, focus_token.text_range())
                            .original_node_file_range_opt(db)
                            .map(|(frange, _)| frange);

                        if let Some(FileRange { file_id, range }) = focus_frange {
                            let contains_frange = |nav: &NavigationTarget| {
                                nav.file_id == file_id && nav.full_range.contains_range(range)
                            };

                            if let Some(def_site) = nav.def_site.as_mut() {
                                if contains_frange(def_site) {
                                    def_site.focus_range = Some(range);
                                }
                            } else if contains_frange(&nav.call_site) {
                                nav.call_site.focus_range = Some(range);
                            }
                        }

                        Some(nav)
                    },
                    ast::ClosureExpr(c) => {
                        let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.text_range();
                        let closure_in_file = InFile::new(file_id, c.into());
                        Some(expr_to_nav(db, closure_in_file, Some(pipe_tok)))
                    },
                    ast::BlockExpr(blk) => {
                        match blk.modifier() {
                            Some(ast::BlockModifier::Async(_)) => {
                                let async_tok = blk.async_token()?.text_range();
                                let blk_in_file = InFile::new(file_id, blk.into());
                                Some(expr_to_nav(db, blk_in_file, Some(async_tok)))
                            },
                            Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => {
                                let try_tok = blk.try_token()?.text_range();
                                let blk_in_file = InFile::new(file_id, blk.into());
                                Some(expr_to_nav(db, blk_in_file, Some(try_tok)))
                            },
                            _ => None,
                        }
                    },
                    _ => None,
                }
            }
        })
        .flatten()
        .collect_vec();

    Some(navs)
}

pub(crate) fn find_loops(
    sema: &Semantics<'_, RootDatabase>,
    token: &SyntaxToken,
) -> Option<Vec<ast::Expr>> {
    let parent = token.parent()?;
    let lbl = match_ast! {
        match parent {
            ast::BreakExpr(break_) => break_.lifetime(),
            ast::ContinueExpr(continue_) => continue_.lifetime(),
            _ => None,
        }
    };
    let label_matches =
        |it: Option<ast::Label>| match (lbl.as_ref(), it.and_then(|it| it.lifetime())) {
            (Some(lbl), Some(it)) => lbl.text() == it.text(),
            (None, _) => true,
            (Some(_), None) => false,
        };

    let find_ancestors = |token: SyntaxToken| {
        for anc in sema.token_ancestors_with_macros(token).filter_map(ast::Expr::cast) {
            let node = match &anc {
                ast::Expr::LoopExpr(loop_) if label_matches(loop_.label()) => anc,
                ast::Expr::WhileExpr(while_) if label_matches(while_.label()) => anc,
                ast::Expr::ForExpr(for_) if label_matches(for_.label()) => anc,
                ast::Expr::BlockExpr(blk)
                    if blk.label().is_some() && label_matches(blk.label()) =>
                {
                    anc
                }
                _ => continue,
            };

            return Some(node);
        }
        None
    };

    sema.descend_into_macros(token.clone())
        .into_iter()
        .filter_map(find_ancestors)
        .collect_vec()
        .into()
}

fn nav_for_break_points(
    sema: &Semantics<'_, RootDatabase>,
    token: &SyntaxToken,
) -> Option<Vec<NavigationTarget>> {
    let db = sema.db;

    let navs = find_loops(sema, token)?
        .into_iter()
        .filter_map(|expr| {
            let file_id = sema.hir_file_for(expr.syntax());
            let expr_in_file = InFile::new(file_id, expr.clone());
            let focus_range = match expr {
                ast::Expr::LoopExpr(loop_) => loop_.loop_token()?.text_range(),
                ast::Expr::WhileExpr(while_) => while_.while_token()?.text_range(),
                ast::Expr::ForExpr(for_) => for_.for_token()?.text_range(),
                // We guarantee that the label exists
                ast::Expr::BlockExpr(blk) => blk.label().unwrap().syntax().text_range(),
                _ => return None,
            };
            let nav = expr_to_nav(db, expr_in_file, Some(focus_range));
            Some(nav)
        })
        .flatten()
        .collect_vec();

    Some(navs)
}

fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
    def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default()
}

fn expr_to_nav(
    db: &RootDatabase,
    InFile { file_id, value }: InFile<ast::Expr>,
    focus_range: Option<TextRange>,
) -> UpmappingResult<NavigationTarget> {
    let kind = SymbolKind::Label;

    let value_range = value.syntax().text_range();
    let navs = navigation_target::orig_range_with_focus_r(db, file_id, value_range, focus_range);
    navs.map(|(hir::FileRangeWrapper { file_id, range }, focus_range)| {
        NavigationTarget::from_syntax(file_id, "<expr>".into(), focus_range, range, kind)
    })
}

#[cfg(test)]
mod tests {
    use ide_db::FileRange;
    use itertools::Itertools;

    use crate::fixture;

    #[track_caller]
    fn check(ra_fixture: &str) {
        let (analysis, position, expected) = fixture::annotations(ra_fixture);
        let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;

        let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
        let navs = navs
            .into_iter()
            .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
            .sorted_by_key(cmp)
            .collect::<Vec<_>>();
        let expected = expected
            .into_iter()
            .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
            .sorted_by_key(cmp)
            .collect::<Vec<_>>();

        assert_eq!(expected, navs);
    }

    fn check_unresolved(ra_fixture: &str) {
        let (analysis, position) = fixture::position(ra_fixture);
        let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;

        assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
    }

    #[test]
    fn goto_def_in_included_file() {
        check(
            r#"
//- minicore:include
//- /main.rs

include!("a.rs");

fn main() {
    foo();
}

//- /a.rs
fn func_in_include() {
 //^^^^^^^^^^^^^^^
}

fn foo() {
    func_in_include$0();
}
"#,
        );
    }

    #[test]
    fn goto_def_in_included_file_nested() {
        check(
            r#"
//- minicore:include
//- /main.rs

macro_rules! passthrough {
    ($($tt:tt)*) => { $($tt)* }
}

passthrough!(include!("a.rs"));

fn main() {
    foo();
}

//- /a.rs
fn func_in_include() {
 //^^^^^^^^^^^^^^^
}

fn foo() {
    func_in_include$0();
}
"#,
        );
    }

    #[test]
    fn goto_def_if_items_same_name() {
        check(
            r#"
trait Trait {
    type A;
    const A: i32;
        //^
}

struct T;
impl Trait for T {
    type A = i32;
    const A$0: i32 = -9;
}"#,
        );
    }
    #[test]
    fn goto_def_in_mac_call_in_attr_invoc() {
        check(
            r#"
//- proc_macros: identity
pub struct Struct {
        // ^^^^^^
    field: i32,
}

macro_rules! identity {
    ($($tt:tt)*) => {$($tt)*};
}

#[proc_macros::identity]
fn function() {
    identity!(Struct$0 { field: 0 });
}

"#,
        )
    }

    #[test]
    fn goto_def_for_extern_crate() {
        check(
            r#"
//- /main.rs crate:main deps:std
extern crate std$0;
//- /std/lib.rs crate:std
// empty
//^file
"#,
        )
    }

    #[test]
    fn goto_def_for_renamed_extern_crate() {
        check(
            r#"
//- /main.rs crate:main deps:std
extern crate std as abc$0;
//- /std/lib.rs crate:std
// empty
//^file
"#,
        )
    }

    #[test]
    fn goto_def_in_items() {
        check(
            r#"
struct Foo;
     //^^^
enum E { X(Foo$0) }
"#,
        );
    }

    #[test]
    fn goto_def_at_start_of_item() {
        check(
            r#"
struct Foo;
     //^^^
enum E { X($0Foo) }
"#,
        );
    }

    #[test]
    fn goto_definition_resolves_correct_name() {
        check(
            r#"
//- /lib.rs
use a::Foo;
mod a;
mod b;
enum E { X(Foo$0) }

//- /a.rs
pub struct Foo;
         //^^^
//- /b.rs
pub struct Foo;
"#,
        );
    }

    #[test]
    fn goto_def_for_module_declaration() {
        check(
            r#"
//- /lib.rs
mod $0foo;

//- /foo.rs
// empty
//^file
"#,
        );

        check(
            r#"
//- /lib.rs
mod $0foo;

//- /foo/mod.rs
// empty
//^file
"#,
        );
    }

    #[test]
    fn goto_def_for_macros() {
        check(
            r#"
macro_rules! foo { () => { () } }
           //^^^
fn bar() {
    $0foo!();
}
"#,
        );
    }

    #[test]
    fn goto_def_for_macros_from_other_crates() {
        check(
            r#"
//- /lib.rs crate:main deps:foo
use foo::foo;
fn bar() {
    $0foo!();
}

//- /foo/lib.rs crate:foo
#[macro_export]
macro_rules! foo { () => { () } }
           //^^^
"#,
        );
    }

    #[test]
    fn goto_def_for_macros_in_use_tree() {
        check(
            r#"
//- /lib.rs crate:main deps:foo
use foo::foo$0;

//- /foo/lib.rs crate:foo
#[macro_export]
macro_rules! foo { () => { () } }
           //^^^
"#,
        );
    }

    #[test]
    fn goto_def_for_macro_defined_fn_with_arg() {
        check(
            r#"
//- /lib.rs
macro_rules! define_fn {
    ($name:ident) => (fn $name() {})
}

define_fn!(foo);
         //^^^

fn bar() {
   $0foo();
}
"#,
        );
    }

    #[test]
    fn goto_def_for_macro_defined_fn_no_arg() {
        check(
            r#"
//- /lib.rs
macro_rules! define_fn {
    () => (fn foo() {})
            //^^^
}

  define_fn!();
//^^^^^^^^^^^^^
fn bar() {
   $0foo();
}
"#,
        );
    }

    #[test]
    fn goto_definition_works_for_macro_inside_pattern() {
        check(
            r#"
//- /lib.rs
macro_rules! foo {() => {0}}
           //^^^

fn bar() {
    match (0,1) {
        ($0foo!(), _) => {}
    }
}
"#,
        );
    }

    #[test]
    fn goto_definition_works_for_macro_inside_match_arm_lhs() {
        check(
            r#"
//- /lib.rs
macro_rules! foo {() => {0}}
           //^^^
fn bar() {
    match 0 {
        $0foo!() => {}
    }
}
"#,
        );
    }

    #[test]
    fn goto_definition_works_for_consts_inside_range_pattern() {
        check(
            r#"
//- /lib.rs
const A: u32 = 0;
    //^

fn bar(v: u32) {
    match v {
        0..=$0A => {}
        _ => {}
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_for_use_alias() {
        check(
            r#"
//- /lib.rs crate:main deps:foo
use foo as bar$0;

//- /foo/lib.rs crate:foo
// empty
//^file
"#,
        );
    }

    #[test]
    fn goto_def_for_use_alias_foo_macro() {
        check(
            r#"
//- /lib.rs crate:main deps:foo
use foo::foo as bar$0;

//- /foo/lib.rs crate:foo
#[macro_export]
macro_rules! foo { () => { () } }
           //^^^
"#,
        );
    }

    #[test]
    fn goto_def_for_methods() {
        check(
            r#"
struct Foo;
impl Foo {
    fn frobnicate(&self) { }
     //^^^^^^^^^^
}

fn bar(foo: &Foo) {
    foo.frobnicate$0();
}
"#,
        );
    }

    #[test]
    fn goto_def_for_fields() {
        check(
            r#"
struct Foo {
    spam: u32,
} //^^^^

fn bar(foo: &Foo) {
    foo.spam$0;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_record_fields() {
        check(
            r#"
//- /lib.rs
struct Foo {
    spam: u32,
} //^^^^

fn bar() -> Foo {
    Foo {
        spam$0: 0,
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_for_record_pat_fields() {
        check(
            r#"
//- /lib.rs
struct Foo {
    spam: u32,
} //^^^^

fn bar(foo: Foo) -> Foo {
    let Foo { spam$0: _, } = foo
}
"#,
        );
    }

    #[test]
    fn goto_def_for_record_fields_macros() {
        check(
            r"
macro_rules! m { () => { 92 };}
struct Foo { spam: u32 }
           //^^^^

fn bar() -> Foo {
    Foo { spam$0: m!() }
}
",
        );
    }

    #[test]
    fn goto_for_tuple_fields() {
        check(
            r#"
struct Foo(u32);
         //^^^

fn bar() {
    let foo = Foo(0);
    foo.$00;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_ufcs_inherent_methods() {
        check(
            r#"
struct Foo;
impl Foo {
    fn frobnicate() { }
}    //^^^^^^^^^^

fn bar(foo: &Foo) {
    Foo::frobnicate$0();
}
"#,
        );
    }

    #[test]
    fn goto_def_for_ufcs_trait_methods_through_traits() {
        check(
            r#"
trait Foo {
    fn frobnicate();
}    //^^^^^^^^^^

fn bar() {
    Foo::frobnicate$0();
}
"#,
        );
    }

    #[test]
    fn goto_def_for_ufcs_trait_methods_through_self() {
        check(
            r#"
struct Foo;
trait Trait {
    fn frobnicate();
}    //^^^^^^^^^^
impl Trait for Foo {}

fn bar() {
    Foo::frobnicate$0();
}
"#,
        );
    }

    #[test]
    fn goto_definition_on_self() {
        check(
            r#"
struct Foo;
impl Foo {
   //^^^
    pub fn new() -> Self {
        Self$0 {}
    }
}
"#,
        );
        check(
            r#"
struct Foo;
impl Foo {
   //^^^
    pub fn new() -> Self$0 {
        Self {}
    }
}
"#,
        );

        check(
            r#"
enum Foo { A }
impl Foo {
   //^^^
    pub fn new() -> Self$0 {
        Foo::A
    }
}
"#,
        );

        check(
            r#"
enum Foo { A }
impl Foo {
   //^^^
    pub fn thing(a: &Self$0) {
    }
}
"#,
        );
    }

    #[test]
    fn goto_definition_on_self_in_trait_impl() {
        check(
            r#"
struct Foo;
trait Make {
    fn new() -> Self;
}
impl Make for Foo {
            //^^^
    fn new() -> Self {
        Self$0 {}
    }
}
"#,
        );

        check(
            r#"
struct Foo;
trait Make {
    fn new() -> Self;
}
impl Make for Foo {
            //^^^
    fn new() -> Self$0 {
        Self {}
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_when_used_on_definition_name_itself() {
        check(
            r#"
struct Foo$0 { value: u32 }
     //^^^
            "#,
        );

        check(
            r#"
struct Foo {
    field$0: string,
} //^^^^^
"#,
        );

        check(
            r#"
fn foo_test$0() { }
 //^^^^^^^^
"#,
        );

        check(
            r#"
enum Foo$0 { Variant }
   //^^^
"#,
        );

        check(
            r#"
enum Foo {
    Variant1,
    Variant2$0,
  //^^^^^^^^
    Variant3,
}
"#,
        );

        check(
            r#"
static INNER$0: &str = "";
     //^^^^^
"#,
        );

        check(
            r#"
const INNER$0: &str = "";
    //^^^^^
"#,
        );

        check(
            r#"
type Thing$0 = Option<()>;
   //^^^^^
"#,
        );

        check(
            r#"
trait Foo$0 { }
    //^^^
"#,
        );

        check(
            r#"
trait Foo$0 = ;
    //^^^
"#,
        );

        check(
            r#"
mod bar$0 { }
  //^^^
"#,
        );
    }

    #[test]
    fn goto_from_macro() {
        check(
            r#"
macro_rules! id {
    ($($tt:tt)*) => { $($tt)* }
}
fn foo() {}
 //^^^
id! {
    fn bar() {
        fo$0o();
    }
}
mod confuse_index { fn foo(); }
"#,
        );
    }

    #[test]
    fn goto_through_format() {
        check(
            r#"
//- minicore: fmt
#[macro_export]
macro_rules! format {
    ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
}
pub mod __export {
    pub use core::format_args;
    fn foo() {} // for index confusion
}
fn foo() -> i8 {}
 //^^^
fn test() {
    format!("{}", fo$0o())
}
"#,
        );
    }

    #[test]
    fn goto_through_included_file() {
        check(
            r#"
//- /main.rs
#[rustc_builtin_macro]
macro_rules! include {}

include!("foo.rs");

fn f() {
    foo$0();
}

mod confuse_index {
    pub fn foo() {}
}

//- /foo.rs
fn foo() {}
 //^^^
        "#,
        );
    }

    #[test]
    fn goto_through_included_file_struct_with_doc_comment() {
        check(
            r#"
//- /main.rs
#[rustc_builtin_macro]
macro_rules! include {}

include!("foo.rs");

fn f() {
    let x = Foo$0;
}

mod confuse_index {
    pub struct Foo;
}

//- /foo.rs
/// This is a doc comment
pub struct Foo;
         //^^^
        "#,
        );
    }

    #[test]
    fn goto_for_type_param() {
        check(
            r#"
struct Foo<T: Clone> { t: $0T }
         //^
"#,
        );
    }

    #[test]
    fn goto_within_macro() {
        check(
            r#"
macro_rules! id {
    ($($tt:tt)*) => ($($tt)*)
}

fn foo() {
    let x = 1;
      //^
    id!({
        let y = $0x;
        let z = y;
    });
}
"#,
        );

        check(
            r#"
macro_rules! id {
    ($($tt:tt)*) => ($($tt)*)
}

fn foo() {
    let x = 1;
    id!({
        let y = x;
          //^
        let z = $0y;
    });
}
"#,
        );
    }

    #[test]
    fn goto_def_in_local_fn() {
        check(
            r#"
fn main() {
    fn foo() {
        let x = 92;
          //^
        $0x;
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_in_local_macro() {
        check(
            r#"
fn bar() {
    macro_rules! foo { () => { () } }
               //^^^
    $0foo!();
}
"#,
        );
    }

    #[test]
    fn goto_def_for_field_init_shorthand() {
        check(
            r#"
struct Foo { x: i32 }
           //^
fn main() {
    let x = 92;
      //^
    Foo { x$0 };
}
"#,
        )
    }

    #[test]
    fn goto_def_for_enum_variant_field() {
        check(
            r#"
enum Foo {
    Bar { x: i32 }
        //^
}
fn baz(foo: Foo) {
    match foo {
        Foo::Bar { x$0 } => x
                 //^
    };
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_variant_self_pattern_const() {
        check(
            r#"
enum Foo { Bar }
         //^^^
impl Foo {
    fn baz(self) {
        match self { Self::Bar$0 => {} }
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_variant_self_pattern_record() {
        check(
            r#"
enum Foo { Bar { val: i32 } }
         //^^^
impl Foo {
    fn baz(self) -> i32 {
        match self { Self::Bar$0 { val } => {} }
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_variant_self_expr_const() {
        check(
            r#"
enum Foo { Bar }
         //^^^
impl Foo {
    fn baz(self) { Self::Bar$0; }
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_variant_self_expr_record() {
        check(
            r#"
enum Foo { Bar { val: i32 } }
         //^^^
impl Foo {
    fn baz(self) { Self::Bar$0 {val: 4}; }
}
"#,
        );
    }

    #[test]
    fn goto_def_for_type_alias_generic_parameter() {
        check(
            r#"
type Alias<T> = T$0;
         //^
"#,
        )
    }

    #[test]
    fn goto_def_for_macro_container() {
        check(
            r#"
//- /lib.rs crate:main deps:foo
foo::module$0::mac!();

//- /foo/lib.rs crate:foo
pub mod module {
      //^^^^^^
    #[macro_export]
    macro_rules! _mac { () => { () } }
    pub use crate::_mac as mac;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_assoc_ty_in_path() {
        check(
            r#"
trait Iterator {
    type Item;
       //^^^^
}

fn f() -> impl Iterator<Item$0 = u8> {}
"#,
        );
    }

    #[test]
    fn goto_def_for_super_assoc_ty_in_path() {
        check(
            r#"
trait Super {
    type Item;
       //^^^^
}

trait Sub: Super {}

fn f() -> impl Sub<Item$0 = u8> {}
"#,
        );
    }

    #[test]
    fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {
        check(
            r#"
mod bar {
    pub struct Foo {}
             //^^^
    pub fn Foo() {}
}

fn baz() {
    let _foo_enum: bar::Foo$0 = bar::Foo {};
}
        "#,
        )
    }

    #[test]
    fn unknown_assoc_ty() {
        check_unresolved(
            r#"
trait Iterator { type Item; }
fn f() -> impl Iterator<Invalid$0 = u8> {}
"#,
        )
    }

    #[test]
    fn goto_def_for_assoc_ty_in_path_multiple() {
        check(
            r#"
trait Iterator {
    type A;
       //^
    type B;
}

fn f() -> impl Iterator<A$0 = u8, B = ()> {}
"#,
        );
        check(
            r#"
trait Iterator {
    type A;
    type B;
       //^
}

fn f() -> impl Iterator<A = u8, B$0 = ()> {}
"#,
        );
    }

    #[test]
    fn goto_def_for_assoc_ty_ufcs() {
        check(
            r#"
trait Iterator {
    type Item;
       //^^^^
}

fn g() -> <() as Iterator<Item$0 = ()>>::Item {}
"#,
        );
    }

    #[test]
    fn goto_def_for_assoc_ty_ufcs_multiple() {
        check(
            r#"
trait Iterator {
    type A;
       //^
    type B;
}

fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}
"#,
        );
        check(
            r#"
trait Iterator {
    type A;
    type B;
       //^
}

fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}
"#,
        );
    }

    #[test]
    fn goto_self_param_ty_specified() {
        check(
            r#"
struct Foo {}

impl Foo {
    fn bar(self: &Foo) {
         //^^^^
        let foo = sel$0f;
    }
}"#,
        )
    }

    #[test]
    fn goto_self_param_on_decl() {
        check(
            r#"
struct Foo {}

impl Foo {
    fn bar(&self$0) {
          //^^^^
    }
}"#,
        )
    }

    #[test]
    fn goto_lifetime_param_on_decl() {
        check(
            r#"
fn foo<'foobar$0>(_: &'foobar ()) {
     //^^^^^^^
}"#,
        )
    }

    #[test]
    fn goto_lifetime_param_decl() {
        check(
            r#"
fn foo<'foobar>(_: &'foobar$0 ()) {
     //^^^^^^^
}"#,
        )
    }

    #[test]
    fn goto_lifetime_param_decl_nested() {
        check(
            r#"
fn foo<'foobar>(_: &'foobar ()) {
    fn foo<'foobar>(_: &'foobar$0 ()) {}
         //^^^^^^^
}"#,
        )
    }

    #[test]
    fn goto_lifetime_hrtb() {
        // FIXME: requires the HIR to somehow track these hrtb lifetimes
        check_unresolved(
            r#"
trait Foo<T> {}
fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
                    //^^
"#,
        );
        check_unresolved(
            r#"
trait Foo<T> {}
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
                    //^^
"#,
        );
    }

    #[test]
    fn goto_lifetime_hrtb_for_type() {
        // FIXME: requires ForTypes to be implemented
        check_unresolved(
            r#"trait Foo<T> {}
fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
                       //^^
"#,
        );
    }

    #[test]
    fn goto_label() {
        check(
            r#"
fn foo<'foo>(_: &'foo ()) {
    'foo: {
  //^^^^
        'bar: loop {
            break 'foo$0;
        }
    }
}"#,
        )
    }

    #[test]
    fn goto_def_for_intra_doc_link_same_file() {
        check(
            r#"
/// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar)
pub fn bar() { }

/// You might want to see [`std::fs::read()`] too.
pub fn foo() { }
     //^^^

}"#,
        )
    }

    #[test]
    fn goto_def_for_intra_doc_link_inner() {
        check(
            r#"
//- /main.rs
mod m;
struct S;
     //^

//- /m.rs
//! [`super::S$0`]
"#,
        )
    }

    #[test]
    fn goto_incomplete_field() {
        check(
            r#"
struct A { a: u32 }
         //^
fn foo() { A { a$0: }; }
"#,
        )
    }

    #[test]
    fn goto_proc_macro() {
        check(
            r#"
//- /main.rs crate:main deps:mac
use mac::fn_macro;

fn_macro$0!();

//- /mac.rs crate:mac
#![crate_type="proc-macro"]
#[proc_macro]
fn fn_macro() {}
 //^^^^^^^^
            "#,
        )
    }

    #[test]
    fn goto_intra_doc_links() {
        check(
            r#"

pub mod theitem {
    /// This is the item. Cool!
    pub struct TheItem;
             //^^^^^^^
}

/// Gives you a [`TheItem$0`].
///
/// [`TheItem`]: theitem::TheItem
pub fn gimme() -> theitem::TheItem {
    theitem::TheItem
}
"#,
        );
    }

    #[test]
    fn goto_ident_from_pat_macro() {
        check(
            r#"
macro_rules! pat {
    ($name:ident) => { Enum::Variant1($name) }
}

enum Enum {
    Variant1(u8),
    Variant2,
}

fn f(e: Enum) {
    match e {
        pat!(bind) => {
           //^^^^
            bind$0
        }
        Enum::Variant2 => {}
    }
}
"#,
        );
    }

    #[test]
    fn goto_include() {
        check(
            r#"
//- /main.rs

#[rustc_builtin_macro]
macro_rules! include_str {}

fn main() {
    let str = include_str!("foo.txt$0");
}
//- /foo.txt
// empty
//^file
"#,
        );
    }

    #[test]
    fn goto_include_has_eager_input() {
        check(
            r#"
//- /main.rs
#[rustc_builtin_macro]
macro_rules! include_str {}
#[rustc_builtin_macro]
macro_rules! concat {}

fn main() {
    let str = include_str!(concat!("foo", ".tx$0t"));
}
//- /foo.txt
// empty
//^file
"#,
        );
    }

    #[test]
    fn goto_doc_include_str() {
        check(
            r#"
//- /main.rs
#[rustc_builtin_macro]
macro_rules! include_str {}

#[doc = include_str!("docs.md$0")]
struct Item;

//- /docs.md
// docs
//^file
"#,
        );
    }

    #[test]
    fn goto_shadow_include() {
        check(
            r#"
//- /main.rs
macro_rules! include {
    ("included.rs") => {}
}

include!("included.rs$0");

//- /included.rs
// empty
"#,
        );
    }

    mod goto_impl_of_trait_fn {
        use super::check;
        #[test]
        fn cursor_on_impl() {
            check(
                r#"
trait Twait {
    fn a();
}

struct Stwuct;

impl Twait for Stwuct {
    fn a$0();
     //^
}
        "#,
            );
        }
        #[test]
        fn method_call() {
            check(
                r#"
trait Twait {
    fn a(&self);
}

struct Stwuct;

impl Twait for Stwuct {
    fn a(&self){};
     //^
}
fn f() {
    let s = Stwuct;
    s.a$0();
}
        "#,
            );
        }
        #[test]
        fn method_call_inside_block() {
            check(
                r#"
trait Twait {
    fn a(&self);
}

fn outer() {
    struct Stwuct;

    impl Twait for Stwuct {
        fn a(&self){}
         //^
    }
    fn f() {
        let s = Stwuct;
        s.a$0();
    }
}
        "#,
            );
        }
        #[test]
        fn path_call() {
            check(
                r#"
trait Twait {
    fn a(&self);
}

struct Stwuct;

impl Twait for Stwuct {
    fn a(&self){};
     //^
}
fn f() {
    let s = Stwuct;
    Stwuct::a$0(&s);
}
        "#,
            );
        }
        #[test]
        fn where_clause_can_work() {
            check(
                r#"
trait G {
    fn g(&self);
}
trait Bound{}
trait EA{}
struct Gen<T>(T);
impl <T:EA> G for Gen<T> {
    fn g(&self) {
    }
}
impl <T> G for Gen<T>
where T : Bound
{
    fn g(&self){
     //^
    }
}
struct A;
impl Bound for A{}
fn f() {
    let gen = Gen::<A>(A);
    gen.g$0();
}
                "#,
            );
        }
        #[test]
        fn wc_case_is_ok() {
            check(
                r#"
trait G {
    fn g(&self);
}
trait BParent{}
trait Bound: BParent{}
struct Gen<T>(T);
impl <T> G for Gen<T>
where T : Bound
{
    fn g(&self){
     //^
    }
}
struct A;
impl Bound for A{}
fn f() {
    let gen = Gen::<A>(A);
    gen.g$0();
}
"#,
            );
        }

        #[test]
        fn method_call_defaulted() {
            check(
                r#"
trait Twait {
    fn a(&self) {}
     //^
}

struct Stwuct;

impl Twait for Stwuct {
}
fn f() {
    let s = Stwuct;
    s.a$0();
}
        "#,
            );
        }

        #[test]
        fn method_call_on_generic() {
            check(
                r#"
trait Twait {
    fn a(&self) {}
     //^
}

fn f<T: Twait>(s: T) {
    s.a$0();
}
        "#,
            );
        }
    }

    #[test]
    fn goto_def_of_trait_impl_const() {
        check(
            r#"
trait Twait {
    const NOMS: bool;
       // ^^^^
}

struct Stwuct;

impl Twait for Stwuct {
    const NOMS$0: bool = true;
}
"#,
        );
    }

    #[test]
    fn goto_def_of_trait_impl_type_alias() {
        check(
            r#"
trait Twait {
    type IsBad;
      // ^^^^^
}

struct Stwuct;

impl Twait for Stwuct {
    type IsBad$0 = !;
}
"#,
        );
    }

    #[test]
    fn goto_def_derive_input() {
        check(
            r#"
        //- minicore:derive
        #[rustc_builtin_macro]
        pub macro Copy {}
               // ^^^^
        #[derive(Copy$0)]
        struct Foo;
                    "#,
        );
        check(
            r#"
//- minicore:derive
#[rustc_builtin_macro]
pub macro Copy {}
       // ^^^^
#[cfg_attr(feature = "false", derive)]
#[derive(Copy$0)]
struct Foo;
            "#,
        );
        check(
            r#"
//- minicore:derive
mod foo {
    #[rustc_builtin_macro]
    pub macro Copy {}
           // ^^^^
}
#[derive(foo::Copy$0)]
struct Foo;
            "#,
        );
        check(
            r#"
//- minicore:derive
mod foo {
 // ^^^
    #[rustc_builtin_macro]
    pub macro Copy {}
}
#[derive(foo$0::Copy)]
struct Foo;
            "#,
        );
    }

    #[test]
    fn goto_def_in_macro_multi() {
        check(
            r#"
struct Foo {
    foo: ()
  //^^^
}
macro_rules! foo {
    ($ident:ident) => {
        fn $ident(Foo { $ident }: Foo) {}
    }
}
  foo!(foo$0);
     //^^^
     //^^^
"#,
        );
        check(
            r#"
fn bar() {}
 //^^^
struct bar;
     //^^^
macro_rules! foo {
    ($ident:ident) => {
        fn foo() {
            let _: $ident = $ident;
        }
    }
}

foo!(bar$0);
"#,
        );
    }

    #[test]
    fn goto_await_poll() {
        check(
            r#"
//- minicore: future

struct MyFut;

impl core::future::Future for MyFut {
    type Output = ();

    fn poll(
     //^^^^
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>
    ) -> std::task::Poll<Self::Output>
    {
        ()
    }
}

fn f() {
    MyFut.await$0;
}
"#,
        );
    }

    #[test]
    fn goto_await_into_future_poll() {
        check(
            r#"
//- minicore: future

struct Futurable;

impl core::future::IntoFuture for Futurable {
    type IntoFuture = MyFut;
}

struct MyFut;

impl core::future::Future for MyFut {
    type Output = ();

    fn poll(
     //^^^^
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>
    ) -> std::task::Poll<Self::Output>
    {
        ()
    }
}

fn f() {
    Futurable.await$0;
}
"#,
        );
    }

    #[test]
    fn goto_try_op() {
        check(
            r#"
//- minicore: try

struct Struct;

impl core::ops::Try for Struct {
    fn branch(
     //^^^^^^
        self
    ) {}
}

fn f() {
    Struct?$0;
}
"#,
        );
    }

    #[test]
    fn goto_index_op() {
        check(
            r#"
//- minicore: index

struct Struct;

impl core::ops::Index<usize> for Struct {
    fn index(
     //^^^^^
        self
    ) {}
}

fn f() {
    Struct[0]$0;
}
"#,
        );
    }

    #[test]
    fn goto_index_mut_op() {
        check(
            r#"
//- minicore: index

struct Foo;
struct Bar;

impl core::ops::Index<usize> for Foo {
    type Output = Bar;

    fn index(&self, index: usize) -> &Self::Output {}
}

impl core::ops::IndexMut<usize> for Foo {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {}
     //^^^^^^^^^
}

fn f() {
    let mut foo = Foo;
    foo[0]$0 = Bar;
}
"#,
        );
    }

    #[test]
    fn goto_prefix_op() {
        check(
            r#"
//- minicore: deref

struct Struct;

impl core::ops::Deref for Struct {
    fn deref(
     //^^^^^
        self
    ) {}
}

fn f() {
    $0*Struct;
}
"#,
        );
    }

    #[test]
    fn goto_deref_mut() {
        check(
            r#"
//- minicore: deref, deref_mut

struct Foo;
struct Bar;

impl core::ops::Deref for Foo {
    type Target = Bar;
    fn deref(&self) -> &Self::Target {}
}

impl core::ops::DerefMut for Foo {
    fn deref_mut(&mut self) -> &mut Self::Target {}
     //^^^^^^^^^
}

fn f() {
    let a = Foo;
    $0*a = Bar;
}
"#,
        );
    }

    #[test]
    fn goto_bin_op() {
        check(
            r#"
//- minicore: add

struct Struct;

impl core::ops::Add for Struct {
    fn add(
     //^^^
        self
    ) {}
}

fn f() {
    Struct +$0 Struct;
}
"#,
        );
    }

    #[test]
    fn goto_bin_op_multiple_impl() {
        check(
            r#"
//- minicore: add
struct S;
impl core::ops::Add for S {
    fn add(
     //^^^
    ) {}
}
impl core::ops::Add<usize> for S {
    fn add(
    ) {}
}

fn f() {
    S +$0 S
}
"#,
        );

        check(
            r#"
//- minicore: add
struct S;
impl core::ops::Add for S {
    fn add(
    ) {}
}
impl core::ops::Add<usize> for S {
    fn add(
     //^^^
    ) {}
}

fn f() {
    S +$0 0usize
}
"#,
        );
    }

    #[test]
    fn path_call_multiple_trait_impl() {
        check(
            r#"
trait Trait<T> {
    fn f(_: T);
}
impl Trait<i32> for usize {
    fn f(_: i32) {}
     //^
}
impl Trait<i64> for usize {
    fn f(_: i64) {}
}
fn main() {
    usize::f$0(0i32);
}
"#,
        );

        check(
            r#"
trait Trait<T> {
    fn f(_: T);
}
impl Trait<i32> for usize {
    fn f(_: i32) {}
}
impl Trait<i64> for usize {
    fn f(_: i64) {}
     //^
}
fn main() {
    usize::f$0(0i64);
}
"#,
        )
    }

    #[test]
    fn query_impls_in_nearest_block() {
        check(
            r#"
struct S1;
impl S1 {
    fn e() -> () {}
}
fn f1() {
    struct S1;
    impl S1 {
        fn e() -> () {}
         //^
    }
    fn f2() {
        fn f3() {
            S1::e$0();
        }
    }
}
"#,
        );

        check(
            r#"
struct S1;
impl S1 {
    fn e() -> () {}
}
fn f1() {
    struct S1;
    impl S1 {
        fn e() -> () {}
         //^
    }
    fn f2() {
        struct S2;
        S1::e$0();
    }
}
fn f12() {
    struct S1;
    impl S1 {
        fn e() -> () {}
    }
}
"#,
        );

        check(
            r#"
struct S1;
impl S1 {
    fn e() -> () {}
     //^
}
fn f2() {
    struct S2;
    S1::e$0();
}
"#,
        );
    }

    #[test]
    fn implicit_format_args() {
        check(
            r#"
//- minicore: fmt
fn test() {
    let a = "world";
     // ^
    format_args!("hello {a$0}");
}
"#,
        );
    }

    #[test]
    fn goto_macro_def_from_macro_use() {
        check(
            r#"
//- /main.rs crate:main deps:mac
#[macro_use(foo$0)]
extern crate mac;

//- /mac.rs crate:mac
#[macro_export]
macro_rules! foo {
           //^^^
    () => {};
}
            "#,
        );

        check(
            r#"
//- /main.rs crate:main deps:mac
#[macro_use(foo, bar$0, baz)]
extern crate mac;

//- /mac.rs crate:mac
#[macro_export]
macro_rules! foo {
    () => {};
}

#[macro_export]
macro_rules! bar {
           //^^^
    () => {};
}

#[macro_export]
macro_rules! baz {
    () => {};
}
            "#,
        );
    }

    #[test]
    fn goto_shadowed_preludes_in_block_module() {
        check(
            r#"
//- /main.rs crate:main edition:2021 deps:core
pub struct S;
         //^

fn main() {
    fn f() -> S$0 {
        fn inner() {} // forces a block def map
        return S;
    }
}
//- /core.rs crate:core
pub mod prelude {
    pub mod rust_2021 {
        pub enum S;
    }
}
        "#,
        );
    }

    #[test]
    fn goto_def_on_return_kw() {
        check(
            r#"
macro_rules! N {
    ($i:ident, $x:expr, $blk:expr) => {
        for $i in 0..$x {
            $blk
        }
    };
}

fn main() {
    fn f() {
 // ^^
        N!(i, 5, {
            println!("{}", i);
            return$0;
        });

        for i in 1..5 {
            return;
        }
       (|| {
            return;
        })();
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_return_kw_in_closure() {
        check(
            r#"
macro_rules! N {
    ($i:ident, $x:expr, $blk:expr) => {
        for $i in 0..$x {
            $blk
        }
    };
}

fn main() {
    fn f() {
        N!(i, 5, {
            println!("{}", i);
            return;
        });

        for i in 1..5 {
            return;
        }
       (|| {
     // ^
            return$0;
        })();
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_break_kw() {
        check(
            r#"
fn main() {
    for i in 1..5 {
 // ^^^
        break$0;
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_continue_kw() {
        check(
            r#"
fn main() {
    for i in 1..5 {
 // ^^^
        continue$0;
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_break_kw_for_block() {
        check(
            r#"
fn main() {
    'a:{
 // ^^^
        break$0 'a;
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_break_with_label() {
        check(
            r#"
fn foo() {
    'outer: loop {
         // ^^^^
         'inner: loop {
            'innermost: loop {
            }
            break$0 'outer;
        }
    }
}
"#,
        );
    }

    #[test]
    fn goto_def_on_return_in_try() {
        check(
            r#"
fn main() {
    fn f() {
 // ^^
        try {
            return$0;
        }

        return;
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_break_in_try() {
        check(
            r#"
fn main() {
    for i in 1..100 {
 // ^^^
        let x: Result<(), ()> = try {
            break$0;
        };
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_return_in_async_block() {
        check(
            r#"
fn main() {
    async {
 // ^^^^^
        return$0;
    }
}
"#,
        )
    }

    #[test]
    fn goto_def_on_for_kw() {
        check(
            r#"
fn main() {
    for$0 i in 1..5 {}
 // ^^^
}
"#,
        )
    }

    #[test]
    fn goto_def_on_fn_kw() {
        check(
            r#"
fn main() {
    fn$0 foo() {}
 // ^^
}
"#,
        )
    }
}
