Skip to content

Commit 25368d2

Browse files
bors[bot]Veykril
andauthored
Merge #9954
9954: feat: Show try operator propogated types on ranged hover r=matklad a=Veykril Basically this just shows the type of the inner expression of the `?` expression as well as the type of the expression that the `?` returns from: ![Code_wIrCxMqLH9](https://user-images.githubusercontent.com/3757771/130111025-f7ee0742-214a-493b-947a-b4a671e4be92.png) Unless both of these types are `core::result::Result` in which case we show the error types only. ![Code_Xruw5FCBNI](https://user-images.githubusercontent.com/3757771/130111024-f9caef82-92e4-4070-b3dd-f2ff9e5d87a9.png) If both types are `core::option::Option` with different type params we do not show this special hover either as it would be pointless(instead fallback to default type hover) Very much open to changes to the hover text here(I suppose we also want to show the actual type of the `?` expression, that is its output type?). Fixes #9931 Co-authored-by: Lukas Wirth <[email protected]>
2 parents 75acb19 + 8bbfd45 commit 25368d2

File tree

2 files changed

+206
-17
lines changed

2 files changed

+206
-17
lines changed

crates/hir_ty/src/infer/pat.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Type inference for patterns.
22
3-
use std::iter::repeat;
4-
use std::sync::Arc;
3+
use std::{iter::repeat, sync::Arc};
54

65
use chalk_ir::Mutability;
76
use hir_def::{
@@ -10,9 +9,10 @@ use hir_def::{
109
};
1110
use hir_expand::name::Name;
1211

13-
use super::{BindingMode, Expectation, InferenceContext, TypeMismatch};
1412
use crate::{
15-
infer::{Adjust, Adjustment, AutoBorrow},
13+
infer::{
14+
Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch,
15+
},
1616
lower::lower_to_chalk_mutability,
1717
static_lifetime, Interner, Substitution, Ty, TyBuilder, TyExt, TyKind,
1818
};

crates/ide/src/hover.rs

Lines changed: 202 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use ide_db::{
1212
use itertools::Itertools;
1313
use stdx::format_to;
1414
use syntax::{
15-
algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, AstToken, Direction,
16-
SyntaxKind::*, SyntaxNode, SyntaxToken, T,
15+
algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, Direction, SyntaxKind::*,
16+
SyntaxNode, SyntaxToken, T,
1717
};
1818

1919
use crate::{
@@ -112,9 +112,8 @@ pub(crate) fn hover(
112112
_ => 1,
113113
})?;
114114
let token = sema.descend_into_macros(token);
115-
116-
let mut range_override = None;
117115
let node = token.parent()?;
116+
let mut range_override = None;
118117
let definition = match_ast! {
119118
match node {
120119
ast::Name(name) => NameClass::classify(&sema, &name).map(|class| match class {
@@ -138,7 +137,7 @@ pub(crate) fn hover(
138137
),
139138
_ => {
140139
// intra-doc links
141-
if ast::Comment::cast(token.clone()).is_some() {
140+
if token.kind() == COMMENT {
142141
cov_mark::hit!(no_highlight_on_comment_hover);
143142
let (attributes, def) = doc_attributes(&sema, &node)?;
144143
let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
@@ -233,7 +232,7 @@ fn hover_ranged(
233232
sema: &Semantics<RootDatabase>,
234233
config: &HoverConfig,
235234
) -> Option<RangeInfo<HoverResult>> {
236-
let expr = file.covering_element(range).ancestors().find_map(|it| {
235+
let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
237236
match_ast! {
238237
match it {
239238
ast::Expr(expr) => Some(Either::Left(expr)),
@@ -242,15 +241,111 @@ fn hover_ranged(
242241
}
243242
}
244243
})?;
245-
hover_type_info(sema, config, &expr).map(|it| {
246-
let range = match expr {
244+
let res = match &expr_or_pat {
245+
Either::Left(ast::Expr::TryExpr(try_expr)) => hover_try_expr(sema, config, try_expr),
246+
_ => None,
247+
};
248+
let res = res.or_else(|| hover_type_info(sema, config, &expr_or_pat));
249+
res.map(|it| {
250+
let range = match expr_or_pat {
247251
Either::Left(it) => it.syntax().text_range(),
248252
Either::Right(it) => it.syntax().text_range(),
249253
};
250254
RangeInfo::new(range, it)
251255
})
252256
}
253257

258+
fn hover_try_expr(
259+
sema: &Semantics<RootDatabase>,
260+
config: &HoverConfig,
261+
try_expr: &ast::TryExpr,
262+
) -> Option<HoverResult> {
263+
let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
264+
let mut ancestors = try_expr.syntax().ancestors();
265+
let mut body_ty = loop {
266+
let next = ancestors.next()?;
267+
break match_ast! {
268+
match next {
269+
ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
270+
ast::Item(__) => return None,
271+
ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
272+
ast::EffectExpr(effect) => if matches!(effect.effect(), ast::Effect::Async(_) | ast::Effect::Try(_)| ast::Effect::Const(_)) {
273+
sema.type_of_expr(&effect.block_expr()?.into())?.original
274+
} else {
275+
continue;
276+
},
277+
_ => continue,
278+
}
279+
};
280+
};
281+
282+
if inner_ty == body_ty {
283+
return None;
284+
}
285+
286+
let mut inner_ty = inner_ty;
287+
let mut s = "Try Target".to_owned();
288+
289+
let adts = inner_ty.as_adt().zip(body_ty.as_adt());
290+
if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts {
291+
let famous_defs = FamousDefs(sema, sema.scope(&try_expr.syntax()).krate());
292+
// special case for two options, there is no value in showing them
293+
if let Some(option_enum) = famous_defs.core_option_Option() {
294+
if inner == option_enum && body == option_enum {
295+
cov_mark::hit!(hover_try_expr_opt_opt);
296+
return None;
297+
}
298+
}
299+
300+
// special case two results to show the error variants only
301+
if let Some(result_enum) = famous_defs.core_result_Result() {
302+
if inner == result_enum && body == result_enum {
303+
let error_type_args =
304+
inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1));
305+
if let Some((inner, body)) = error_type_args {
306+
inner_ty = inner;
307+
body_ty = body;
308+
s = "Try Error".to_owned();
309+
}
310+
}
311+
}
312+
}
313+
314+
let mut res = HoverResult::default();
315+
316+
let mut targets: Vec<hir::ModuleDef> = Vec::new();
317+
let mut push_new_def = |item: hir::ModuleDef| {
318+
if !targets.contains(&item) {
319+
targets.push(item);
320+
}
321+
};
322+
walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
323+
walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
324+
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
325+
326+
let inner_ty = inner_ty.display(sema.db).to_string();
327+
let body_ty = body_ty.display(sema.db).to_string();
328+
let ty_len_max = inner_ty.len().max(body_ty.len());
329+
330+
let l = "Propagated as: ".len() - " Type: ".len();
331+
let static_text_len_diff = l as isize - s.len() as isize;
332+
let tpad = static_text_len_diff.max(0) as usize;
333+
let ppad = static_text_len_diff.min(0).abs() as usize;
334+
335+
res.markup = format!(
336+
"{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}",
337+
s,
338+
inner_ty,
339+
body_ty,
340+
pad0 = ty_len_max + tpad,
341+
pad1 = ty_len_max + ppad,
342+
bt_start = if config.markdown() { "```text\n" } else { "" },
343+
bt_end = if config.markdown() { "```\n" } else { "" }
344+
)
345+
.into();
346+
Some(res)
347+
}
348+
254349
fn hover_type_info(
255350
sema: &Semantics<RootDatabase>,
256351
config: &HoverConfig,
@@ -274,13 +369,15 @@ fn hover_type_info(
274369
walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
275370
let original = original.display(sema.db).to_string();
276371
let adjusted = adjusted_ty.display(sema.db).to_string();
372+
let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
277373
format!(
278-
"```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
279-
uncoerced = original,
280-
coerced = adjusted,
281-
// 6 base padding for difference of length of the two text prefixes
282-
apad = 6 + adjusted.len().max(original.len()),
374+
"{bt_start}Type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
375+
original,
376+
adjusted,
377+
apad = static_text_diff_len + adjusted.len().max(original.len()),
283378
opad = original.len(),
379+
bt_start = if config.markdown() { "```text\n" } else { "" },
380+
bt_end = if config.markdown() { "```\n" } else { "" }
284381
)
285382
.into()
286383
} else {
@@ -4257,4 +4354,96 @@ fn foo() {
42574354
"#]],
42584355
);
42594356
}
4357+
4358+
#[test]
4359+
fn hover_try_expr_res() {
4360+
check_hover_range(
4361+
r#"
4362+
//- minicore:result
4363+
struct FooError;
4364+
4365+
fn foo() -> Result<(), FooError> {
4366+
Ok($0Result::<(), FooError>::Ok(())?$0)
4367+
}
4368+
"#,
4369+
expect![[r#"
4370+
```rust
4371+
()
4372+
```"#]],
4373+
);
4374+
check_hover_range(
4375+
r#"
4376+
//- minicore:result
4377+
struct FooError;
4378+
struct BarError;
4379+
4380+
fn foo() -> Result<(), FooError> {
4381+
Ok($0Result::<(), BarError>::Ok(())?$0)
4382+
}
4383+
"#,
4384+
expect![[r#"
4385+
```text
4386+
Try Error Type: BarError
4387+
Propagated as: FooError
4388+
```
4389+
"#]],
4390+
);
4391+
}
4392+
4393+
#[test]
4394+
fn hover_try_expr() {
4395+
check_hover_range(
4396+
r#"
4397+
struct NotResult<T, U>(T, U);
4398+
struct Short;
4399+
struct Looooong;
4400+
4401+
fn foo() -> NotResult<(), Looooong> {
4402+
$0NotResult((), Short)?$0;
4403+
}
4404+
"#,
4405+
expect![[r#"
4406+
```text
4407+
Try Target Type: NotResult<(), Short>
4408+
Propagated as: NotResult<(), Looooong>
4409+
```
4410+
"#]],
4411+
);
4412+
check_hover_range(
4413+
r#"
4414+
struct NotResult<T, U>(T, U);
4415+
struct Short;
4416+
struct Looooong;
4417+
4418+
fn foo() -> NotResult<(), Short> {
4419+
$0NotResult((), Looooong)?$0;
4420+
}
4421+
"#,
4422+
expect![[r#"
4423+
```text
4424+
Try Target Type: NotResult<(), Looooong>
4425+
Propagated as: NotResult<(), Short>
4426+
```
4427+
"#]],
4428+
);
4429+
}
4430+
4431+
#[test]
4432+
fn hover_try_expr_option() {
4433+
cov_mark::check!(hover_try_expr_opt_opt);
4434+
check_hover_range(
4435+
r#"
4436+
//- minicore: option, try
4437+
4438+
fn foo() -> Option<()> {
4439+
$0Some(0)?$0;
4440+
None
4441+
}
4442+
"#,
4443+
expect![[r#"
4444+
```rust
4445+
<Option<i32> as Try>::Output
4446+
```"#]],
4447+
);
4448+
}
42604449
}

0 commit comments

Comments
 (0)