summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide/src/hover
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide/src/hover')
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs163
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs248
2 files changed, 256 insertions, 155 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 47257f0bf..22611cfb8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -26,58 +26,24 @@ use syntax::{
use crate::{
doc_links::{remove_links, rewrite_links},
hover::walk_and_push_ty,
- markdown_remove::remove_markdown,
HoverAction, HoverConfig, HoverResult, Markup,
};
-pub(super) fn type_info(
+pub(super) fn type_info_of(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ _config: &HoverConfig,
expr_or_pat: &Either<ast::Expr, ast::Pat>,
) -> Option<HoverResult> {
let TypeInfo { original, adjusted } = match expr_or_pat {
Either::Left(expr) => sema.type_of_expr(expr)?,
Either::Right(pat) => sema.type_of_pat(pat)?,
};
-
- let mut res = HoverResult::default();
- let mut targets: Vec<hir::ModuleDef> = Vec::new();
- let mut push_new_def = |item: hir::ModuleDef| {
- if !targets.contains(&item) {
- targets.push(item);
- }
- };
- walk_and_push_ty(sema.db, &original, &mut push_new_def);
-
- res.markup = if let Some(adjusted_ty) = adjusted {
- walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
- let original = original.display(sema.db).to_string();
- let adjusted = adjusted_ty.display(sema.db).to_string();
- let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
- format!(
- "{bt_start}Type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
- original,
- adjusted,
- apad = static_text_diff_len + adjusted.len().max(original.len()),
- opad = original.len(),
- bt_start = if config.markdown() { "```text\n" } else { "" },
- bt_end = if config.markdown() { "```\n" } else { "" }
- )
- .into()
- } else {
- if config.markdown() {
- Markup::fenced_block(&original.display(sema.db))
- } else {
- original.display(sema.db).to_string().into()
- }
- };
- res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
- Some(res)
+ type_info(sema, _config, original, adjusted)
}
pub(super) fn try_expr(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ _config: &HoverConfig,
try_expr: &ast::TryExpr,
) -> Option<HoverResult> {
let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
@@ -153,14 +119,12 @@ pub(super) fn try_expr(
let ppad = static_text_len_diff.min(0).abs() as usize;
res.markup = format!(
- "{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}",
+ "```text\n{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n```\n",
s,
inner_ty,
body_ty,
pad0 = ty_len_max + tpad,
pad1 = ty_len_max + ppad,
- bt_start = if config.markdown() { "```text\n" } else { "" },
- bt_end = if config.markdown() { "```\n" } else { "" }
)
.into();
Some(res)
@@ -168,7 +132,7 @@ pub(super) fn try_expr(
pub(super) fn deref_expr(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ _config: &HoverConfig,
deref_expr: &ast::PrefixExpr,
) -> Option<HoverResult> {
let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
@@ -197,15 +161,13 @@ pub(super) fn deref_expr(
.max(adjusted.len() + coerced_len)
.max(inner.len() + deref_len);
format!(
- "{bt_start}Dereferenced from: {:>ipad$}\nTo type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
+ "```text\nDereferenced from: {:>ipad$}\nTo type: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
inner,
original,
adjusted,
ipad = max_len - deref_len,
apad = max_len - type_len,
opad = max_len - coerced_len,
- bt_start = if config.markdown() { "```text\n" } else { "" },
- bt_end = if config.markdown() { "```\n" } else { "" }
)
.into()
} else {
@@ -215,13 +177,11 @@ pub(super) fn deref_expr(
let deref_len = "Dereferenced from: ".len();
let max_len = (original.len() + type_len).max(inner.len() + deref_len);
format!(
- "{bt_start}Dereferenced from: {:>ipad$}\nTo type: {:>apad$}\n{bt_end}",
+ "```text\nDereferenced from: {:>ipad$}\nTo type: {:>apad$}\n```\n",
inner,
original,
ipad = max_len - deref_len,
apad = max_len - type_len,
- bt_start = if config.markdown() { "```text\n" } else { "" },
- bt_end = if config.markdown() { "```\n" } else { "" }
)
.into()
};
@@ -230,12 +190,54 @@ pub(super) fn deref_expr(
Some(res)
}
+pub(super) fn underscore(
+ sema: &Semantics<'_, RootDatabase>,
+ config: &HoverConfig,
+ token: &SyntaxToken,
+) -> Option<HoverResult> {
+ if token.kind() != T![_] {
+ return None;
+ }
+ let parent = token.parent()?;
+ let _it = match_ast! {
+ match parent {
+ ast::InferType(it) => it,
+ ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it))),
+ ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it))),
+ _ => return None,
+ }
+ };
+ // let it = infer_type.syntax().parent()?;
+ // match_ast! {
+ // match it {
+ // ast::LetStmt(_it) => (),
+ // ast::Param(_it) => (),
+ // ast::RetType(_it) => (),
+ // ast::TypeArg(_it) => (),
+
+ // ast::CastExpr(_it) => (),
+ // ast::ParenType(_it) => (),
+ // ast::TupleType(_it) => (),
+ // ast::PtrType(_it) => (),
+ // ast::RefType(_it) => (),
+ // ast::ArrayType(_it) => (),
+ // ast::SliceType(_it) => (),
+ // ast::ForType(_it) => (),
+ // _ => return None,
+ // }
+ // }
+
+ // FIXME: https://github.com/rust-lang/rust-analyzer/issues/11762, this currently always returns Unknown
+ // type_info(sema, config, sema.resolve_type(&ast::Type::InferType(it))?, None)
+ None
+}
+
pub(super) fn keyword(
sema: &Semantics<'_, RootDatabase>,
config: &HoverConfig,
token: &SyntaxToken,
) -> Option<HoverResult> {
- if !token.kind().is_keyword() || !config.documentation.is_some() || !config.keywords {
+ if !token.kind().is_keyword() || !config.documentation || !config.keywords {
return None;
}
let parent = token.parent()?;
@@ -259,7 +261,7 @@ pub(super) fn keyword(
/// i.e. `let S {a, ..} = S {a: 1, b: 2}`
pub(super) fn struct_rest_pat(
sema: &Semantics<'_, RootDatabase>,
- config: &HoverConfig,
+ _config: &HoverConfig,
pattern: &RecordPat,
) -> HoverResult {
let missing_fields = sema.record_pattern_missing_fields(pattern);
@@ -288,11 +290,7 @@ pub(super) fn struct_rest_pat(
// get rid of trailing comma
s.truncate(s.len() - 2);
- if config.markdown() {
- Markup::fenced_block(&s)
- } else {
- s.into()
- }
+ Markup::fenced_block(&s)
};
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
res
@@ -346,13 +344,8 @@ pub(super) fn process_markup(
config: &HoverConfig,
) -> Markup {
let markup = markup.as_str();
- let markup = if !config.markdown() {
- remove_markdown(markup)
- } else if config.links_in_hover {
- rewrite_links(db, markup, def)
- } else {
- remove_links(markup)
- };
+ let markup =
+ if config.links_in_hover { rewrite_links(db, markup, def) } else { remove_links(markup) };
Markup::from(markup)
}
@@ -465,8 +458,9 @@ pub(super) fn definition(
Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None),
};
- let docs = match config.documentation {
- Some(_) => docs.or_else(|| {
+ let docs = docs
+ .filter(|_| config.documentation)
+ .or_else(|| {
// docs are missing, for assoc items of trait impls try to fall back to the docs of the
// original item of the trait
let assoc = def.as_assoc_item(db)?;
@@ -474,13 +468,46 @@ pub(super) fn definition(
let name = Some(assoc.name(db)?);
let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
item.docs(db)
- }),
- None => None,
- };
- let docs = docs.filter(|_| config.documentation.is_some()).map(Into::into);
+ })
+ .map(Into::into);
markup(docs, label, mod_path)
}
+fn type_info(
+ sema: &Semantics<'_, RootDatabase>,
+ _config: &HoverConfig,
+ original: hir::Type,
+ adjusted: Option<hir::Type>,
+) -> Option<HoverResult> {
+ let mut res = HoverResult::default();
+ let mut targets: Vec<hir::ModuleDef> = Vec::new();
+ let mut push_new_def = |item: hir::ModuleDef| {
+ if !targets.contains(&item) {
+ targets.push(item);
+ }
+ };
+ walk_and_push_ty(sema.db, &original, &mut push_new_def);
+
+ res.markup = if let Some(adjusted_ty) = adjusted {
+ walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+ let original = original.display(sema.db).to_string();
+ let adjusted = adjusted_ty.display(sema.db).to_string();
+ let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
+ format!(
+ "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
+ original,
+ adjusted,
+ apad = static_text_diff_len + adjusted.len().max(original.len()),
+ opad = original.len(),
+ )
+ .into()
+ } else {
+ Markup::fenced_block(&original.display(sema.db))
+ };
+ res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+ Some(res)
+}
+
fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
let name = attr.name(db);
let desc = format!("#[{name}]");
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index c7f241f2f..bd7ce2f1d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -2,7 +2,7 @@ use expect_test::{expect, Expect};
use ide_db::base_db::{FileLoader, FileRange};
use syntax::TextRange;
-use crate::{fixture, hover::HoverDocFormat, HoverConfig};
+use crate::{fixture, HoverConfig, HoverDocFormat};
fn check_hover_no_result(ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
@@ -10,8 +10,9 @@ fn check_hover_no_result(ra_fixture: &str) {
.hover(
&HoverConfig {
links_in_hover: true,
- documentation: Some(HoverDocFormat::Markdown),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::Markdown,
},
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
@@ -26,8 +27,9 @@ fn check(ra_fixture: &str, expect: Expect) {
.hover(
&HoverConfig {
links_in_hover: true,
- documentation: Some(HoverDocFormat::Markdown),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::Markdown,
},
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
@@ -47,8 +49,9 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
.hover(
&HoverConfig {
links_in_hover: false,
- documentation: Some(HoverDocFormat::Markdown),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::Markdown,
},
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
@@ -68,8 +71,9 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
.hover(
&HoverConfig {
links_in_hover: true,
- documentation: Some(HoverDocFormat::PlainText),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::PlainText,
},
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
@@ -89,8 +93,9 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
.hover(
&HoverConfig {
links_in_hover: true,
- documentation: Some(HoverDocFormat::Markdown),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::Markdown,
},
FileRange { file_id, range: position.range_or_empty() },
)
@@ -105,8 +110,9 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) {
.hover(
&HoverConfig {
links_in_hover: false,
- documentation: Some(HoverDocFormat::Markdown),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::Markdown,
},
range,
)
@@ -121,8 +127,9 @@ fn check_hover_range_no_results(ra_fixture: &str) {
.hover(
&HoverConfig {
links_in_hover: false,
- documentation: Some(HoverDocFormat::Markdown),
+ documentation: true,
keywords: true,
+ format: HoverDocFormat::Markdown,
},
range,
)
@@ -207,37 +214,20 @@ m!(ab$0c);
}
#[test]
-fn hover_shows_type_of_an_expression() {
- check(
- r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() {
- let foo_test = foo()$0;
-}
-"#,
- expect![[r#"
- *foo()*
- ```rust
- u32
- ```
- "#]],
- );
-}
-
-#[test]
fn hover_remove_markdown_if_configured() {
check_hover_no_markdown(
r#"
pub fn foo() -> u32 { 1 }
fn main() {
- let foo_test = foo()$0;
+ let foo_test = foo$0();
}
"#,
expect![[r#"
- *foo()*
- u32
+ *foo*
+ test
+
+ pub fn foo() -> u32
"#]],
);
}
@@ -297,33 +287,6 @@ fn main() { let foo_test = fo$0o(); }
"#]],
);
- // Multiple candidates but results are ambiguous.
- check(
- r#"
-//- /a.rs
-pub fn foo() -> u32 { 1 }
-
-//- /b.rs
-pub fn foo() -> &str { "" }
-
-//- /c.rs
-pub fn foo(a: u32, b: u32) {}
-
-//- /main.rs
-mod a;
-mod b;
-mod c;
-
-fn main() { let foo_test = fo$0o(); }
- "#,
- expect![[r#"
- *foo*
- ```rust
- {unknown}
- ```
- "#]],
- );
-
// Use literal `crate` in path
check(
r#"
@@ -527,6 +490,7 @@ fn hover_field_offset() {
// Hovering over the field when instantiating
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
"#,
expect![[r#"
@@ -548,6 +512,7 @@ fn hover_shows_struct_field_info() {
// Hovering over the field when instantiating
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct Foo { field_a: u32 }
fn main() {
@@ -570,6 +535,7 @@ fn main() {
// Hovering over the field in the definition
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
struct Foo { field_a$0: u32 }
fn main() {
@@ -1184,33 +1150,19 @@ fn test_hover_through_func_in_macro_recursive() {
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)] ); }
+fn foo() { let a = id!([0u32, bar$0()] ); }
"#,
expect![[r#"
- *bar()*
- ```rust
- u32
- ```
- "#]],
- );
-}
+ *bar*
-#[test]
-fn test_hover_through_literal_string_in_macro() {
- check(
- r#"
-macro_rules! arr { ($($tt:tt)*) => { [$($tt)*] } }
-fn foo() {
- let mastered_for_itunes = "";
- let _ = arr!("Tr$0acks", &mastered_for_itunes);
-}
-"#,
- expect![[r#"
- *"Tracks"*
- ```rust
- &str
- ```
- "#]],
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn bar() -> u32
+ ```
+ "#]],
);
}
@@ -1515,6 +1467,8 @@ fn my() {}
fn test_hover_struct_doc_comment() {
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
+
/// This is an example
/// multiline doc
///
@@ -1573,7 +1527,7 @@ fn foo() { let bar = Ba$0r; }
```
```rust
- struct Bar // size = 0, align = 1
+ struct Bar
```
---
@@ -1602,7 +1556,7 @@ fn foo() { let bar = Ba$0r; }
```
```rust
- struct Bar // size = 0, align = 1
+ struct Bar
```
---
@@ -1630,7 +1584,7 @@ pub struct B$0ar
```
```rust
- pub struct Bar // size = 0, align = 1
+ pub struct Bar
```
---
@@ -1657,7 +1611,7 @@ pub struct B$0ar
```
```rust
- pub struct Bar // size = 0, align = 1
+ pub struct Bar
```
---
@@ -2959,6 +2913,8 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
fn hover_field_pat_shorthand_ref_match_ergonomics() {
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
+
struct S {
f: i32,
}
@@ -4398,6 +4354,7 @@ fn main() {
fn hover_intra_doc_links() {
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
pub mod theitem {
/// This is the item. Cool!
@@ -4539,7 +4496,7 @@ trait A where
fn string_shadowed_with_inner_items() {
check(
r#"
-//- /main.rs crate:main deps:alloc
+//- /main.rs crate:main deps:alloc target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
/// Custom `String` type.
struct String;
@@ -5234,7 +5191,7 @@ foo_macro!(
```
```rust
- pub struct Foo // size = 0, align = 1
+ pub struct Foo
```
---
@@ -5248,6 +5205,8 @@ foo_macro!(
fn hover_intra_in_attr() {
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
+
#[doc = "Doc comment for [`Foo$0`]"]
pub struct Foo(i32);
"#,
@@ -5368,6 +5327,8 @@ enum Enum {
fn hover_record_variant_field() {
check(
r#"
+//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
+
enum Enum {
RecordV { field$0: u32 }
}
@@ -5573,3 +5534,116 @@ fn main() {
"#]],
);
}
+
+#[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
+ ```
+ "#]],
+ );
+}