| use expect_test::{expect, Expect}; |
| use ide_db::{base_db::SourceDatabase, FileRange}; |
| use syntax::TextRange; |
| |
| use crate::{ |
| fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, |
| }; |
| |
| const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { |
| links_in_hover: false, |
| memory_layout: Some(MemoryLayoutHoverConfig { |
| size: Some(MemoryLayoutHoverRenderKind::Both), |
| offset: Some(MemoryLayoutHoverRenderKind::Both), |
| alignment: Some(MemoryLayoutHoverRenderKind::Both), |
| niches: true, |
| }), |
| documentation: true, |
| format: HoverDocFormat::Markdown, |
| keywords: true, |
| max_trait_assoc_items_count: None, |
| max_fields_count: Some(5), |
| max_enum_variants_count: Some(5), |
| }; |
| |
| fn check_hover_no_result(ra_fixture: &str) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap(); |
| assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap()); |
| } |
| |
| #[track_caller] |
| fn check(ra_fixture: &str, expect: Expect) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| #[track_caller] |
| fn check_hover_fields_limit( |
| fields_count: impl Into<Option<usize>>, |
| ra_fixture: &str, |
| expect: Expect, |
| ) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { |
| links_in_hover: true, |
| max_fields_count: fields_count.into(), |
| ..HOVER_BASE_CONFIG |
| }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| #[track_caller] |
| fn check_hover_enum_variants_limit( |
| variants_count: impl Into<Option<usize>>, |
| ra_fixture: &str, |
| expect: Expect, |
| ) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { |
| links_in_hover: true, |
| max_enum_variants_count: variants_count.into(), |
| ..HOVER_BASE_CONFIG |
| }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| #[track_caller] |
| fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { |
| links_in_hover: true, |
| max_trait_assoc_items_count: Some(count), |
| ..HOVER_BASE_CONFIG |
| }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| fn check_hover_no_links(ra_fixture: &str, expect: Expect) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HOVER_BASE_CONFIG, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { |
| let (analysis, position) = fixture::position(ra_fixture); |
| let hover = analysis |
| .hover( |
| &HoverConfig { |
| links_in_hover: true, |
| format: HoverDocFormat::PlainText, |
| ..HOVER_BASE_CONFIG |
| }, |
| FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, |
| ) |
| .unwrap() |
| .unwrap(); |
| |
| let content = analysis.db.file_text(position.file_id); |
| let hovered_element = &content[hover.range]; |
| |
| let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); |
| expect.assert_eq(&actual) |
| } |
| |
| fn check_actions(ra_fixture: &str, expect: Expect) { |
| let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); |
| let mut hover = analysis |
| .hover( |
| &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, |
| FileRange { file_id, range: position.range_or_empty() }, |
| ) |
| .unwrap() |
| .unwrap(); |
| // stub out ranges into minicore as they can change every now and then |
| hover.info.actions.iter_mut().for_each(|action| match action { |
| super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { |
| if data.nav.file_id == file_id { |
| return; |
| } |
| data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); |
| if let Some(range) = &mut data.nav.focus_range { |
| *range = TextRange::empty(span::TextSize::new(!0)); |
| } |
| }), |
| _ => (), |
| }); |
| expect.assert_debug_eq(&hover.info.actions) |
| } |
| |
| fn check_hover_range(ra_fixture: &str, expect: Expect) { |
| let (analysis, range) = fixture::range(ra_fixture); |
| let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap(); |
| expect.assert_eq(hover.info.markup.as_str()) |
| } |
| |
| fn check_hover_range_actions(ra_fixture: &str, expect: Expect) { |
| let (analysis, range) = fixture::range(ra_fixture); |
| let mut hover = analysis |
| .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) |
| .unwrap() |
| .unwrap(); |
| // stub out ranges into minicore as they can change every now and then |
| hover.info.actions.iter_mut().for_each(|action| match action { |
| super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { |
| if data.nav.file_id == range.file_id { |
| return; |
| } |
| data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); |
| if let Some(range) = &mut data.nav.focus_range { |
| *range = TextRange::empty(span::TextSize::new(!0)); |
| } |
| }), |
| _ => (), |
| }); |
| expect.assert_debug_eq(&hover.info.actions); |
| } |
| |
| fn check_hover_range_no_results(ra_fixture: &str) { |
| let (analysis, range) = fixture::range(ra_fixture); |
| let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap(); |
| assert!(hover.is_none()); |
| } |
| |
| #[test] |
| fn hover_descend_macros_avoids_duplicates() { |
| check( |
| r#" |
| macro_rules! dupe_use { |
| ($local:ident) => { |
| { |
| $local; |
| $local; |
| } |
| } |
| } |
| fn foo() { |
| let local = 0; |
| dupe_use!(local$0); |
| } |
| "#, |
| expect![[r#" |
| *local* |
| |
| ```rust |
| // size = 4, align = 4 |
| let local: i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_all_macro_descends() { |
| check( |
| r#" |
| macro_rules! m { |
| ($name:ident) => { |
| /// Outer |
| fn $name() {} |
| |
| mod module { |
| /// Inner |
| fn $name() {} |
| } |
| }; |
| } |
| |
| m!(ab$0c); |
| "#, |
| expect![[r#" |
| *abc* |
| |
| ```rust |
| test::module |
| ``` |
| |
| ```rust |
| fn abc() |
| ``` |
| |
| --- |
| |
| Inner |
| --- |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn abc() |
| ``` |
| |
| --- |
| |
| Outer |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_remove_markdown_if_configured() { |
| check_hover_no_markdown( |
| r#" |
| pub fn foo() -> u32 { 1 } |
| |
| fn main() { |
| let foo_test = foo$0(); |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| test |
| |
| pub fn foo() -> u32 |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_closure() { |
| check( |
| r#" |
| //- minicore: copy |
| fn main() { |
| let x = 2; |
| let y = $0|z| x + z; |
| } |
| "#, |
| expect![[r#" |
| *|* |
| ```rust |
| {closure#0} // size = 8, align = 8, niches = 1 |
| impl Fn(i32) -> i32 |
| ``` |
| |
| ## Captures |
| * `x` by immutable borrow |
| "#]], |
| ); |
| |
| check( |
| r#" |
| //- minicore: copy |
| fn foo(x: impl Fn(i32) -> i32) { |
| |
| } |
| fn main() { |
| foo($0|x: i32| x) |
| } |
| "#, |
| expect![[r#" |
| *|* |
| ```rust |
| {closure#0} // size = 0, align = 1 |
| impl Fn(i32) -> i32 |
| ``` |
| |
| ## Captures |
| This closure captures nothing |
| "#]], |
| ); |
| |
| check( |
| r#" |
| //- minicore: copy |
| |
| struct Z { f: i32 } |
| |
| struct Y(&'static mut Z) |
| |
| struct X { |
| f1: Y, |
| f2: (Y, Y), |
| } |
| |
| fn main() { |
| let x: X; |
| let y = $0|| { |
| x.f1; |
| &mut x.f2.0 .0.f; |
| }; |
| } |
| "#, |
| expect![[r#" |
| *|* |
| ```rust |
| {closure#0} // size = 16 (0x10), align = 8, niches = 1 |
| impl FnOnce() |
| ``` |
| |
| ## Captures |
| * `x.f1` by move |
| * `(*x.f2.0.0).f` by mutable borrow |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: copy, option |
| |
| fn do_char(c: char) {} |
| |
| fn main() { |
| let x = None; |
| let y = |$0| { |
| match x { |
| Some(c) => do_char(c), |
| None => x = None, |
| } |
| }; |
| } |
| "#, |
| expect![[r#" |
| *|* |
| ```rust |
| {closure#0} // size = 8, align = 8, niches = 1 |
| impl FnMut() |
| ``` |
| |
| ## Captures |
| * `x` by mutable borrow |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_ranged_closure() { |
| check_hover_range( |
| r#" |
| //- minicore: fn |
| struct S; |
| struct S2; |
| fn main() { |
| let x = &S; |
| let y = ($0|| {x; S2}$0).call(); |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| {closure#0} // size = 8, align = 8, niches = 1 |
| impl FnOnce() -> S2 |
| ``` |
| Coerced to: &impl FnOnce() -> S2 |
| |
| ## Captures |
| * `x` by move"#]], |
| ); |
| check_hover_range_actions( |
| r#" |
| //- minicore: fn |
| struct S; |
| struct S2; |
| fn main() { |
| let x = &S; |
| let y = ($0|| {x; S2}$0).call(); |
| } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::S2", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 10..20, |
| focus_range: 17..19, |
| name: "S2", |
| kind: Struct, |
| description: "struct S2", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..9, |
| focus_range: 7..8, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "core::ops::function::FnOnce", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 1, |
| ), |
| full_range: 4294967295..4294967295, |
| focus_range: 4294967295..4294967295, |
| name: "FnOnce", |
| kind: Trait, |
| container_name: "function", |
| description: "pub trait FnOnce<Args>\nwhere\n Args: Tuple,", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_long_type_of_an_expression() { |
| check( |
| r#" |
| struct Scan<A, B, C> { a: A, b: B, c: C } |
| struct Iter<I> { inner: I } |
| enum Option<T> { Some(T), None } |
| |
| struct OtherStruct<T> { i: T } |
| |
| fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> { |
| Iter { inner: Scan { a, b, c } } |
| } |
| |
| fn main() { |
| let num: i32 = 55; |
| let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> { |
| Option::Some(*memo + value) |
| }; |
| let number = 5u32; |
| let mut iter$0 = scan(OtherStruct { i: num }, closure, number); |
| } |
| "#, |
| expect![[r#" |
| *iter* |
| |
| ```rust |
| // size = 8, align = 4 |
| let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_fn_signature() { |
| // Single file with result |
| check( |
| r#" |
| pub fn foo() -> u32 { 1 } |
| |
| fn main() { let foo_test = fo$0o(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo() -> u32 |
| ``` |
| "#]], |
| ); |
| |
| // Use literal `crate` in path |
| check( |
| r#" |
| pub struct X; |
| |
| fn foo() -> crate::X { X } |
| |
| fn main() { f$0oo(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo() -> crate::X |
| ``` |
| "#]], |
| ); |
| |
| // Check `super` in path |
| check( |
| r#" |
| pub struct X; |
| |
| mod m { pub fn foo() -> super::X { super::X } } |
| |
| fn main() { m::f$0oo(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::m |
| ``` |
| |
| ```rust |
| pub fn foo() -> super::X |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_omits_unnamed_where_preds() { |
| check( |
| r#" |
| pub fn foo(bar: impl T) { } |
| |
| fn main() { fo$0o(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo(bar: impl T) |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| pub fn foo<V: AsRef<str>>(bar: impl T, baz: V) { } |
| |
| fn main() { fo$0o(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo<V>(bar: impl T, baz: V) |
| where |
| V: AsRef<str>, |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_fn_signature_with_type_params() { |
| check( |
| r#" |
| pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { } |
| |
| fn main() { let foo_test = fo$0o(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo<'a, T>(b: &'a T) -> &'a str |
| where |
| T: AsRef<str>, |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_fn_signature_on_fn_name() { |
| check( |
| r#" |
| pub fn foo$0(a: u32, b: u32) -> u32 {} |
| |
| fn main() { } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo(a: u32, b: u32) -> u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_fn_doc() { |
| check( |
| r#" |
| /// # Example |
| /// ``` |
| /// # use std::path::Path; |
| /// # |
| /// foo(Path::new("hello, world!")) |
| /// ``` |
| pub fn foo$0(_: &Path) {} |
| |
| fn main() { } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo(_: &Path) |
| ``` |
| |
| --- |
| |
| # Example |
| |
| ``` |
| # use std::path::Path; |
| # |
| foo(Path::new("hello, world!")) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_fn_doc_attr_raw_string() { |
| check( |
| r##" |
| #[doc = r#"Raw string doc attr"#] |
| pub fn foo$0(_: &Path) {} |
| |
| fn main() { } |
| "##, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo(_: &Path) |
| ``` |
| |
| --- |
| |
| Raw string doc attr |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_field_offset() { |
| // Hovering over the field when instantiating |
| check( |
| r#" |
| struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } |
| "#, |
| expect![[r#" |
| *field_a* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 1, align = 1, offset = 6 |
| field_a: u8 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_struct_field_info() { |
| // Hovering over the field when instantiating |
| check( |
| r#" |
| struct Foo { pub field_a: u32 } |
| |
| fn main() { |
| let foo = Foo { field_a$0: 0, }; |
| } |
| "#, |
| expect![[r#" |
| *field_a* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 4, align = 4, offset = 0 |
| pub field_a: u32 |
| ``` |
| "#]], |
| ); |
| |
| // Hovering over the field in the definition |
| check( |
| r#" |
| struct Foo { pub field_a$0: u32 } |
| |
| fn main() { |
| let foo = Foo { field_a: 0 }; |
| } |
| "#, |
| expect![[r#" |
| *field_a* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 4, align = 4, offset = 0 |
| pub field_a: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_shows_tuple_struct_field_info() { |
| check( |
| r#" |
| struct Foo(pub u32) |
| |
| fn main() { |
| let foo = Foo { 0$0: 0, }; |
| } |
| "#, |
| expect![[r#" |
| *0* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 4, align = 4, offset = 0 |
| pub 0: u32 |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| struct Foo(pub u32) |
| |
| fn foo(foo: Foo) { |
| foo.0$0; |
| } |
| "#, |
| expect![[r#" |
| *0* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 4, align = 4, offset = 0 |
| pub 0: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_tuple_struct() { |
| check( |
| r#" |
| struct Foo$0(pub u32) where u32: Copy; |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| struct Foo(pub u32) |
| where |
| u32: Copy, |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_record_struct() { |
| check( |
| r#" |
| struct Foo$0 { field: u32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| struct Foo { |
| field: u32, |
| } |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| struct Foo$0 where u32: Copy { field: u32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| struct Foo |
| where |
| u32: Copy, |
| { |
| field: u32, |
| } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_record_struct_limit() { |
| check_hover_fields_limit( |
| 3, |
| r#" |
| struct Foo$0 { a: u32, b: i32, c: i32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4 |
| struct Foo { |
| a: u32, |
| b: i32, |
| c: i32, |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 3, |
| r#" |
| struct Foo$0 { a: u32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| struct Foo { |
| a: u32, |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 3, |
| r#" |
| struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 16 (0x10), align = 4 |
| struct Foo { |
| a: u32, |
| b: i32, |
| c: i32, |
| /* … */ |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| None, |
| r#" |
| struct Foo$0 { a: u32, b: i32, c: i32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4 |
| struct Foo |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 0, |
| r#" |
| struct Foo$0 { a: u32, b: i32, c: i32 } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4 |
| struct Foo { /* … */ } |
| ``` |
| "#]], |
| ); |
| |
| // No extra spaces within `{}` when there are no fields |
| check_hover_fields_limit( |
| 5, |
| r#" |
| struct Foo$0 {} |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct Foo {} |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_record_variant_limit() { |
| check_hover_fields_limit( |
| 3, |
| r#" |
| enum Foo { A$0 { a: u32, b: i32, c: i32 } } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4 |
| A { a: u32, b: i32, c: i32, } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 3, |
| r#" |
| enum Foo { A$0 { a: u32 } } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| A { a: u32, } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 3, |
| r#" |
| enum Foo { A$0 { a: u32, b: i32, c: i32, d: u32 } } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 16 (0x10), align = 4 |
| A { a: u32, b: i32, c: i32, /* … */ } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| None, |
| r#" |
| enum Foo { A$0 { a: u32, b: i32, c: i32 } } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4 |
| A |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 0, |
| r#" |
| enum Foo { A$0 { a: u32, b: i32, c: i32 } } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4 |
| A { /* … */ } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_enum_limit() { |
| check_hover_enum_variants_limit( |
| 5, |
| r#"enum Foo$0 { A, B }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 1, align = 1, niches = 254 |
| enum Foo { |
| A, |
| B, |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_enum_variants_limit( |
| 1, |
| r#"enum Foo$0 { A, B }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 1, align = 1, niches = 254 |
| enum Foo { |
| A, |
| /* … */ |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_enum_variants_limit( |
| 0, |
| r#"enum Foo$0 { A, B }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 1, align = 1, niches = 254 |
| enum Foo { /* … */ } |
| ``` |
| "#]], |
| ); |
| check_hover_enum_variants_limit( |
| None, |
| r#"enum Foo$0 { A, B }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 1, align = 1, niches = 254 |
| enum Foo |
| ``` |
| "#]], |
| ); |
| check_hover_enum_variants_limit( |
| 7, |
| r#"enum Enum$0 { |
| Variant {}, |
| Variant2 { field: i32 }, |
| Variant3 { field: i32, field2: i32 }, |
| Variant4(), |
| Variant5(i32), |
| Variant6(i32, i32), |
| Variant7, |
| Variant8, |
| }"#, |
| expect![[r#" |
| *Enum* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 12 (0xC), align = 4, niches = 4294967288 |
| enum Enum { |
| Variant {}, |
| Variant2 { /* … */ }, |
| Variant3 { /* … */ }, |
| Variant4(), |
| Variant5( /* … */ ), |
| Variant6( /* … */ ), |
| Variant7, |
| /* … */ |
| } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_union_limit() { |
| check_hover_fields_limit( |
| 5, |
| r#"union Foo$0 { a: u32, b: i32 }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| union Foo { |
| a: u32, |
| b: i32, |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 1, |
| r#"union Foo$0 { a: u32, b: i32 }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| union Foo { |
| a: u32, |
| /* … */ |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| 0, |
| r#"union Foo$0 { a: u32, b: i32 }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| union Foo { /* … */ } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| None, |
| r#"union Foo$0 { a: u32, b: i32 }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| union Foo |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_unit_struct() { |
| check( |
| r#" |
| struct Foo$0 where u32: Copy; |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct Foo |
| where |
| u32: Copy, |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_type_alias() { |
| check( |
| r#" |
| type Fo$0o: Trait = S where T: Trait; |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| type Foo: Trait = S |
| where |
| T: Trait, |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_static() { |
| check( |
| r#"const foo$0: u32 = 123;"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const foo: u32 = 123 (0x7B) |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| const foo$0: u32 = { |
| let x = foo(); |
| x + 100 |
| };"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const foo: u32 = { |
| let x = foo(); |
| x + 100 |
| } |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#"static foo$0: u32 = 456;"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| static foo: u32 = 456 |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#"const FOO$0: i32 = -2147483648;"#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: i32 = -2147483648 (0x80000000) |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| const FOO: i32 = -2147483648; |
| const BAR$0: bool = FOO > 0; |
| "#, |
| expect![[r#" |
| *BAR* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const BAR: bool = false |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_eval_complex_constants() { |
| check( |
| r#" |
| struct X { f1: (), f2: i32 } |
| const foo$0: (i8, X, i64) = (1, X { f2: 5 - 1, f1: () }, 1 - 2); |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const foo: (i8, X, i64) = (1, X { f1: (), f2: 4 }, -1) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_default_generic_types() { |
| check( |
| r#" |
| struct Test<K, T = u8> { k: K, t: T } |
| |
| fn main() { |
| let zz$0 = Test { t: 23u8, k: 33 }; |
| }"#, |
| expect![[r#" |
| *zz* |
| |
| ```rust |
| // size = 8, align = 4 |
| let zz: Test<i32> |
| ``` |
| "#]], |
| ); |
| check_hover_range( |
| r#" |
| struct Test<K, T = u8> { k: K, t: T } |
| |
| fn main() { |
| let $0zz$0 = Test { t: 23u8, k: 33 }; |
| }"#, |
| expect![[r#" |
| ```rust |
| Test<i32, u8> |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_some() { |
| check( |
| r#" |
| enum Option<T> { Some(T) } |
| use Option::Some; |
| |
| fn main() { So$0me(12); } |
| "#, |
| expect![[r#" |
| *Some* |
| |
| ```rust |
| test::Option |
| ``` |
| |
| ```rust |
| Some(T) |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| enum Option<T> { Some(T) } |
| use Option::Some; |
| |
| fn main() { let b$0ar = Some(12); } |
| "#, |
| expect![[r#" |
| *bar* |
| |
| ```rust |
| // size = 4, align = 4 |
| let bar: Option<i32> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_enum_variant() { |
| check( |
| r#" |
| enum Option<T> { |
| Some(T) |
| /// The None variant |
| Non$0e |
| } |
| "#, |
| expect![[r#" |
| *None* |
| |
| ```rust |
| test::Option |
| ``` |
| |
| ```rust |
| None |
| ``` |
| |
| --- |
| |
| The None variant |
| "#]], |
| ); |
| |
| check( |
| r#" |
| enum Option<T> { |
| /// The Some variant |
| Some(T) |
| } |
| fn main() { |
| let s = Option::Som$0e(12); |
| } |
| "#, |
| expect![[r#" |
| *Some* |
| |
| ```rust |
| test::Option |
| ``` |
| |
| ```rust |
| Some(T) |
| ``` |
| |
| --- |
| |
| The Some variant |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_for_local_variable() { |
| check( |
| r#"fn func(foo: i32) { fo$0o; }"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| // size = 4, align = 4 |
| foo: i32 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_for_local_variable_pat() { |
| check( |
| r#"fn func(fo$0o: i32) {}"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| // size = 4, align = 4 |
| foo: i32 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_local_var_edge() { |
| check( |
| r#"fn func(foo: i32) { if true { $0foo; }; }"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| // size = 4, align = 4 |
| foo: i32 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_for_param_edge() { |
| check( |
| r#"fn func($0foo: i32) {}"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| // size = 4, align = 4 |
| foo: i32 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_for_param_with_multiple_traits() { |
| check( |
| r#" |
| //- minicore: sized |
| trait Deref { |
| type Target: ?Sized; |
| } |
| trait DerefMut { |
| type Target: ?Sized; |
| } |
| fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#, |
| expect![[r#" |
| *_x* |
| |
| ```rust |
| _x: impl Deref<Target = u8> + DerefMut<Target = u8> |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_infer_associated_method_result() { |
| check( |
| r#" |
| struct Thing { x: u32 } |
| |
| impl Thing { |
| fn new() -> Thing { Thing { x: 0 } } |
| } |
| |
| fn main() { let foo_$0test = Thing::new(); } |
| "#, |
| expect![[r#" |
| *foo_test* |
| |
| ```rust |
| // size = 4, align = 4 |
| let foo_test: Thing |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_infer_associated_method_exact() { |
| check( |
| r#" |
| mod wrapper { |
| pub struct Thing { x: u32 } |
| |
| impl Thing { |
| pub fn new() -> Thing { Thing { x: 0 } } |
| } |
| } |
| |
| fn main() { let foo_test = wrapper::Thing::new$0(); } |
| "#, |
| expect![[r#" |
| *new* |
| |
| ```rust |
| test::wrapper::Thing |
| ``` |
| |
| ```rust |
| pub fn new() -> Thing |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_infer_associated_const_in_pattern() { |
| check( |
| r#" |
| struct X; |
| impl X { |
| const C: u32 = 1; |
| } |
| |
| fn main() { |
| match 1 { |
| X::C$0 => {}, |
| 2 => {}, |
| _ => {} |
| }; |
| } |
| "#, |
| expect![[r#" |
| *C* |
| |
| ```rust |
| test::X |
| ``` |
| |
| ```rust |
| const C: u32 = 1 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_self() { |
| check( |
| r#" |
| struct Thing { x: u32 } |
| impl Thing { |
| fn new() -> Self { Self$0 { x: 0 } } |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| struct Thing { |
| x: u32, |
| } |
| ``` |
| "#]], |
| ); |
| check_hover_fields_limit( |
| None, |
| r#" |
| struct Thing { x: u32 } |
| impl Thing { |
| fn new() -> Self$0 { Self { x: 0 } } |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| struct Thing |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| struct Thing { x: u32 } |
| impl Thing { |
| fn new() -> Self$0 { Self { x: 0 } } |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| struct Thing { |
| x: u32, |
| } |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| enum Thing { A } |
| impl Thing { |
| pub fn new() -> Self$0 { Thing::A } |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| enum Thing { |
| A, |
| } |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| enum Thing { A } |
| impl Thing { |
| pub fn thing(a: Self$0) {} |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| enum Thing { |
| A, |
| } |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| impl usize { |
| pub fn thing(a: Self$0) {} |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| usize |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| impl fn() -> usize { |
| pub fn thing(a: Self$0) {} |
| } |
| "#, |
| expect![[r#" |
| *Self* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn() -> usize |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_shadowing_pat() { |
| check( |
| r#" |
| fn x() {} |
| |
| fn y() { |
| let x = 0i32; |
| x$0; |
| } |
| "#, |
| expect![[r#" |
| *x* |
| |
| ```rust |
| // size = 4, align = 4 |
| let x: i32 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_macro_invocation() { |
| check( |
| r#" |
| macro_rules! foo { (a) => {}; () => {} } |
| |
| fn f() { fo$0o!(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| macro_rules! foo // matched arm #1 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_macro2_invocation() { |
| check( |
| r#" |
| /// foo bar |
| /// |
| /// foo bar baz |
| macro foo() {} |
| |
| fn f() { fo$0o!(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| macro foo // matched arm #0 |
| ``` |
| |
| --- |
| |
| foo bar |
| |
| foo bar baz |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_tuple_field() { |
| check( |
| r#"struct TS(String, i32$0);"#, |
| expect![[r#" |
| *i32* |
| |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_through_macro() { |
| check( |
| r#" |
| macro_rules! id { ($($tt:tt)*) => { $($tt)* } } |
| fn foo() {} |
| id! { |
| fn bar() { fo$0o(); } |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo() |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_through_attr() { |
| check( |
| r#" |
| //- proc_macros: identity |
| #[proc_macros::identity] |
| fn foo$0() {} |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo() |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_through_expr_in_macro() { |
| check( |
| r#" |
| macro_rules! id { ($($tt:tt)*) => { $($tt)* } } |
| fn foo(bar:u32) { let a = id!(ba$0r); } |
| "#, |
| expect![[r#" |
| *bar* |
| |
| ```rust |
| // size = 4, align = 4 |
| bar: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_through_expr_in_macro_recursive() { |
| check( |
| r#" |
| macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } } |
| macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } } |
| fn foo(bar:u32) { let a = id!(ba$0r); } |
| "#, |
| expect![[r#" |
| *bar* |
| |
| ```rust |
| // size = 4, align = 4 |
| bar: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_through_func_in_macro_recursive() { |
| check( |
| r#" |
| macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } } |
| macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } } |
| fn bar() -> u32 { 0 } |
| fn foo() { let a = id!([0u32, bar$0()] ); } |
| "#, |
| expect![[r#" |
| *bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn bar() -> u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_through_assert_macro() { |
| check( |
| r#" |
| #[rustc_builtin_macro] |
| macro_rules! assert {} |
| |
| fn bar() -> bool { true } |
| fn foo() { |
| assert!(ba$0r()); |
| } |
| "#, |
| expect![[r#" |
| *bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn bar() -> bool |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_multiple_actions() { |
| check_actions( |
| r#" |
| struct Bar; |
| struct Foo { bar: Bar } |
| |
| fn foo(Foo { b$0ar }: &Foo) {} |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Bar", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..11, |
| focus_range: 7..10, |
| name: "Bar", |
| kind: Struct, |
| description: "struct Bar", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_hover_non_ascii_space_doc() { |
| check( |
| " |
| /// <- `\u{3000}` here |
| fn foo() { } |
| |
| fn bar() { fo$0o(); } |
| ", |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo() |
| ``` |
| |
| --- |
| |
| \<- ` ` here |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_function_show_qualifiers() { |
| check( |
| r#"async fn foo$0() {}"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| async fn foo() |
| ``` |
| "#]], |
| ); |
| check( |
| r#"pub const unsafe fn foo$0() {}"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub const unsafe fn foo() |
| ``` |
| "#]], |
| ); |
| // Top level `pub(crate)` will be displayed as no visibility. |
| check( |
| r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::m |
| ``` |
| |
| ```rust |
| pub(crate) async unsafe extern "C" fn foo() |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_function_show_types() { |
| check( |
| r#"fn foo$0(a: i32, b:i32) -> i32 { 0 }"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo(a: i32, b: i32) -> i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_function_associated_type_params() { |
| check( |
| r#" |
| trait Foo { type Bar; } |
| impl Foo for i32 { type Bar = i64; } |
| fn foo(arg: <i32 as Foo>::Bar) {} |
| fn main() { foo$0; } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo(arg: <i32 as Foo>::Bar) |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| trait Foo<T> { type Bar<U>; } |
| impl Foo<i64> for i32 { type Bar<U> = i32; } |
| fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {} |
| fn main() { foo$0; } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_function_pointer_show_identifiers() { |
| check( |
| r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 8, align = 8, niches = 1 |
| type foo = fn(a: i32, b: i32) -> i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_function_pointer_no_identifier() { |
| check( |
| r#"type foo$0 = fn(i32, _: i32) -> i32;"#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 8, align = 8, niches = 1 |
| type foo = fn(i32, i32) -> i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_trait_show_qualifiers() { |
| check_actions( |
| r"unsafe trait foo$0() {}", |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 13, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_extern_crate() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:std |
| //! Crate docs |
| |
| /// Decl docs! |
| extern crate st$0d; |
| //- /std/lib.rs crate:std |
| //! Standard library for this test |
| //! |
| //! Printed? |
| //! abc123 |
| "#, |
| expect![[r#" |
| *std* |
| |
| ```rust |
| main |
| ``` |
| |
| ```rust |
| extern crate std |
| ``` |
| |
| --- |
| |
| Decl docs! |
| |
| Standard library for this test |
| |
| Printed? |
| abc123 |
| "#]], |
| ); |
| check( |
| r#" |
| //- /main.rs crate:main deps:std |
| //! Crate docs |
| |
| /// Decl docs! |
| extern crate std as ab$0c; |
| //- /std/lib.rs crate:std |
| //! Standard library for this test |
| //! |
| //! Printed? |
| //! abc123 |
| "#, |
| expect![[r#" |
| *abc* |
| |
| ```rust |
| main |
| ``` |
| |
| ```rust |
| extern crate std as abc |
| ``` |
| |
| --- |
| |
| Decl docs! |
| |
| Standard library for this test |
| |
| Printed? |
| abc123 |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_mod_with_same_name_as_function() { |
| check( |
| r#" |
| use self::m$0y::Bar; |
| mod my { pub struct Bar; } |
| |
| fn my() {} |
| "#, |
| expect![[r#" |
| *my* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| mod my |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_struct_doc_comment() { |
| check( |
| r#" |
| /// This is an example |
| /// multiline doc |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// let five = 5; |
| /// |
| /// assert_eq!(6, my_crate::add_one(5)); |
| /// ``` |
| struct Bar; |
| |
| fn foo() { let bar = Ba$0r; } |
| "#, |
| expect![[r#" |
| *Bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct Bar |
| ``` |
| |
| --- |
| |
| This is an example |
| multiline doc |
| |
| # Example |
| |
| ``` |
| let five = 5; |
| |
| assert_eq!(6, my_crate::add_one(5)); |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_struct_doc_attr() { |
| check( |
| r#" |
| #[doc = "bar docs"] |
| struct Bar; |
| |
| fn foo() { let bar = Ba$0r; } |
| "#, |
| expect![[r#" |
| *Bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct Bar |
| ``` |
| |
| --- |
| |
| bar docs |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_struct_doc_attr_multiple_and_mixed() { |
| check( |
| r#" |
| /// bar docs 0 |
| #[doc = "bar docs 1"] |
| #[doc = "bar docs 2"] |
| struct Bar; |
| |
| fn foo() { let bar = Ba$0r; } |
| "#, |
| expect![[r#" |
| *Bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct Bar |
| ``` |
| |
| --- |
| |
| bar docs 0 |
| bar docs 1 |
| bar docs 2 |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_external_url() { |
| check( |
| r#" |
| pub struct Foo; |
| /// [external](https://www.google.com) |
| pub struct B$0ar |
| "#, |
| expect![[r#" |
| *Bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| pub struct Bar |
| ``` |
| |
| --- |
| |
| [external](https://www.google.com) |
| "#]], |
| ); |
| } |
| |
| // Check that we don't rewrite links which we can't identify |
| #[test] |
| fn test_hover_unknown_target() { |
| check( |
| r#" |
| pub struct Foo; |
| /// [baz](Baz) |
| pub struct B$0ar |
| "#, |
| expect![[r#" |
| *Bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| pub struct Bar |
| ``` |
| |
| --- |
| |
| [baz](Baz) |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_no_links() { |
| check_hover_no_links( |
| r#" |
| /// Test cases: |
| /// case 1. bare URL: https://www.example.com/ |
| /// case 2. inline URL with title: [example](https://www.example.com/) |
| /// case 3. code reference: [`Result`] |
| /// case 4. code reference but miss footnote: [`String`] |
| /// case 5. autolink: <http://www.example.com/> |
| /// case 6. email address: <test@example.com> |
| /// case 7. reference: [example][example] |
| /// case 8. collapsed link: [example][] |
| /// case 9. shortcut link: [example] |
| /// case 10. inline without URL: [example]() |
| /// case 11. reference: [foo][foo] |
| /// case 12. reference: [foo][bar] |
| /// case 13. collapsed link: [foo][] |
| /// case 14. shortcut link: [foo] |
| /// case 15. inline without URL: [foo]() |
| /// case 16. just escaped text: \[foo] |
| /// case 17. inline link: [Foo](foo::Foo) |
| /// |
| /// [`Result`]: ../../std/result/enum.Result.html |
| /// [^example]: https://www.example.com/ |
| pub fn fo$0o() {} |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub fn foo() |
| ``` |
| |
| --- |
| |
| Test cases: |
| case 1. bare URL: https://www.example.com/ |
| case 2. inline URL with title: [example](https://www.example.com/) |
| case 3. code reference: `Result` |
| case 4. code reference but miss footnote: `String` |
| case 5. autolink: http://www.example.com/ |
| case 6. email address: test@example.com |
| case 7. reference: example |
| case 8. collapsed link: example |
| case 9. shortcut link: example |
| case 10. inline without URL: example |
| case 11. reference: foo |
| case 12. reference: foo |
| case 13. collapsed link: foo |
| case 14. shortcut link: foo |
| case 15. inline without URL: foo |
| case 16. just escaped text: \[foo\] |
| case 17. inline link: Foo |
| |
| [^example]: https://www.example.com/ |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_layout_of_variant() { |
| check( |
| r#"enum Foo { |
| Va$0riant1(u8, u16), |
| Variant2(i32, u8, i64), |
| }"#, |
| expect![[r#" |
| *Variant1* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| // size = 4, align = 2 |
| Variant1(u8, u16) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_layout_of_variant_generic() { |
| check( |
| r#"enum Option<T> { |
| Some(T), |
| None$0 |
| }"#, |
| expect![[r#" |
| *None* |
| |
| ```rust |
| test::Option |
| ``` |
| |
| ```rust |
| None |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_layout_generic_unused() { |
| check( |
| r#" |
| //- minicore: phantom_data |
| struct S$0<T>(core::marker::PhantomData<T>); |
| "#, |
| expect![[r#" |
| *S* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct S<T>(PhantomData<T>) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_layout_of_enum() { |
| check( |
| r#"enum $0Foo { |
| Variant1(u8, u16), |
| Variant2(i32, u8, i64), |
| }"#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 16 (0x10), align = 8, niches = 254 |
| enum Foo { |
| Variant1( /* … */ ), |
| Variant2( /* … */ ), |
| } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_no_memory_layout() { |
| check_hover_no_memory_layout( |
| r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#, |
| expect![[r#" |
| *field_a* |
| |
| ```rust |
| test::Foo |
| ``` |
| |
| ```rust |
| field_a: u8 |
| ``` |
| "#]], |
| ); |
| |
| check_hover_no_memory_layout( |
| r#" |
| //- minicore: copy |
| fn main() { |
| let x = 2; |
| let y = $0|z| x + z; |
| } |
| "#, |
| expect![[r#" |
| *|* |
| ```rust |
| {closure#0} |
| impl Fn(i32) -> i32 |
| ``` |
| |
| ## Captures |
| * `x` by immutable borrow |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_macro_generated_struct_fn_doc_comment() { |
| cov_mark::check!(hover_macro_generated_struct_fn_doc_comment); |
| |
| check( |
| r#" |
| macro_rules! bar { |
| () => { |
| struct Bar; |
| impl Bar { |
| /// Do the foo |
| fn foo(&self) {} |
| } |
| } |
| } |
| |
| bar!(); |
| |
| fn foo() { let bar = Bar; bar.fo$0o(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::Bar |
| ``` |
| |
| ```rust |
| fn foo(&self) |
| ``` |
| |
| --- |
| |
| Do the foo |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_macro_generated_struct_fn_doc_attr() { |
| cov_mark::check!(hover_macro_generated_struct_fn_doc_attr); |
| |
| check( |
| r#" |
| macro_rules! bar { |
| () => { |
| struct Bar; |
| impl Bar { |
| #[doc = "Do the foo"] |
| fn foo(&self) {} |
| } |
| } |
| } |
| |
| bar!(); |
| |
| fn foo() { let bar = Bar; bar.fo$0o(); } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::Bar |
| ``` |
| |
| ```rust |
| fn foo(&self) |
| ``` |
| |
| --- |
| |
| Do the foo |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_variadic_function() { |
| check( |
| r#" |
| extern "C" { |
| pub fn foo(bar: i32, ...) -> i32; |
| } |
| |
| fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::<extern> |
| ``` |
| |
| ```rust |
| pub unsafe fn foo(bar: i32, ...) -> i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_trait_has_impl_action() { |
| check_actions( |
| r#"trait foo$0() {}"#, |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 6, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_struct_has_impl_action() { |
| check_actions( |
| r"struct foo$0() {}", |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 7, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_union_has_impl_action() { |
| check_actions( |
| r#"union foo$0() {}"#, |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 6, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_enum_has_impl_action() { |
| check_actions( |
| r"enum foo$0() { A, B }", |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 5, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_self_has_impl_action() { |
| check_actions( |
| r#"struct foo where Self$0:;"#, |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 7, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_test_has_action() { |
| check_actions( |
| r#" |
| #[test] |
| fn foo_$0test() {} |
| "#, |
| expect![[r#" |
| [ |
| Reference( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 11, |
| }, |
| ), |
| Runnable( |
| Runnable { |
| use_name_in_title: false, |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..24, |
| focus_range: 11..19, |
| name: "foo_test", |
| kind: Function, |
| }, |
| kind: Test { |
| test_id: Path( |
| "foo_test", |
| ), |
| attr: TestAttr { |
| ignore: false, |
| }, |
| }, |
| cfg: None, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_test_mod_has_action() { |
| check_actions( |
| r#" |
| mod tests$0 { |
| #[test] |
| fn foo_test() {} |
| } |
| "#, |
| expect![[r#" |
| [ |
| Runnable( |
| Runnable { |
| use_name_in_title: false, |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..46, |
| focus_range: 4..9, |
| name: "tests", |
| kind: Module, |
| description: "mod tests", |
| }, |
| kind: TestMod { |
| path: "tests", |
| }, |
| cfg: None, |
| }, |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_struct_has_goto_type_action() { |
| check_actions( |
| r#" |
| struct S{ f1: u32 } |
| |
| fn main() { let s$0t = S{ f1:0 }; } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..19, |
| focus_range: 7..8, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_generic_struct_has_goto_type_actions() { |
| check_actions( |
| r#" |
| struct Arg(u32); |
| struct S<T>{ f1: T } |
| |
| fn main() { let s$0t = S{ f1:Arg(0) }; } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Arg", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..16, |
| focus_range: 7..10, |
| name: "Arg", |
| kind: Struct, |
| description: "struct Arg(u32)", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 17..37, |
| focus_range: 24..25, |
| name: "S", |
| kind: Struct, |
| description: "struct S<T>", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_generic_excludes_sized_go_to_action() { |
| check_actions( |
| r#" |
| //- minicore: sized |
| struct S<T$0>(T); |
| "#, |
| expect![[r#" |
| [] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_generic_struct_has_flattened_goto_type_actions() { |
| check_actions( |
| r#" |
| struct Arg(u32); |
| struct S<T>{ f1: T } |
| |
| fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Arg", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..16, |
| focus_range: 7..10, |
| name: "Arg", |
| kind: Struct, |
| description: "struct Arg(u32)", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 17..37, |
| focus_range: 24..25, |
| name: "S", |
| kind: Struct, |
| description: "struct S<T>", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_tuple_has_goto_type_actions() { |
| check_actions( |
| r#" |
| struct A(u32); |
| struct B(u32); |
| mod M { |
| pub struct C(u32); |
| } |
| |
| fn main() { let s$0t = (A(1), B(2), M::C(3) ); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::A", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..14, |
| focus_range: 7..8, |
| name: "A", |
| kind: Struct, |
| description: "struct A(u32)", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::B", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 15..29, |
| focus_range: 22..23, |
| name: "B", |
| kind: Struct, |
| description: "struct B(u32)", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::M::C", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 42..60, |
| focus_range: 53..54, |
| name: "C", |
| kind: Struct, |
| container_name: "M", |
| description: "pub struct C(u32)", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_return_impl_trait_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo {} |
| fn foo() -> impl Foo {} |
| |
| fn main() { let s$0t = foo(); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Foo", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..12, |
| focus_range: 6..9, |
| name: "Foo", |
| kind: Trait, |
| description: "trait Foo", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_generic_return_impl_trait_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo<T> {} |
| struct S; |
| fn foo() -> impl Foo<S> {} |
| |
| fn main() { let s$0t = foo(); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Foo", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..15, |
| focus_range: 6..9, |
| name: "Foo", |
| kind: Trait, |
| description: "trait Foo<T>", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 16..25, |
| focus_range: 23..24, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_return_impl_traits_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo {} |
| trait Bar {} |
| fn foo() -> impl Foo + Bar {} |
| |
| fn main() { let s$0t = foo(); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Bar", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 13..25, |
| focus_range: 19..22, |
| name: "Bar", |
| kind: Trait, |
| description: "trait Bar", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::Foo", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..12, |
| focus_range: 6..9, |
| name: "Foo", |
| kind: Trait, |
| description: "trait Foo", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_generic_return_impl_traits_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo<T> {} |
| trait Bar<T> {} |
| struct S1 {} |
| struct S2 {} |
| |
| fn foo() -> impl Foo<S1> + Bar<S2> {} |
| |
| fn main() { let s$0t = foo(); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Bar", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 16..31, |
| focus_range: 22..25, |
| name: "Bar", |
| kind: Trait, |
| description: "trait Bar<T>", |
| |