| use expect_test::{expect, Expect}; |
| use ide_db::{base_db::FileLoader, 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 hover = analysis |
| .hover( |
| &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, |
| FileRange { file_id, range: position.range_or_empty() }, |
| ) |
| .unwrap() |
| .unwrap(); |
| 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 hover = analysis |
| .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) |
| .unwrap() |
| .unwrap(); |
| 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: 632..867, |
| focus_range: 693..699, |
| 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>", |
| }, |
| }, |
| 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::S1", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 32..44, |
| focus_range: 39..41, |
| name: "S1", |
| kind: Struct, |
| description: "struct S1", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S2", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 45..57, |
| focus_range: 52..54, |
| name: "S2", |
| kind: Struct, |
| description: "struct S2", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_arg_impl_trait_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo {} |
| fn foo(ar$0g: &impl 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_arg_impl_traits_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo {} |
| trait Bar<T> {} |
| struct S{} |
| |
| fn foo(ar$0g: &impl Foo + Bar<S>) {} |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Bar", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 13..28, |
| focus_range: 19..22, |
| name: "Bar", |
| kind: Trait, |
| description: "trait Bar<T>", |
| }, |
| }, |
| 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", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 29..39, |
| focus_range: 36..37, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_async_block_impl_trait_has_goto_type_action() { |
| check_actions( |
| r#" |
| //- /main.rs crate:main deps:core |
| // we don't use minicore here so that this test doesn't randomly fail |
| // when someone edits minicore |
| struct S; |
| fn foo() { |
| let fo$0o = async { S }; |
| } |
| //- /core.rs crate:core |
| pub mod future { |
| #[lang = "future_trait"] |
| pub trait Future {} |
| } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "core::future::Future", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 1, |
| ), |
| full_range: 21..69, |
| focus_range: 60..66, |
| name: "Future", |
| kind: Trait, |
| container_name: "future", |
| description: "pub trait Future", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "main::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..110, |
| focus_range: 108..109, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_arg_generic_impl_trait_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo<T> {} |
| struct S {} |
| fn foo(ar$0g: &impl Foo<S>) {} |
| "#, |
| 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..27, |
| focus_range: 23..24, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_dyn_return_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo<T> {} |
| struct S; |
| impl Foo<S> for S {} |
| |
| struct B<T>{} |
| fn foo() -> B<dyn Foo<S>> {} |
| |
| fn main() { let s$0t = foo(); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::B", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 48..61, |
| focus_range: 55..56, |
| name: "B", |
| kind: Struct, |
| description: "struct B<T>", |
| }, |
| }, |
| 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_dyn_arg_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo {} |
| fn foo(ar$0g: &dyn 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_dyn_arg_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo<T> {} |
| struct S {} |
| fn foo(ar$0g: &dyn Foo<S>) {} |
| "#, |
| 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..27, |
| focus_range: 23..24, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_goto_type_action_links_order() { |
| check_actions( |
| r#" |
| trait ImplTrait<T> {} |
| trait DynTrait<T> {} |
| struct B<T> {} |
| struct S {} |
| |
| fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::B", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 43..57, |
| focus_range: 50..51, |
| name: "B", |
| kind: Struct, |
| description: "struct B<T>", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::DynTrait", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 22..42, |
| focus_range: 28..36, |
| name: "DynTrait", |
| kind: Trait, |
| description: "trait DynTrait<T>", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::ImplTrait", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..21, |
| focus_range: 6..15, |
| name: "ImplTrait", |
| kind: Trait, |
| description: "trait ImplTrait<T>", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::S", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 58..69, |
| focus_range: 65..66, |
| name: "S", |
| kind: Struct, |
| description: "struct S", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_associated_type_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo { |
| type Item; |
| fn get(self) -> Self::Item {} |
| } |
| |
| struct Bar{} |
| struct S{} |
| |
| impl Foo for S { type Item = Bar; } |
| |
| fn test() -> impl Foo { S {} } |
| |
| fn main() { let s$0t = test().get(); } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Foo", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..62, |
| focus_range: 6..9, |
| name: "Foo", |
| kind: Trait, |
| description: "trait Foo", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_const_param_has_goto_type_action() { |
| check_actions( |
| r#" |
| struct Bar; |
| struct Foo<const BAR: Bar>; |
| |
| impl<const BAR: Bar> Foo<BAR$0> {} |
| "#, |
| 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_type_param_has_goto_type_action() { |
| check_actions( |
| r#" |
| trait Foo {} |
| |
| fn foo<T: Foo>(t: T$0){} |
| "#, |
| 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_self_has_go_to_type() { |
| check_actions( |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&self$0) {} |
| } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Foo", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..11, |
| focus_range: 7..10, |
| name: "Foo", |
| kind: Struct, |
| description: "struct Foo", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_displays_normalized_crate_names() { |
| check( |
| r#" |
| //- /lib.rs crate:name-with-dashes |
| pub mod wrapper { |
| pub struct Thing { x: u32 } |
| |
| impl Thing { |
| pub fn new() -> Thing { Thing { x: 0 } } |
| } |
| } |
| |
| //- /main.rs crate:main deps:name-with-dashes |
| fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); } |
| "#, |
| expect![[r#" |
| *new* |
| |
| ```rust |
| name_with_dashes::wrapper::Thing |
| ``` |
| |
| ```rust |
| pub fn new() -> Thing |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_field_pat_shorthand_ref_match_ergonomics() { |
| check( |
| r#" |
| struct S { |
| f: i32, |
| } |
| |
| fn main() { |
| let s = S { f: 0 }; |
| let S { f$0 } = &s; |
| } |
| "#, |
| expect![[r#" |
| *f* |
| |
| ```rust |
| // size = 8, align = 8, niches = 1 |
| let f: &i32 |
| ``` |
| --- |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| // size = 4, align = 4, offset = 0 |
| f: i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_order() { |
| check( |
| r#" |
| struct Foo; |
| struct S$0T<const C: usize = 1, T = Foo>(T); |
| "#, |
| expect![[r#" |
| *ST* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct ST<const C: usize = 1, T = Foo>(T) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_default_value() { |
| check( |
| r#" |
| struct Foo; |
| struct S$0T<const C: usize = {40 + 2}, T = Foo>(T); |
| "#, |
| expect![[r#" |
| *ST* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct ST<const C: usize = {const}, T = Foo>(T) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_default_value_2() { |
| check( |
| r#" |
| struct Foo; |
| const VAL = 1; |
| struct S$0T<const C: usize = VAL, T = Foo>(T); |
| "#, |
| expect![[r#" |
| *ST* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct ST<const C: usize = VAL, T = Foo>(T) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_positive_i8_literal() { |
| check( |
| r#" |
| struct Const<const N: i8>; |
| |
| fn main() { |
| let v$0alue = Const::<1>; |
| } |
| "#, |
| expect![[r#" |
| *value* |
| |
| ```rust |
| // size = 0, align = 1 |
| let value: Const<1> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_zero_i8_literal() { |
| check( |
| r#" |
| struct Const<const N: i8>; |
| |
| fn main() { |
| let v$0alue = Const::<0>; |
| } |
| "#, |
| expect![[r#" |
| *value* |
| |
| ```rust |
| // size = 0, align = 1 |
| let value: Const<0> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_negative_i8_literal() { |
| check( |
| r#" |
| struct Const<const N: i8>; |
| |
| fn main() { |
| let v$0alue = Const::<-1>; |
| } |
| "#, |
| expect![[r#" |
| *value* |
| |
| ```rust |
| // size = 0, align = 1 |
| let value: Const<-1> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_bool_literal() { |
| check( |
| r#" |
| struct Const<const F: bool>; |
| |
| fn main() { |
| let v$0alue = Const::<true>; |
| } |
| "#, |
| expect![[r#" |
| *value* |
| |
| ```rust |
| // size = 0, align = 1 |
| let value: Const<true> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn const_generic_char_literal() { |
| check( |
| r#" |
| struct Const<const C: char>; |
| |
| fn main() { |
| let v$0alue = Const::<'🦀'>; |
| } |
| "#, |
| expect![[r#" |
| *value* |
| |
| ```rust |
| // size = 0, align = 1 |
| let value: Const<'🦀'> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_self_param_shows_type() { |
| check( |
| r#" |
| struct Foo {} |
| impl Foo { |
| fn bar(&sel$0f) {} |
| } |
| "#, |
| expect![[r#" |
| *self* |
| |
| ```rust |
| // size = 8, align = 8, niches = 1 |
| self: &Foo |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_self_param_shows_type_for_arbitrary_self_type() { |
| check( |
| r#" |
| struct Arc<T>(T); |
| struct Foo {} |
| impl Foo { |
| fn bar(sel$0f: Arc<Foo>) {} |
| } |
| "#, |
| expect![[r#" |
| *self* |
| |
| ```rust |
| // size = 0, align = 1 |
| self: Arc<Foo> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_doc_outer_inner() { |
| check( |
| r#" |
| /// Be quick; |
| mod Foo$0 { |
| //! time is mana |
| |
| /// This comment belongs to the function |
| fn foo() {} |
| } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| mod Foo |
| ``` |
| |
| --- |
| |
| Be quick; |
| time is mana |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_doc_outer_inner_attribute() { |
| check( |
| r#" |
| #[doc = "Be quick;"] |
| mod Foo$0 { |
| #![doc = "time is mana"] |
| |
| #[doc = "This comment belongs to the function"] |
| fn foo() {} |
| } |
| "#, |
| expect![[r#" |
| *Foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| mod Foo |
| ``` |
| |
| --- |
| |
| Be quick; |
| time is mana |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_doc_block_style_indent_end() { |
| check( |
| r#" |
| /** |
| foo |
| ```rust |
| let x = 3; |
| ``` |
| */ |
| fn foo$0() {} |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn foo() |
| ``` |
| |
| --- |
| |
| foo |
| |
| ```rust |
| let x = 3; |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_comments_dont_highlight_parent() { |
| cov_mark::check!(no_highlight_on_comment_hover); |
| check_hover_no_result( |
| r#" |
| fn no_hover() { |
| // no$0hover |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn hover_label() { |
| check( |
| r#" |
| fn foo() { |
| 'label$0: loop {} |
| } |
| "#, |
| expect![[r#" |
| *'label* |
| |
| ```rust |
| 'label |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_lifetime() { |
| check( |
| r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#, |
| expect![[r#" |
| *'lifetime* |
| |
| ```rust |
| 'lifetime |
| ``` |
| "#]], |
| ); |
| check( |
| r#"fn foo(_: &'static$0 ()) {}"#, |
| expect![[r#" |
| *'static* |
| |
| ```rust |
| 'static |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_type_param() { |
| check( |
| r#" |
| //- minicore: sized |
| struct Foo<T>(T); |
| trait TraitA {} |
| trait TraitB {} |
| impl<T: TraitA + TraitB> Foo<T$0> where T: Sized {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: TraitA + TraitB |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: sized |
| struct Foo<T>(T); |
| impl<T> Foo<T$0> {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: sized |
| struct Foo<T>(T); |
| impl<T: 'static> Foo<T$0> {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: 'static |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_type_param_sized_bounds() { |
| // implicit `: Sized` bound |
| check( |
| r#" |
| //- minicore: sized |
| trait Trait {} |
| struct Foo<T>(T); |
| impl<T: Trait> Foo<T$0> {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: Trait |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: sized |
| trait Trait {} |
| struct Foo<T>(T); |
| impl<T: Trait + ?Sized> Foo<T$0> {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: Trait + ?Sized |
| ``` |
| "#]], |
| ); |
| } |
| |
| mod type_param_sized_bounds { |
| use super::*; |
| |
| #[test] |
| fn single_implicit() { |
| check( |
| r#" |
| //- minicore: sized |
| fn foo<T$0>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn single_explicit() { |
| check( |
| r#" |
| //- minicore: sized |
| fn foo<T$0: Sized>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn single_relaxed() { |
| check( |
| r#" |
| //- minicore: sized |
| fn foo<T$0: ?Sized>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: ?Sized |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn multiple_implicit() { |
| check( |
| r#" |
| //- minicore: sized |
| trait Trait {} |
| fn foo<T$0: Trait>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: Trait |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn multiple_explicit() { |
| check( |
| r#" |
| //- minicore: sized |
| trait Trait {} |
| fn foo<T$0: Trait + Sized>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: Trait |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn multiple_relaxed() { |
| check( |
| r#" |
| //- minicore: sized |
| trait Trait {} |
| fn foo<T$0: Trait + ?Sized>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: Trait + ?Sized |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn mixed() { |
| check( |
| r#" |
| //- minicore: sized |
| fn foo<T$0: ?Sized + Sized + Sized>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn mixed2() { |
| check( |
| r#" |
| //- minicore: sized |
| trait Trait {} |
| fn foo<T$0: Sized + ?Sized + Sized + Trait>() {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| T: Trait |
| ``` |
| "#]], |
| ); |
| } |
| } |
| |
| #[test] |
| fn hover_const_generic_type_alias() { |
| check( |
| r#" |
| struct Foo<const LEN: usize>; |
| type Fo$0o2 = Foo<2>; |
| "#, |
| expect![[r#" |
| *Foo2* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| type Foo2 = Foo<2> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_param() { |
| check( |
| r#" |
| struct Foo<const LEN: usize>; |
| impl<const LEN: usize> Foo<LEN$0> {} |
| "#, |
| expect![[r#" |
| *LEN* |
| |
| ```rust |
| const LEN: usize |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_discriminant() { |
| // Don't show hex for <10 |
| check( |
| r#" |
| #[repr(u8)] |
| enum E { |
| /// This is a doc |
| A$0 = 1 << 3, |
| } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::E |
| ``` |
| |
| ```rust |
| // size = 1, align = 1 |
| A = 8 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // Show hex for >10 |
| check( |
| r#" |
| #[repr(u8)] |
| enum E { |
| /// This is a doc |
| A$0 = (1 << 3) + (1 << 2), |
| } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::E |
| ``` |
| |
| ```rust |
| // size = 1, align = 1 |
| A = 12 (0xC) |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // enums in const eval |
| check( |
| r#" |
| #[repr(u8)] |
| enum E { |
| A = 1, |
| /// This is a doc |
| B$0 = E::A as u8 + 1, |
| } |
| "#, |
| expect![[r#" |
| *B* |
| |
| ```rust |
| test::E |
| ``` |
| |
| ```rust |
| // size = 1, align = 1 |
| B = 2 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // unspecified variant should increment by one |
| check( |
| r#" |
| #[repr(u8)] |
| enum E { |
| A = 4, |
| /// This is a doc |
| B$0, |
| } |
| "#, |
| expect![[r#" |
| *B* |
| |
| ```rust |
| test::E |
| ``` |
| |
| ```rust |
| // size = 1, align = 1 |
| B = 5 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval() { |
| check( |
| r#" |
| trait T { |
| const B: bool = false; |
| } |
| impl T for <()> { |
| /// true |
| const B: bool = true; |
| } |
| fn main() { |
| <()>::B$0; |
| } |
| "#, |
| expect![[r#" |
| *B* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const B: bool = true |
| ``` |
| |
| --- |
| |
| true |
| "#]], |
| ); |
| |
| check( |
| r#" |
| struct A { |
| i: i32 |
| }; |
| |
| trait T { |
| const AA: A = A { |
| i: 1 |
| }; |
| } |
| impl T for i32 { |
| const AA: A = A { |
| i: 2 + 3 |
| } |
| } |
| fn main() { |
| <i32>::AA$0; |
| } |
| "#, |
| expect![[r#" |
| *AA* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const AA: A = A { i: 5 } |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| trait T { |
| /// false |
| const B: bool = false; |
| } |
| impl T for () { |
| /// true |
| const B: bool = true; |
| } |
| fn main() { |
| T::B$0; |
| } |
| "#, |
| expect![[r#" |
| *B* |
| |
| ```rust |
| test::T |
| ``` |
| |
| ```rust |
| const B: bool = false |
| ``` |
| |
| --- |
| |
| false |
| "#]], |
| ); |
| |
| check( |
| r#" |
| trait T { |
| /// false |
| const B: bool = false; |
| } |
| impl T for () { |
| } |
| fn main() { |
| <()>::B$0; |
| } |
| "#, |
| expect![[r#" |
| *B* |
| |
| ```rust |
| test::T |
| ``` |
| |
| ```rust |
| const B: bool = false |
| ``` |
| |
| --- |
| |
| false |
| "#]], |
| ); |
| |
| check( |
| r#" |
| trait T { |
| /// false |
| const B: bool = false; |
| } |
| impl T for () { |
| /// true |
| const B: bool = true; |
| } |
| impl T for i32 {} |
| fn main() { |
| <i32>::B$0; |
| } |
| "#, |
| expect![[r#" |
| *B* |
| |
| ```rust |
| test::T |
| ``` |
| |
| ```rust |
| const B: bool = false |
| ``` |
| |
| --- |
| |
| false |
| "#]], |
| ); |
| |
| // show hex for <10 |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: usize = 1 << 3; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: usize = 8 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: usize = (1 << 3) + (1 << 2); |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: usize = 12 (0xC) |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // show original body when const eval fails |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: usize = 2 - 3; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: usize = 2 - 3 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // don't show hex for negatives |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: i32 = 2 - 3; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: i32 = -1 (0xFFFFFFFF) |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: &str = "bar"; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &str = "bar" |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // show char literal |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: char = 'a'; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: char = 'a' |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // show escaped char literal |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: char = '\x61'; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: char = 'a' |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // show byte literal |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: u8 = b'a'; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: u8 = 97 (0x61) |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // show escaped byte literal |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: u8 = b'\x61'; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: u8 = 97 (0x61) |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // show float literal |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: f64 = 1.0234; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f64 = 1.0234 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| //show float typecasted from int |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: f32 = 1f32; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f32 = 1.0 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // Don't show `<ref-not-supported>` in const hover |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: &i32 = &2; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &i32 = &2 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| //show f64 typecasted from float |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: f64 = 1.0f64; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f64 = 1.0 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_floating_point() { |
| check( |
| r#" |
| extern "rust-intrinsic" { |
| pub fn expf64(x: f64) -> f64; |
| } |
| |
| const FOO$0: f64 = expf64(1.2); |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f64 = 3.3201169227365472 |
| ``` |
| "#]], |
| ); |
| // check `f32` isn't double rounded via `f64` |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: f32 = 1.9999999403953552_f32; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f32 = 1.9999999 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| // Check `f16` and `f128` |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: f16 = -1.0f16; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f16 = -1.0 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| check( |
| r#" |
| /// This is a doc |
| const FOO$0: f128 = -1.0f128; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: f128 = -1.0 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_enum() { |
| check( |
| r#" |
| enum Enum { |
| V1, |
| V2, |
| } |
| |
| const VX: Enum = Enum::V1; |
| |
| const FOO$0: Enum = VX; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: Enum = V1 |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: option |
| const FOO$0: Option<i32> = Some(2); |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: Option<i32> = Some(2) |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: option |
| const FOO$0: Option<&i32> = Some(2).as_ref(); |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: Option<&i32> = Some(&2) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_dyn_trait() { |
| check( |
| r#" |
| //- minicore: fmt, coerce_unsized, builtin_impls |
| use core::fmt::Debug; |
| |
| const FOO$0: &dyn Debug = &2i32; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &dyn Debug = &2 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_slice() { |
| check( |
| r#" |
| //- minicore: slice, index, coerce_unsized |
| const FOO$0: &[i32] = &[1, 2, 3 + 4]; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &[i32] = &[1, 2, 7] |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: slice, index, coerce_unsized |
| const FOO$0: &[i32; 5] = &[12; 5]; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &[i32; 5] = &[12, 12, 12, 12, 12] |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: slice, index, coerce_unsized |
| |
| const FOO$0: (&i32, &[i32], &i32) = { |
| let a: &[i32] = &[1, 2, 3]; |
| (&a[0], a, &a[0]) |
| } |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1) |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore: slice, index, coerce_unsized |
| |
| struct Tree(&[Tree]); |
| |
| const FOO$0: Tree = { |
| let x = &[Tree(&[]), Tree(&[Tree(&[])])]; |
| Tree(&[Tree(x), Tree(x)]) |
| } |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])]) |
| ``` |
| "#]], |
| ); |
| // FIXME: Show the data of unsized structs |
| check( |
| r#" |
| //- minicore: slice, index, coerce_unsized, transmute |
| #[repr(transparent)] |
| struct S<T: ?Sized>(T); |
| const FOO$0: &S<[u8]> = core::mem::transmute::<&[u8], _>(&[1, 2, 3]); |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &S<[u8]> = &S |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_str() { |
| check( |
| r#" |
| const FOO$0: &str = "foo"; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: &str = "foo" |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| struct X { |
| a: &'static str, |
| b: &'static str, |
| } |
| const FOO$0: X = X { |
| a: "axiom", |
| b: "buy N large", |
| }; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: X = X { a: "axiom", b: "buy N large" } |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| const FOO$0: (&str, &str) = { |
| let x = "foo"; |
| (x, x) |
| }; |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: (&str, &str) = ("foo", "foo") |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_eval_in_generic_trait() { |
| // Doesn't compile, but we shouldn't crash. |
| check( |
| r#" |
| trait Trait<T> { |
| const FOO: bool = false; |
| } |
| struct S<T>(T); |
| impl<T> Trait<T> for S<T> { |
| const FOO: bool = true; |
| } |
| |
| fn test() { |
| S::FOO$0; |
| } |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| const FOO: bool = true |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_pat() { |
| check( |
| r#" |
| /// This is a doc |
| const FOO: usize = 3; |
| fn foo() { |
| match 5 { |
| FOO$0 => (), |
| _ => () |
| } |
| } |
| "#, |
| expect![[r#" |
| *FOO* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const FOO: usize = 3 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| check( |
| r#" |
| enum E { |
| /// This is a doc |
| A = 3, |
| } |
| fn foo(e: E) { |
| match e { |
| E::A$0 => (), |
| _ => () |
| } |
| } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::E |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| A = 3 |
| ``` |
| |
| --- |
| |
| This is a doc |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_const_value() { |
| check( |
| r#" |
| pub enum AA { |
| BB, |
| } |
| const CONST: AA = AA::BB; |
| pub fn the_function() -> AA { |
| CON$0ST |
| } |
| "#, |
| expect![[r#" |
| *CONST* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| const CONST: AA = BB |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn array_repeat_exp() { |
| check( |
| r#" |
| fn main() { |
| let til$0e4 = [0_u32; (4 * 8 * 8) / 32]; |
| } |
| "#, |
| expect![[r#" |
| *tile4* |
| |
| ```rust |
| // size = 32 (0x20), align = 4 |
| let tile4: [u32; 8] |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_mod_def() { |
| check( |
| r#" |
| //- /main.rs |
| mod foo$0; |
| //- /foo.rs |
| //! For the horde! |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| mod foo |
| ``` |
| |
| --- |
| |
| For the horde! |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_self_in_use() { |
| check( |
| r#" |
| //! This should not appear |
| mod foo { |
| /// But this should appear |
| pub mod bar {} |
| } |
| use foo::bar::{self$0}; |
| "#, |
| expect![[r#" |
| *self* |
| |
| ```rust |
| test::foo |
| ``` |
| |
| ```rust |
| mod bar |
| ``` |
| |
| --- |
| |
| But this should appear |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_keyword() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:std |
| fn f() { retur$0n; } |
| //- /libstd.rs crate:std |
| /// Docs for return_keyword |
| mod return_keyword {} |
| "#, |
| expect![[r#" |
| *return* |
| |
| ```rust |
| return |
| ``` |
| |
| --- |
| |
| Docs for return_keyword |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_keyword_doc() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:std |
| fn foo() { |
| let bar = mov$0e || {}; |
| } |
| //- /libstd.rs crate:std |
| #[doc(keyword = "move")] |
| /// [closure] |
| /// [closures][closure] |
| /// [threads] |
| /// <https://doc.rust-lang.org/nightly/book/ch13-01-closures.html> |
| /// |
| /// [closure]: ../book/ch13-01-closures.html |
| /// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads |
| mod move_keyword {} |
| "#, |
| expect![[r#" |
| *move* |
| |
| ```rust |
| move |
| ``` |
| |
| --- |
| |
| [closure](https://doc.rust-lang.org/stable/book/ch13-01-closures.html) |
| [closures](https://doc.rust-lang.org/stable/book/ch13-01-closures.html) |
| [threads](https://doc.rust-lang.org/stable/book/ch16-01-threads.html#using-move-closures-with-threads) |
| <https://doc.rust-lang.org/nightly/book/ch13-01-closures.html> |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_keyword_as_primitive() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:std |
| type F = f$0n(i32) -> i32; |
| //- /libstd.rs crate:std |
| /// Docs for prim_fn |
| mod prim_fn {} |
| "#, |
| expect![[r#" |
| *fn* |
| |
| ```rust |
| fn |
| ``` |
| |
| --- |
| |
| Docs for prim_fn |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_builtin() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:std |
| const _: &str$0 = ""; } |
| |
| //- /libstd.rs crate:std |
| /// Docs for prim_str |
| /// [`foo`](../std/keyword.foo.html) |
| mod prim_str {} |
| "#, |
| expect![[r#" |
| *str* |
| |
| ```rust |
| str |
| ``` |
| |
| --- |
| |
| Docs for prim_str |
| [`foo`](https://doc.rust-lang.org/nightly/std/keyword.foo.html) |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_macro_expanded_function() { |
| check( |
| r#" |
| struct S<'a, T>(&'a T); |
| trait Clone {} |
| macro_rules! foo { |
| () => { |
| fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where |
| 't: 't + 't, |
| for<'a> T: Clone + 'a |
| { 0 as _ } |
| }; |
| } |
| |
| foo!(); |
| |
| fn main() { |
| bar$0; |
| } |
| "#, |
| expect![[r#" |
| *bar* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32 |
| where |
| T: Clone + 't, |
| 't: 't + 't, |
| for<'a> T: Clone + 'a, |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_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 |
| } |
| "#, |
| expect![[r#" |
| *[`TheItem`]* |
| |
| ```rust |
| test::theitem |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| pub struct TheItem |
| ``` |
| |
| --- |
| |
| This is the item. Cool! |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_hover_trait_assoc_typealias() { |
| check( |
| r#" |
| fn main() {} |
| |
| trait T1 { |
| type Bar; |
| type Baz; |
| } |
| |
| struct Foo; |
| |
| mod t2 { |
| pub trait T2 { |
| type Bar; |
| } |
| } |
| |
| use t2::T2; |
| |
| impl T2 for Foo { |
| type Bar = String; |
| } |
| |
| impl T1 for Foo { |
| type Bar = <Foo as t2::T2>::Ba$0r; |
| // ^^^ unresolvedReference |
| } |
| "#, |
| expect![[r#" |
| *Bar* |
| |
| ```rust |
| test::t2::T2 |
| ``` |
| |
| ```rust |
| pub type Bar |
| ``` |
| "#]], |
| ); |
| } |
| #[test] |
| fn hover_generic_assoc() { |
| check( |
| r#" |
| fn foo<T: A>() where T::Assoc$0: {} |
| |
| trait A { |
| type Assoc; |
| }"#, |
| expect![[r#" |
| *Assoc* |
| |
| ```rust |
| test::A |
| ``` |
| |
| ```rust |
| type Assoc |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| fn foo<T: A>() { |
| let _: <T>::Assoc$0; |
| } |
| |
| trait A { |
| type Assoc; |
| }"#, |
| expect![[r#" |
| *Assoc* |
| |
| ```rust |
| test::A |
| ``` |
| |
| ```rust |
| type Assoc |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| trait A where |
| Self::Assoc$0: , |
| { |
| type Assoc; |
| }"#, |
| expect![[r#" |
| *Assoc* |
| |
| ```rust |
| test::A |
| ``` |
| |
| ```rust |
| type Assoc |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn string_shadowed_with_inner_items() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:alloc |
| |
| /// Custom `String` type. |
| struct String; |
| |
| fn f() { |
| let _: String$0; |
| |
| fn inner() {} |
| } |
| |
| //- /alloc.rs crate:alloc |
| #[prelude_import] |
| pub use string::*; |
| |
| mod string { |
| /// This is `alloc::String`. |
| pub struct String; |
| } |
| "#, |
| expect![[r#" |
| *String* |
| |
| ```rust |
| main |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| struct String |
| ``` |
| |
| --- |
| |
| Custom `String` type. |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn function_doesnt_shadow_crate_in_use_tree() { |
| check( |
| r#" |
| //- /main.rs crate:main deps:foo |
| use foo$0::{foo}; |
| |
| //- /foo.rs crate:foo |
| pub fn foo() {} |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| extern crate foo |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_feature() { |
| check( |
| r#"#![feature(intrinsics$0)]"#, |
| expect![[r#" |
| *intrinsics* |
| ``` |
| intrinsics |
| ``` |
| ___ |
| |
| # `intrinsics` |
| |
| The tracking issue for this feature is: None. |
| |
| Intrinsics are rarely intended to be stable directly, but are usually |
| exported in some sort of stable manner. Prefer using the stable interfaces to |
| the intrinsic directly when you can. |
| |
| ------------------------ |
| |
| |
| ## Intrinsics with fallback logic |
| |
| Many intrinsics can be written in pure rust, albeit inefficiently or without supporting |
| some features that only exist on some backends. Backends can simply not implement those |
| intrinsics without causing any code miscompilations or failures to compile. |
| All intrinsic fallback bodies are automatically made cross-crate inlineable (like `#[inline]`) |
| by the codegen backend, but not the MIR inliner. |
| |
| ```rust |
| #![feature(rustc_attrs)] |
| #![allow(internal_features)] |
| |
| #[rustc_intrinsic] |
| const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} |
| ``` |
| |
| Since these are just regular functions, it is perfectly ok to create the intrinsic twice: |
| |
| ```rust |
| #![feature(rustc_attrs)] |
| #![allow(internal_features)] |
| |
| #[rustc_intrinsic] |
| const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} |
| |
| mod foo { |
| #[rustc_intrinsic] |
| const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) { |
| panic!("noisy const dealloc") |
| } |
| } |
| |
| ``` |
| |
| The behaviour on backends that override the intrinsic is exactly the same. On other |
| backends, the intrinsic behaviour depends on which implementation is called, just like |
| with any regular function. |
| |
| ## Intrinsics lowered to MIR instructions |
| |
| Various intrinsics have native MIR operations that they correspond to. Instead of requiring |
| backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass |
| will convert the calls to the MIR operation. Backends do not need to know about these intrinsics |
| at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic" |
| or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist |
| anymore after MIR analyses. |
| |
| ## Intrinsics without fallback logic |
| |
| These must be implemented by all backends. |
| |
| ### `#[rustc_intrinsic]` declarations |
| |
| These are written like intrinsics with fallback bodies, but the body is irrelevant. |
| Use `loop {}` for the body or call the intrinsic recursively and add |
| `#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't |
| invoke the body. |
| |
| ### Legacy extern ABI based intrinsics |
| |
| These are imported as if they were FFI functions, with the special |
| `rust-intrinsic` ABI. For example, if one was in a freestanding |
| context, but wished to be able to `transmute` between types, and |
| perform efficient pointer arithmetic, one would import those functions |
| via a declaration like |
| |
| ```rust |
| #![feature(intrinsics)] |
| #![allow(internal_features)] |
| # fn main() {} |
| |
| extern "rust-intrinsic" { |
| fn transmute<T, U>(x: T) -> U; |
| |
| fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; |
| } |
| ``` |
| |
| As with any other FFI functions, these are by default always `unsafe` to call. |
| You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call. |
| |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_lint() { |
| check( |
| r#"#![allow(arithmetic_overflow$0)]"#, |
| expect![[r#" |
| *arithmetic_overflow* |
| ``` |
| arithmetic_overflow |
| ``` |
| ___ |
| |
| arithmetic operation overflows |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_clippy_lint() { |
| check( |
| r#"#![allow(clippy::almost_swapped$0)]"#, |
| expect![[r#" |
| *almost_swapped* |
| ``` |
| clippy::almost_swapped |
| ``` |
| ___ |
| |
| Checks for `foo = bar; bar = foo` sequences. |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_attr_path_qualifier() { |
| check( |
| r#" |
| //- /foo.rs crate:foo |
| |
| //- /lib.rs crate:main.rs deps:foo |
| #[fo$0o::bar()] |
| struct Foo; |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| extern crate foo |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_rename() { |
| check( |
| r#" |
| use self as foo$0; |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| extern crate test |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| mod bar {} |
| use bar::{self as foo$0}; |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| mod bar |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| mod bar { |
| use super as foo$0; |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| extern crate test |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| use crate as foo$0; |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| extern crate test |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_attribute_in_macro() { |
| check( |
| r#" |
| //- minicore:derive |
| macro_rules! identity { |
| ($struct:item) => { |
| $struct |
| }; |
| } |
| #[rustc_builtin_macro] |
| pub macro Copy {} |
| identity!{ |
| #[derive(Copy$0)] |
| struct Foo; |
| } |
| "#, |
| expect![[r#" |
| *Copy* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| macro Copy |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_derive_input() { |
| check( |
| r#" |
| //- minicore:derive |
| #[rustc_builtin_macro] |
| pub macro Copy {} |
| #[derive(Copy$0)] |
| struct Foo; |
| "#, |
| expect![[r#" |
| *Copy* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| macro Copy |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| //- minicore:derive |
| mod foo { |
| #[rustc_builtin_macro] |
| pub macro Copy {} |
| } |
| #[derive(foo::Copy$0)] |
| struct Foo; |
| "#, |
| expect![[r#" |
| *Copy* |
| |
| ```rust |
| test::foo |
| ``` |
| |
| ```rust |
| macro Copy |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_math() { |
| check_hover_range( |
| r#" |
| fn f() { let expr = $01 + 2 * 3$0 } |
| "#, |
| expect![[r#" |
| ```rust |
| i32 |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn f() { let expr = 1 $0+ 2 * $03 } |
| "#, |
| expect![[r#" |
| ```rust |
| i32 |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn f() { let expr = 1 + $02 * 3$0 } |
| "#, |
| expect![[r#" |
| ```rust |
| i32 |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_arrays() { |
| check_hover_range( |
| r#" |
| fn f() { let expr = $0[1, 2, 3, 4]$0 } |
| "#, |
| expect![[r#" |
| ```rust |
| [i32; 4] |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn f() { let expr = [1, 2, $03, 4]$0 } |
| "#, |
| expect![[r#" |
| ```rust |
| [i32; 4] |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn f() { let expr = [1, 2, $03$0, 4] } |
| "#, |
| expect![[r#" |
| ```rust |
| i32 |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_functions() { |
| check_hover_range( |
| r#" |
| fn f<T>(a: &[T]) { } |
| fn b() { $0f$0(&[1, 2, 3, 4, 5]); } |
| "#, |
| expect![[r#" |
| ```rust |
| fn f<i32>(&[i32]) |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn f<T>(a: &[T]) { } |
| fn b() { f($0&[1, 2, 3, 4, 5]$0); } |
| "#, |
| expect![[r#" |
| ```rust |
| &[i32; 5] |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_shows_nothing_when_invalid() { |
| check_hover_range_no_results( |
| r#" |
| fn f<T>(a: &[T]) { } |
| fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0 |
| "#, |
| ); |
| |
| check_hover_range_no_results( |
| r#" |
| fn f<T>$0(a: &[T]) { } |
| fn b() { f(&[1, 2, 3,$0 4, 5]); } |
| "#, |
| ); |
| |
| check_hover_range_no_results( |
| r#" |
| fn $0f() { let expr = [1, 2, 3, 4]$0 } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn hover_range_shows_unit_for_statements() { |
| check_hover_range( |
| r#" |
| fn f<T>(a: &[T]) { } |
| fn b() { $0f(&[1, 2, 3, 4, 5]); }$0 |
| "#, |
| expect![[r#" |
| ```rust |
| () |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn f() { let expr$0 = $0[1, 2, 3, 4] } |
| "#, |
| expect![[r#" |
| ```rust |
| () |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_for_pat() { |
| check_hover_range( |
| r#" |
| fn foo() { |
| let $0x$0 = 0; |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| i32 |
| ```"#]], |
| ); |
| |
| check_hover_range( |
| r#" |
| fn foo() { |
| let $0x$0 = ""; |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| &str |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_shows_coercions_if_applicable_expr() { |
| check_hover_range( |
| r#" |
| fn foo() { |
| let x: &u32 = $0&&&&&0$0; |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Type: &&&&&u32 |
| Coerced to: &u32 |
| ``` |
| "#]], |
| ); |
| check_hover_range( |
| r#" |
| fn foo() { |
| let x: *const u32 = $0&0$0; |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Type: &u32 |
| Coerced to: *const u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_range_shows_type_actions() { |
| check_actions( |
| r#" |
| struct Foo; |
| fn foo() { |
| let x: &Foo = $0&&&&&Foo$0; |
| } |
| "#, |
| expect![[r#" |
| [ |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "test::Foo", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 0..11, |
| focus_range: 7..10, |
| name: "Foo", |
| kind: Struct, |
| description: "struct Foo", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_try_expr_res() { |
| check_hover_range( |
| r#" |
| //- minicore: try, from, result |
| struct FooError; |
| |
| fn foo() -> Result<(), FooError> { |
| Ok($0Result::<(), FooError>::Ok(())?$0) |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| () |
| ```"#]], |
| ); |
| check_hover_range( |
| r#" |
| //- minicore: try, from, result |
| struct FooError; |
| struct BarError; |
| |
| fn foo() -> Result<(), FooError> { |
| Ok($0Result::<(), BarError>::Ok(())?$0) |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Try Error Type: BarError |
| Propagated as: FooError |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_try_expr() { |
| check_hover_range( |
| r#" |
| //- minicore: try |
| struct NotResult<T, U>(T, U); |
| struct Short; |
| struct Looooong; |
| |
| fn foo() -> NotResult<(), Looooong> { |
| $0NotResult((), Short)?$0; |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Try Target Type: NotResult<(), Short> |
| Propagated as: NotResult<(), Looooong> |
| ``` |
| "#]], |
| ); |
| check_hover_range( |
| r#" |
| //- minicore: try |
| struct NotResult<T, U>(T, U); |
| struct Short; |
| struct Looooong; |
| |
| fn foo() -> NotResult<(), Short> { |
| $0NotResult((), Looooong)?$0; |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Try Target Type: NotResult<(), Looooong> |
| Propagated as: NotResult<(), Short> |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_try_expr_option() { |
| cov_mark::check!(hover_try_expr_opt_opt); |
| check_hover_range( |
| r#" |
| //- minicore: option, try |
| |
| fn foo() -> Option<()> { |
| $0Some(0)?$0; |
| None |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| i32 |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_deref_expr() { |
| check_hover_range( |
| r#" |
| //- minicore: deref |
| use core::ops::Deref; |
| |
| struct DerefExample<T> { |
| value: T |
| } |
| |
| impl<T> Deref for DerefExample<T> { |
| type Target = T; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.value |
| } |
| } |
| |
| fn foo() { |
| let x = DerefExample { value: 0 }; |
| let y: i32 = $0*x$0; |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Dereferenced from: DerefExample<i32> |
| To type: i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_deref_expr_with_coercion() { |
| check_hover_range( |
| r#" |
| //- minicore: deref |
| use core::ops::Deref; |
| |
| struct DerefExample<T> { |
| value: T |
| } |
| |
| impl<T> Deref for DerefExample<T> { |
| type Target = T; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.value |
| } |
| } |
| |
| fn foo() { |
| let x = DerefExample { value: &&&&&0 }; |
| let y: &i32 = $0*x$0; |
| } |
| "#, |
| expect![[r#" |
| ```text |
| Dereferenced from: DerefExample<&&&&&i32> |
| To type: &&&&&i32 |
| Coerced to: &i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_intra_in_macro() { |
| check( |
| r#" |
| macro_rules! foo_macro { |
| ($(#[$attr:meta])* $name:ident) => { |
| $(#[$attr])* |
| pub struct $name; |
| } |
| } |
| |
| foo_macro!( |
| /// Doc comment for [`Foo$0`] |
| Foo |
| ); |
| "#, |
| expect![[r#" |
| *[`Foo`]* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| pub struct Foo |
| ``` |
| |
| --- |
| |
| Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html) |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_intra_in_attr() { |
| check( |
| r#" |
| #[doc = "Doc comment for [`Foo$0`]"] |
| pub struct Foo(i32); |
| "#, |
| expect![[r#" |
| *[`Foo`]* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| pub struct Foo(i32) |
| ``` |
| |
| --- |
| |
| Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html) |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_intra_generics() { |
| check( |
| r#" |
| /// Doc comment for [`Foo$0<T>`] |
| pub struct Foo<T>(T); |
| "#, |
| expect![[r#" |
| *[`Foo<T>`]* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub struct Foo<T>(T) |
| ``` |
| |
| --- |
| |
| Doc comment for [`Foo<T>`](https://docs.rs/test/*/test/struct.Foo.html) |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_inert_attr() { |
| check( |
| r#" |
| #[doc$0 = ""] |
| pub struct Foo; |
| "#, |
| expect![[r##" |
| *doc* |
| |
| ```rust |
| #[doc] |
| ``` |
| |
| --- |
| |
| Valid forms are: |
| |
| * \#\[doc(hidden|inline|...)\] |
| * \#\[doc = string\] |
| "##]], |
| ); |
| check( |
| r#" |
| #[allow$0()] |
| pub struct Foo; |
| "#, |
| expect![[r##" |
| *allow* |
| |
| ```rust |
| #[allow] |
| ``` |
| |
| --- |
| |
| Valid forms are: |
| |
| * \#\[allow(lint1, lint2, ..., /\*opt\*/ reason = "...")\] |
| "##]], |
| ); |
| } |
| |
| #[test] |
| fn hover_dollar_crate() { |
| // $crate should be resolved to the right crate name. |
| |
| check( |
| r#" |
| //- /main.rs crate:main deps:dep |
| dep::m!(KONST$0); |
| //- /dep.rs crate:dep |
| #[macro_export] |
| macro_rules! m { |
| ( $name:ident ) => { const $name: $crate::Type = $crate::Type; }; |
| } |
| |
| pub struct Type; |
| "#, |
| expect![[r#" |
| *KONST* |
| |
| ```rust |
| main |
| ``` |
| |
| ```rust |
| const KONST: dep::Type = Type |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_record_variant() { |
| check( |
| r#" |
| enum Enum { |
| RecordV$0 { field: u32 } |
| } |
| "#, |
| expect![[r#" |
| *RecordV* |
| |
| ```rust |
| test::Enum |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| RecordV { field: u32, } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_record_variant_field() { |
| check( |
| r#" |
| enum Enum { |
| RecordV { field$0: u32 } |
| } |
| "#, |
| expect![[r#" |
| *field* |
| |
| ```rust |
| test::RecordV |
| ``` |
| |
| ```rust |
| // size = 4, align = 4 |
| field: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_trait_impl_assoc_item_def_doc_forwarding() { |
| check( |
| r#" |
| trait T { |
| /// Trait docs |
| fn func() {} |
| } |
| impl T for () { |
| fn func$0() {} |
| } |
| "#, |
| expect![[r#" |
| *func* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| fn func() |
| ``` |
| |
| --- |
| |
| Trait docs |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_trait_show_assoc_items() { |
| check_assoc_count( |
| 0, |
| r#" |
| trait T {} |
| impl T$0 for () {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| trait T {} |
| ``` |
| "#]], |
| ); |
| |
| check_assoc_count( |
| 1, |
| r#" |
| trait T {} |
| impl T$0 for () {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| trait T {} |
| ``` |
| "#]], |
| ); |
| |
| check_assoc_count( |
| 0, |
| r#" |
| trait T { |
| fn func() {} |
| const FLAG: i32 = 34; |
| type Bar; |
| } |
| impl T$0 for () {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| trait T { /* … */ } |
| ``` |
| "#]], |
| ); |
| |
| check_assoc_count( |
| 2, |
| r#" |
| trait T { |
| fn func() {} |
| const FLAG: i32 = 34; |
| type Bar; |
| } |
| impl T$0 for () {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| trait T { |
| fn func(); |
| const FLAG: i32; |
| /* … */ |
| } |
| ``` |
| "#]], |
| ); |
| |
| check_assoc_count( |
| 3, |
| r#" |
| trait T { |
| fn func() {} |
| const FLAG: i32 = 34; |
| type Bar; |
| } |
| impl T$0 for () {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| trait T { |
| fn func(); |
| const FLAG: i32; |
| type Bar; |
| } |
| ``` |
| "#]], |
| ); |
| |
| check_assoc_count( |
| 4, |
| r#" |
| trait T { |
| fn func() {} |
| const FLAG: i32 = 34; |
| type Bar; |
| } |
| impl T$0 for () {} |
| "#, |
| expect![[r#" |
| *T* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| trait T { |
| fn func(); |
| const FLAG: i32; |
| type Bar; |
| } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_ranged_macro_call() { |
| check_hover_range( |
| r#" |
| macro_rules! __rust_force_expr { |
| ($e:expr) => { |
| $e |
| }; |
| } |
| macro_rules! vec { |
| ($elem:expr) => { |
| __rust_force_expr!($elem) |
| }; |
| } |
| |
| struct Struct; |
| impl Struct { |
| fn foo(self) {} |
| } |
| |
| fn f() { |
| $0vec![Struct]$0; |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| Struct |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_deref() { |
| check( |
| r#" |
| //- minicore: deref |
| |
| struct Struct(usize); |
| |
| impl core::ops::Deref for Struct { |
| type Target = usize; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.0 |
| } |
| } |
| |
| fn f() { |
| $0*Struct(0); |
| } |
| "#, |
| expect![[r#" |
| *** |
| |
| ```rust |
| test::Struct |
| ``` |
| |
| ```rust |
| fn deref(&self) -> &Self::Target |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn static_const_macro_expanded_body() { |
| check( |
| r#" |
| macro_rules! m { |
| () => { |
| pub const V: i8 = { |
| let e = 123; |
| f(e) // Prevent const eval from evaluating this constant, we want to print the body's code. |
| }; |
| }; |
| } |
| m!(); |
| fn main() { $0V; } |
| "#, |
| expect![[r#" |
| *V* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub const V: i8 = { |
| let e = 123; |
| f(e) |
| } |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| macro_rules! m { |
| () => { |
| pub static V: i8 = { |
| let e = 123; |
| }; |
| }; |
| } |
| m!(); |
| fn main() { $0V; } |
| "#, |
| expect![[r#" |
| *V* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| pub static V: i8 = { |
| let e = 123; |
| } |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_rest_pat() { |
| check( |
| r#" |
| struct Struct {a: u32, b: u32, c: u8, d: u16}; |
| |
| fn main() { |
| let Struct {a, c, .$0.} = Struct {a: 1, b: 2, c: 3, d: 4}; |
| } |
| "#, |
| expect![[r#" |
| *..* |
| ```rust |
| .., b: u32, d: u16 |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| struct Struct {a: u32, b: u32, c: u8, d: u16}; |
| |
| fn main() { |
| let Struct {a, b, c, d, .$0.} = Struct {a: 1, b: 2, c: 3, d: 4}; |
| } |
| "#, |
| expect![[r#" |
| *..* |
| ```rust |
| .. |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_underscore_pat() { |
| check( |
| r#" |
| fn main() { |
| let _$0 = 0; |
| } |
| "#, |
| expect![[r#" |
| *_* |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| let (_$0,) = (0,); |
| } |
| "#, |
| expect![[r#" |
| *_* |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_underscore_expr() { |
| check( |
| r#" |
| fn main() { |
| _$0 = 0; |
| } |
| "#, |
| expect![[r#" |
| *_* |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| (_$0,) = (0,); |
| } |
| "#, |
| expect![[r#" |
| *_* |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_underscore_type() { |
| check_hover_no_result( |
| r#" |
| fn main() { |
| let x: _$0 = 0; |
| } |
| "#, |
| ); |
| check_hover_no_result( |
| r#" |
| fn main() { |
| let x: (_$0,) = (0,); |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn hover_call_parens() { |
| check( |
| r#" |
| fn foo() -> i32 {} |
| fn main() { |
| foo($0); |
| } |
| "#, |
| expect![[r#" |
| *)* |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| struct S; |
| impl S { |
| fn foo(self) -> i32 {} |
| } |
| fn main() { |
| S.foo($0); |
| } |
| "#, |
| expect![[r#" |
| *)* |
| ```rust |
| i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn assoc_fn_in_block_local_impl() { |
| check( |
| r#" |
| struct S; |
| mod m { |
| const _: () = { |
| impl crate::S { |
| pub(crate) fn foo() {} |
| } |
| }; |
| } |
| fn test() { |
| S::foo$0(); |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| pub(crate) fn foo() |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| struct S; |
| mod m { |
| const _: () = { |
| const _: () = { |
| impl crate::S { |
| pub(crate) fn foo() {} |
| } |
| }; |
| }; |
| } |
| fn test() { |
| S::foo$0(); |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| pub(crate) fn foo() |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| struct S; |
| mod m { |
| mod inner { |
| const _: () = { |
| impl crate::S { |
| pub(super) fn foo() {} |
| } |
| }; |
| } |
| |
| fn test() { |
| crate::S::foo$0(); |
| } |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| pub(super) fn foo() |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn assoc_const_in_block_local_impl() { |
| check( |
| r#" |
| struct S; |
| mod m { |
| const _: () = { |
| impl crate::S { |
| pub(crate) const A: () = (); |
| } |
| }; |
| } |
| fn test() { |
| S::A$0; |
| } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| pub(crate) const A: () = () |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| struct S; |
| mod m { |
| const _: () = { |
| const _: () = { |
| impl crate::S { |
| pub(crate) const A: () = (); |
| } |
| }; |
| }; |
| } |
| fn test() { |
| S::A$0; |
| } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| pub(crate) const A: () = () |
| ``` |
| "#]], |
| ); |
| |
| check( |
| r#" |
| struct S; |
| mod m { |
| mod inner { |
| const _: () = { |
| impl crate::S { |
| pub(super) const A: () = (); |
| } |
| }; |
| } |
| |
| fn test() { |
| crate::S::A$0; |
| } |
| } |
| "#, |
| expect![[r#" |
| *A* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| pub(super) const A: () = () |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn field_as_method_call_fallback() { |
| check( |
| r#" |
| struct S { f: u32 } |
| fn test() { |
| S { f: 0 }.f$0(); |
| } |
| "#, |
| expect![[r#" |
| *f* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| // size = 4, align = 4, offset = 0 |
| f: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn generic_params_disabled_by_cfg() { |
| check( |
| r#" |
| struct S<#[cfg(never)] T>; |
| fn test() { |
| let s$0: S = S; |
| } |
| "#, |
| expect![[r#" |
| *s* |
| |
| ```rust |
| // size = 0, align = 1 |
| let s: S |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn format_args_arg() { |
| check( |
| r#" |
| //- minicore: fmt |
| fn test() { |
| let foo = 0; |
| format_args!("{}", foo$0); |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| // size = 4, align = 4 |
| let foo: i32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn format_args_implicit() { |
| check( |
| r#" |
| //- minicore: fmt |
| fn test() { |
| let aaaaa = "foo"; |
| format_args!("{aaaaa$0}"); |
| } |
| "#, |
| expect![[r#" |
| *aaaaa* |
| |
| ```rust |
| // size = 16 (0x10), align = 8, niches = 1 |
| let aaaaa: &str |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn format_args_implicit2() { |
| check( |
| r#" |
| //- minicore: fmt |
| fn test() { |
| let aaaaa = "foo"; |
| format_args!("{$0aaaaa}"); |
| } |
| "#, |
| expect![[r#" |
| *aaaaa* |
| |
| ```rust |
| // size = 16 (0x10), align = 8, niches = 1 |
| let aaaaa: &str |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn format_args_implicit_raw() { |
| check( |
| r#" |
| //- minicore: fmt |
| fn test() { |
| let aaaaa = "foo"; |
| format_args!(r"{$0aaaaa}"); |
| } |
| "#, |
| expect![[r#" |
| *aaaaa* |
| |
| ```rust |
| // size = 16 (0x10), align = 8, niches = 1 |
| let aaaaa: &str |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn format_args_implicit_nested() { |
| check( |
| r#" |
| //- minicore: fmt |
| macro_rules! foo { |
| ($($tt:tt)*) => { |
| format_args!($($tt)*) |
| } |
| } |
| fn test() { |
| let aaaaa = "foo"; |
| foo!(r"{$0aaaaa}"); |
| } |
| "#, |
| expect![[r#" |
| *aaaaa* |
| |
| ```rust |
| // size = 16 (0x10), align = 8, niches = 1 |
| let aaaaa: &str |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn method_call_without_parens() { |
| check( |
| r#" |
| struct S; |
| impl S { |
| fn foo<T>(&self, t: T) {} |
| } |
| |
| fn main() { |
| S.foo$0; |
| } |
| "#, |
| expect![[r#" |
| *foo* |
| |
| ```rust |
| test::S |
| ``` |
| |
| ```rust |
| fn foo<T>(&self, t: T) |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn string_literal() { |
| check( |
| r#" |
| fn main() { |
| $0"🦀\u{1f980}\\\x41"; |
| } |
| "#, |
| expect![[r#" |
| *"🦀\u{1f980}\\\x41"* |
| ```rust |
| &str |
| ``` |
| ___ |
| |
| value of literal: 🦀🦀\A |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0r"🦀\u{1f980}\\\x41"; |
| } |
| "#, |
| expect![[r#" |
| *r"🦀\u{1f980}\\\x41"* |
| ```rust |
| &str |
| ``` |
| ___ |
| |
| value of literal: 🦀\u{1f980}\\\x41 |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0r"🦀\u{1f980}\\\x41 |
| |
| |
| fsdghs"; |
| } |
| "#, |
| expect![[r#" |
| *r"🦀\u{1f980}\\\x41 |
| |
| |
| fsdghs"* |
| ```rust |
| &str |
| ``` |
| ___ |
| |
| value of literal (truncated up to newline): 🦀\u{1f980}\\\x41 |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn cstring_literal() { |
| check( |
| r#" |
| fn main() { |
| $0c"🦀\u{1f980}\\\x41"; |
| } |
| "#, |
| expect![[r#" |
| *c"🦀\u{1f980}\\\x41"* |
| ```rust |
| &{unknown} |
| ``` |
| ___ |
| |
| value of literal: 🦀🦀\A |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn byte_string_literal() { |
| check( |
| r#" |
| fn main() { |
| $0b"\xF0\x9F\xA6\x80\\"; |
| } |
| "#, |
| expect![[r#" |
| *b"\xF0\x9F\xA6\x80\\"* |
| ```rust |
| &[u8; 5] |
| ``` |
| ___ |
| |
| value of literal: [240, 159, 166, 128, 92] |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0br"\xF0\x9F\xA6\x80\\"; |
| } |
| "#, |
| expect![[r#" |
| *br"\xF0\x9F\xA6\x80\\"* |
| ```rust |
| &[u8; 18] |
| ``` |
| ___ |
| |
| value of literal: [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn byte_literal() { |
| check( |
| r#" |
| fn main() { |
| $0b'\xF0'; |
| } |
| "#, |
| expect![[r#" |
| *b'\xF0'* |
| ```rust |
| u8 |
| ``` |
| ___ |
| |
| value of literal: 0xF0 |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0b'\\'; |
| } |
| "#, |
| expect![[r#" |
| *b'\\'* |
| ```rust |
| u8 |
| ``` |
| ___ |
| |
| value of literal: 0x5C |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn char_literal() { |
| check( |
| r#" |
| fn main() { |
| $0'\x41'; |
| } |
| "#, |
| expect![[r#" |
| *'\x41'* |
| ```rust |
| char |
| ``` |
| ___ |
| |
| value of literal: A |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0'\\'; |
| } |
| "#, |
| expect![[r#" |
| *'\\'* |
| ```rust |
| char |
| ``` |
| ___ |
| |
| value of literal: \ |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0'\u{1f980}'; |
| } |
| "#, |
| expect![[r#" |
| *'\u{1f980}'* |
| ```rust |
| char |
| ``` |
| ___ |
| |
| value of literal: 🦀 |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn float_literal() { |
| check( |
| r#" |
| fn main() { |
| $01.0; |
| } |
| "#, |
| expect![[r#" |
| *1.0* |
| ```rust |
| f64 |
| ``` |
| ___ |
| |
| value of literal: 1 (bits: 0x3FF0000000000000) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $01.0f16; |
| } |
| "#, |
| expect![[r#" |
| *1.0f16* |
| ```rust |
| f16 |
| ``` |
| ___ |
| |
| value of literal: 1 (bits: 0x3C00) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $01.0f32; |
| } |
| "#, |
| expect![[r#" |
| *1.0f32* |
| ```rust |
| f32 |
| ``` |
| ___ |
| |
| value of literal: 1 (bits: 0x3F800000) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $01.0f128; |
| } |
| "#, |
| expect![[r#" |
| *1.0f128* |
| ```rust |
| f128 |
| ``` |
| ___ |
| |
| value of literal: 1 (bits: 0x3FFF0000000000000000000000000000) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0134e12; |
| } |
| "#, |
| expect![[r#" |
| *134e12* |
| ```rust |
| f64 |
| ``` |
| ___ |
| |
| value of literal: 134000000000000 (bits: 0x42DE77D399980000) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $01523527134274733643531312.0; |
| } |
| "#, |
| expect![[r#" |
| *1523527134274733643531312.0* |
| ```rust |
| f64 |
| ``` |
| ___ |
| |
| value of literal: 1523527134274733600000000 (bits: 0x44F429E9249F629B) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $00.1ea123; |
| } |
| "#, |
| expect![[r#" |
| *0.1ea123* |
| ```rust |
| f64 |
| ``` |
| ___ |
| |
| invalid literal: invalid float literal |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn int_literal() { |
| check( |
| r#" |
| fn main() { |
| $034325236457856836345234; |
| } |
| "#, |
| expect![[r#" |
| *34325236457856836345234* |
| ```rust |
| i32 |
| ``` |
| ___ |
| |
| value of literal: 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $0134_123424_21; |
| } |
| "#, |
| expect![[r#" |
| *134_123424_21* |
| ```rust |
| i32 |
| ``` |
| ___ |
| |
| value of literal: 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $00x12423423; |
| } |
| "#, |
| expect![[r#" |
| *0x12423423* |
| ```rust |
| i32 |
| ``` |
| ___ |
| |
| value of literal: 306328611 (0x12423423|0b10010010000100011010000100011) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $00b1111_1111; |
| } |
| "#, |
| expect![[r#" |
| *0b1111_1111* |
| ```rust |
| i32 |
| ``` |
| ___ |
| |
| value of literal: 255 (0xFF|0b11111111) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $00o12345; |
| } |
| "#, |
| expect![[r#" |
| *0o12345* |
| ```rust |
| i32 |
| ``` |
| ___ |
| |
| value of literal: 5349 (0x14E5|0b1010011100101) |
| "#]], |
| ); |
| check( |
| r#" |
| fn main() { |
| $00xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F; |
| } |
| "#, |
| expect![[r#" |
| *0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F* |
| ```rust |
| i32 |
| ``` |
| ___ |
| |
| invalid literal: number too large to fit in target type |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn notable_local() { |
| check( |
| r#" |
| #[doc(notable_trait)] |
| trait Notable { |
| type Assoc; |
| type Assoc2; |
| } |
| |
| impl Notable for u32 { |
| type Assoc = &str; |
| type Assoc2 = char; |
| } |
| fn main(notable$0: u32) {} |
| "#, |
| expect![[r#" |
| *notable* |
| |
| ```rust |
| // Implements notable traits: Notable<Assoc = &str, Assoc2 = char> |
| // size = 4, align = 4 |
| notable: u32 |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn notable_foreign() { |
| check( |
| r#" |
| //- minicore: future, iterator |
| struct S; |
| #[doc(notable_trait)] |
| trait Notable {} |
| impl Notable for S$0 {} |
| impl core::future::Future for S { |
| type Output = u32; |
| } |
| impl Iterator for S { |
| type Item = S; |
| } |
| "#, |
| expect![[r#" |
| *S* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S> |
| // size = 0, align = 1 |
| struct S |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn extern_items() { |
| check( |
| r#" |
| extern "C" { |
| static STATIC$0: (); |
| } |
| "#, |
| expect![[r#" |
| *STATIC* |
| |
| ```rust |
| test::<extern> |
| ``` |
| |
| ```rust |
| static STATIC: () |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| extern "C" { |
| fn fun$0(); |
| } |
| "#, |
| expect![[r#" |
| *fun* |
| |
| ```rust |
| test::<extern> |
| ``` |
| |
| ```rust |
| unsafe fn fun() |
| ``` |
| "#]], |
| ); |
| check( |
| r#" |
| extern "C" { |
| type Ty$0; |
| } |
| "#, |
| expect![[r#" |
| *Ty* |
| |
| ```rust |
| test::<extern> |
| ``` |
| |
| ```rust |
| // size = 0, align = 1 |
| type Ty |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn notable_ranged() { |
| check_hover_range( |
| r#" |
| //- minicore: future, iterator |
| struct S; |
| #[doc(notable_trait)] |
| trait Notable {} |
| impl Notable for S {} |
| impl core::future::Future for S { |
| type Output = u32; |
| } |
| impl Iterator for S { |
| type Item = S; |
| } |
| fn main() { |
| $0S$0; |
| } |
| "#, |
| expect![[r#" |
| ```rust |
| // Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S> |
| S |
| ```"#]], |
| ); |
| } |
| |
| #[test] |
| fn notable_actions() { |
| check_actions( |
| r#" |
| //- minicore: future, iterator |
| struct S; |
| struct S2; |
| #[doc(notable_trait)] |
| trait Notable {} |
| impl Notable for S$0 {} |
| impl core::future::Future for S { |
| type Output = u32; |
| } |
| impl Iterator for S { |
| type Item = S2; |
| } |
| "#, |
| expect![[r#" |
| [ |
| Implementation( |
| FilePositionWrapper { |
| file_id: FileId( |
| 0, |
| ), |
| offset: 7, |
| }, |
| ), |
| GoToType( |
| [ |
| HoverGotoTypeData { |
| mod_path: "core::future::Future", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 1, |
| ), |
| full_range: 7800..8008, |
| focus_range: 7865..7871, |
| name: "Future", |
| kind: Trait, |
| container_name: "future", |
| description: "pub trait Future", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "core::iter::traits::iterator::Iterator", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 1, |
| ), |
| full_range: 8638..9104, |
| focus_range: 8682..8690, |
| name: "Iterator", |
| kind: Trait, |
| container_name: "iterator", |
| description: "pub trait Iterator", |
| }, |
| }, |
| HoverGotoTypeData { |
| mod_path: "test::Notable", |
| nav: NavigationTarget { |
| file_id: FileId( |
| 0, |
| ), |
| full_range: 21..59, |
| focus_range: 49..56, |
| name: "Notable", |
| kind: Trait, |
| description: "trait Notable", |
| }, |
| }, |
| 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", |
| }, |
| }, |
| ], |
| ), |
| ] |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_lifetime_regression_16963() { |
| check( |
| r#" |
| struct Pedro$0<'a> { |
| hola: &'a str |
| } |
| "#, |
| expect![[r#" |
| *Pedro* |
| |
| ```rust |
| test |
| ``` |
| |
| ```rust |
| // size = 16 (0x10), align = 8, niches = 1 |
| struct Pedro<'a> { |
| hola: &str, |
| } |
| ``` |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn hover_impl_trait_arg_self() { |
| check( |
| r#" |
| trait T<Rhs = Self> {} |
| fn main(a$0: impl T) {} |
| "#, |
| expect![[r#" |
| *a* |
| |
| ```rust |
| a: impl T + ?Sized |
| ``` |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn hover_struct_default_arg_self() { |
| check( |
| r#" |
| struct T<Rhs = Self> {} |
| fn main(a$0: T) {} |
| "#, |
| expect![[r#" |
| *a* |
| |
| ```rust |
| // size = 0, align = 1 |
| a: T |
| ``` |
| "#]], |
| ); |
| } |