mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 11:59:49 +00:00
Show coerced types on type hover
This commit is contained in:
parent
432bb222c3
commit
486603d559
3 changed files with 120 additions and 73 deletions
|
@ -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(
|
||||
|
|
|
@ -79,34 +79,20 @@ 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)?;
|
||||
|
||||
if ty.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut res = HoverResult::default();
|
||||
|
||||
res.markup = if config.markdown() {
|
||||
Markup::fenced_block(&ty.display(db))
|
||||
let offset = if range.is_empty() {
|
||||
range.start()
|
||||
} else {
|
||||
ty.display(db).to_string().into()
|
||||
let expr = find_node_at_range::<ast::Expr>(&file, range).map(Either::Left)?;
|
||||
return hover_type_info(&sema, config, expr).map(|it| RangeInfo::new(range, it));
|
||||
};
|
||||
|
||||
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 +100,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 +130,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);
|
||||
resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
|
||||
|
@ -173,6 +157,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);
|
||||
|
@ -182,7 +167,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);
|
||||
}
|
||||
|
||||
|
@ -204,10 +189,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,
|
||||
|
@ -215,16 +200,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()) {
|
||||
|
@ -4078,4 +4095,34 @@ fn f() { let expr$0 = $0[1, 2, 3, 4] }
|
|||
```"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_range_shows_coercions_if_applicable() {
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn foo() {
|
||||
let x: &u32 = $0&&&&&0$0;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
```
|
||||
Type: &&&&&u32
|
||||
Coerced to: &u32
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
check_hover_range(
|
||||
r#"
|
||||
fn foo() {
|
||||
let x: *const u32 = $0&0$0;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
```
|
||||
Type: &u32
|
||||
Coerced to: *const u32
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue