mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 15:32:06 +00:00
Merge #9761
9761: feat: Show coerced types on type hover r=Veykril a=Veykril This applies to both the ranged hover request as well as the normal hover type fallback. ![image](https://user-images.githubusercontent.com/3757771/127883884-2935b624-a3e5-4f35-861a-7d6d3266d187.png) ![image](https://user-images.githubusercontent.com/3757771/127883951-4ff96b6b-7576-4886-887b-1198c1121841.png) We unfortunately have to leave out syntax highlighting here as otherwise the `Type` and `Coerced` words in the hover will get colored. Note that this does not show all the coercions yet(and almost no pattern coercions) as not all coercion adjustments are implemented yet. Closes https://github.com/rust-analyzer/rust-analyzer/issues/2677 Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
cae54d86d8
@ -225,7 +225,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||
self.imp.type_of_pat(pat)
|
||||
}
|
||||
|
||||
pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<Type> {
|
||||
pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<(Type, bool)> {
|
||||
self.imp.type_of_pat_with_coercion(expr)
|
||||
}
|
||||
|
||||
@ -577,7 +577,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
self.analyze(pat.syntax()).type_of_pat(self.db, pat)
|
||||
}
|
||||
|
||||
fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<Type> {
|
||||
fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<(Type, bool)> {
|
||||
self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat)
|
||||
}
|
||||
|
||||
|
@ -147,15 +147,15 @@ impl SourceAnalyzer {
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
pat: &ast::Pat,
|
||||
) -> Option<Type> {
|
||||
) -> Option<(Type, bool)> {
|
||||
let pat_id = self.pat_id(pat)?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
let ty = infer
|
||||
let (ty, coerced) = infer
|
||||
.pat_adjustments
|
||||
.get(&pat_id)
|
||||
.and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target))
|
||||
.unwrap_or_else(|| &infer[pat_id]);
|
||||
Type::new_with_resolver(db, &self.resolver, ty.clone())
|
||||
.and_then(|adjusts| adjusts.last().map(|adjust| (&adjust.target, true)))
|
||||
.unwrap_or_else(|| (&infer[pat_id], false));
|
||||
Type::new_with_resolver(db, &self.resolver, ty.clone()).zip(Some(coerced))
|
||||
}
|
||||
|
||||
pub(crate) fn type_of_self(
|
||||
|
@ -12,12 +12,8 @@ use ide_db::{
|
||||
use itertools::Itertools;
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
algo::{self, find_node_at_range},
|
||||
ast,
|
||||
display::fn_as_proc_macro_label,
|
||||
match_ast, AstNode, AstToken, Direction,
|
||||
SyntaxKind::*,
|
||||
SyntaxToken, T,
|
||||
algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, AstToken, Direction,
|
||||
SyntaxKind::*, SyntaxToken, T,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -79,34 +75,28 @@ pub struct HoverResult {
|
||||
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
|
||||
pub(crate) fn hover(
|
||||
db: &RootDatabase,
|
||||
range: FileRange,
|
||||
FileRange { file_id, range }: FileRange,
|
||||
config: &HoverConfig,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
let sema = hir::Semantics::new(db);
|
||||
let file = sema.parse(range.file_id).syntax().clone();
|
||||
let file = sema.parse(file_id).syntax().clone();
|
||||
|
||||
// This means we're hovering over a range.
|
||||
if !range.range.is_empty() {
|
||||
let expr = find_node_at_range::<ast::Expr>(&file, range.range)?;
|
||||
let ty = sema.type_of_expr(&expr)?;
|
||||
let offset = if range.is_empty() {
|
||||
range.start()
|
||||
} else {
|
||||
let expr = file.covering_element(range).ancestors().find_map(|it| {
|
||||
match_ast! {
|
||||
match it {
|
||||
ast::Expr(expr) => Some(Either::Left(expr)),
|
||||
ast::Pat(pat) => Some(Either::Right(pat)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
})?;
|
||||
return hover_type_info(&sema, config, expr).map(|it| RangeInfo::new(range, it));
|
||||
};
|
||||
|
||||
if ty.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut res = HoverResult::default();
|
||||
|
||||
res.markup = if config.markdown() {
|
||||
Markup::fenced_block(&ty.display(db))
|
||||
} else {
|
||||
ty.display(db).to_string().into()
|
||||
};
|
||||
|
||||
return Some(RangeInfo::new(range.range, res));
|
||||
}
|
||||
|
||||
let position = FilePosition { file_id: range.file_id, offset: range.range.start() };
|
||||
let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
|
||||
let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
||||
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
|
||||
T!['('] | T![')'] => 2,
|
||||
kind if kind.is_trivia() => 0,
|
||||
@ -114,8 +104,6 @@ pub(crate) fn hover(
|
||||
})?;
|
||||
let token = sema.descend_into_macros(token);
|
||||
|
||||
let mut res = HoverResult::default();
|
||||
|
||||
let node = token.parent()?;
|
||||
let mut range = None;
|
||||
let definition = match_ast! {
|
||||
@ -146,8 +134,8 @@ pub(crate) fn hover(
|
||||
let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
|
||||
let (idl_range, link, ns) =
|
||||
extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
|
||||
let hir::InFile { file_id, value: mapped_range } = doc_mapping.map(range)?;
|
||||
(file_id == position.file_id.into() && mapped_range.contains(position.offset)).then(||(mapped_range, link, ns))
|
||||
let mapped = doc_mapping.map(range)?;
|
||||
(mapped.file_id == file_id.into() && mapped.value.contains(offset)).then(||(mapped.value, link, ns))
|
||||
})?;
|
||||
range = Some(idl_range);
|
||||
Some(match resolve_doc_path_for_def(db,def, &link,ns)? {
|
||||
@ -176,6 +164,7 @@ pub(crate) fn hover(
|
||||
_ => None,
|
||||
};
|
||||
if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref(), config) {
|
||||
let mut res = HoverResult::default();
|
||||
res.markup = process_markup(sema.db, definition, &markup, config);
|
||||
if let Some(action) = show_implementations_action(db, definition) {
|
||||
res.actions.push(action);
|
||||
@ -185,7 +174,7 @@ pub(crate) fn hover(
|
||||
res.actions.push(action);
|
||||
}
|
||||
|
||||
if let Some(action) = runnable_action(&sema, definition, position.file_id) {
|
||||
if let Some(action) = runnable_action(&sema, definition, file_id) {
|
||||
res.actions.push(action);
|
||||
}
|
||||
|
||||
@ -207,10 +196,10 @@ pub(crate) fn hover(
|
||||
.take_while(|it| !ast::Item::can_cast(it.kind()))
|
||||
.find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
|
||||
|
||||
let ty = match_ast! {
|
||||
let expr_or_pat = match_ast! {
|
||||
match node {
|
||||
ast::Expr(it) => sema.type_of_expr(&it)?,
|
||||
ast::Pat(it) => sema.type_of_pat(&it)?,
|
||||
ast::Expr(it) => Either::Left(it),
|
||||
ast::Pat(it) => Either::Right(it),
|
||||
// If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
|
||||
// (e.g expanding a builtin macro). So we give up here.
|
||||
ast::MacroCall(_it) => return None,
|
||||
@ -218,16 +207,48 @@ pub(crate) fn hover(
|
||||
}
|
||||
};
|
||||
|
||||
res.markup = if config.markdown() {
|
||||
Markup::fenced_block(&ty.display(db))
|
||||
} else {
|
||||
ty.display(db).to_string().into()
|
||||
};
|
||||
|
||||
let res = hover_type_info(&sema, config, expr_or_pat)?;
|
||||
let range = sema.original_range(&node).range;
|
||||
Some(RangeInfo::new(range, res))
|
||||
}
|
||||
|
||||
fn hover_type_info(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
expr_or_pat: Either<ast::Expr, ast::Pat>,
|
||||
) -> Option<HoverResult> {
|
||||
let (ty, coerced) = match &expr_or_pat {
|
||||
Either::Left(expr) => sema.type_of_expr_with_coercion(expr)?,
|
||||
Either::Right(pat) => sema.type_of_pat_with_coercion(pat)?,
|
||||
};
|
||||
|
||||
let mut res = HoverResult::default();
|
||||
res.markup = if coerced {
|
||||
let uncoerced_ty = match &expr_or_pat {
|
||||
Either::Left(expr) => sema.type_of_expr(expr)?,
|
||||
Either::Right(pat) => sema.type_of_pat(pat)?,
|
||||
};
|
||||
let uncoerced = uncoerced_ty.display(sema.db).to_string();
|
||||
let coerced = ty.display(sema.db).to_string();
|
||||
format!(
|
||||
"```text\nType: {:>upad$}\nCoerced to: {:>cpad$}\n```\n",
|
||||
uncoerced = uncoerced,
|
||||
coerced = coerced,
|
||||
// 6 base padding for static text prefix of each line
|
||||
upad = 6 + coerced.len().max(uncoerced.len()),
|
||||
cpad = uncoerced.len(),
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
if config.markdown() {
|
||||
Markup::fenced_block(&ty.display(sema.db))
|
||||
} else {
|
||||
ty.display(sema.db).to_string().into()
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn try_hover_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
|
||||
let (path, tt) = attr.as_simple_call()?;
|
||||
if !tt.syntax().text_range().contains(token.text_range().start()) {
|
||||
@ -1192,7 +1213,7 @@ impl Thing {
|
||||
}
|
||||
|
||||
fn main() { let foo_$0test = Thing::new(); }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*foo_test*
|
||||
|
||||
@ -1562,7 +1583,7 @@ fn foo() {
|
||||
fn foo() {
|
||||
format!("hel$0lo {}", 0);
|
||||
}
|
||||
"#,
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1670,7 +1691,7 @@ extern crate st$0d;
|
||||
//!
|
||||
//! Printed?
|
||||
//! abc123
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*std*
|
||||
|
||||
@ -1695,7 +1716,7 @@ extern crate std as ab$0c;
|
||||
//!
|
||||
//! Printed?
|
||||
//! abc123
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*abc*
|
||||
|
||||
@ -2214,7 +2235,7 @@ mod tests$0 {
|
||||
struct S{ f1: u32 }
|
||||
|
||||
fn main() { let s$0t = S{ f1:0 }; }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
@ -2293,7 +2314,7 @@ struct Arg(u32);
|
||||
struct S<T>{ f1: T }
|
||||
|
||||
fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
@ -2482,7 +2503,7 @@ trait Bar {}
|
||||
fn foo() -> impl Foo + Bar {}
|
||||
|
||||
fn main() { let s$0t = foo(); }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
@ -2915,7 +2936,7 @@ struct B<T> {}
|
||||
struct S {}
|
||||
|
||||
fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
@ -3711,7 +3732,7 @@ mod string {
|
||||
/// This is `alloc::String`.
|
||||
pub struct String;
|
||||
}
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*String*
|
||||
|
||||
@ -3830,7 +3851,7 @@ pub fn foo() {}
|
||||
//- /lib.rs crate:main.rs deps:foo
|
||||
#[fo$0o::bar()]
|
||||
struct Foo;
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*foo*
|
||||
|
||||
@ -3846,7 +3867,7 @@ struct Foo;
|
||||
check(
|
||||
r#"
|
||||
use self as foo$0;
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*foo*
|
||||
|
||||
@ -3859,7 +3880,7 @@ use self as foo$0;
|
||||
r#"
|
||||
mod bar {}
|
||||
use bar::{self as foo$0};
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*foo*
|
||||
|
||||
@ -3877,7 +3898,7 @@ use bar::{self as foo$0};
|
||||
mod bar {
|
||||
use super as foo$0;
|
||||
}
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*foo*
|
||||
|
||||
@ -3889,7 +3910,7 @@ mod bar {
|
||||
check(
|
||||
r#"
|
||||
use crate as foo$0;
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*foo*
|
||||
|
||||
@ -3908,7 +3929,7 @@ use crate as foo$0;
|
||||
pub macro Copy {}
|
||||
#[derive(Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Copy*
|
||||
|
||||
@ -3929,7 +3950,7 @@ mod foo {
|
||||
}
|
||||
#[derive(foo::Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Copy*
|
||||
|
||||
@ -3949,7 +3970,7 @@ struct Foo;
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn f() { let expr = $01 + 2 * 3$0 }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
i32
|
||||
@ -3959,7 +3980,7 @@ fn f() { let expr = $01 + 2 * 3$0 }
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn f() { let expr = 1 $0+ 2 * $03 }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
i32
|
||||
@ -3969,7 +3990,7 @@ fn f() { let expr = 1 $0+ 2 * $03 }
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn f() { let expr = 1 + $02 * 3$0 }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
i32
|
||||
@ -3982,7 +4003,7 @@ fn f() { let expr = 1 + $02 * 3$0 }
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn f() { let expr = $0[1, 2, 3, 4]$0 }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
[i32; 4]
|
||||
@ -3992,7 +4013,7 @@ fn f() { let expr = $0[1, 2, 3, 4]$0 }
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn f() { let expr = [1, 2, $03, 4]$0 }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
[i32; 4]
|
||||
@ -4002,7 +4023,7 @@ fn f() { let expr = [1, 2, $03, 4]$0 }
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn f() { let expr = [1, 2, $03$0, 4] }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
i32
|
||||
@ -4016,7 +4037,7 @@ fn f() { let expr = [1, 2, $03$0, 4] }
|
||||
r#"
|
||||
fn f<T>(a: &[T]) { }
|
||||
fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
fn f<i32>(&[i32])
|
||||
@ -4027,7 +4048,7 @@ fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
|
||||
r#"
|
||||
fn f<T>(a: &[T]) { }
|
||||
fn b() { f($0&[1, 2, 3, 4, 5]$0); }
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
&[i32; 5]
|
||||
@ -4041,20 +4062,20 @@ fn b() { f($0&[1, 2, 3, 4, 5]$0); }
|
||||
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 }
|
||||
"#,
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
@ -4064,7 +4085,7 @@ fn $0f() { let expr = [1, 2, 3, 4]$0 }
|
||||
r#"
|
||||
fn f<T>(a: &[T]) { }
|
||||
fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
|
||||
"#,
|
||||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
()
|
||||
@ -4074,11 +4095,68 @@ fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
|
||||
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
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user