From 95923c7b2997f479d36a2712ead123123fd962d8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 19 Aug 2021 17:33:58 +0200 Subject: [PATCH 1/3] Fix imports --- crates/hir_ty/src/infer/pat.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index df438d2ac4c..5cd760393f4 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs @@ -1,7 +1,6 @@ //! Type inference for patterns. -use std::iter::repeat; -use std::sync::Arc; +use std::{iter::repeat, sync::Arc}; use chalk_ir::Mutability; use hir_def::{ @@ -10,9 +9,10 @@ }; use hir_expand::name::Name; -use super::{BindingMode, Expectation, InferenceContext, TypeMismatch}; use crate::{ - infer::{Adjust, Adjustment, AutoBorrow}, + infer::{ + Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch, + }, lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder, TyExt, TyKind, }; From a0d5290b7fe9c8d8ca4107900123a218835a9dff Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 19 Aug 2021 18:53:25 +0200 Subject: [PATCH 2/3] Show try operator propogated types on ranged hover --- crates/ide/src/hover.rs | 209 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 198 insertions(+), 11 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 09e6156dd5d..016d38b1eb8 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -12,8 +12,8 @@ use itertools::Itertools; use stdx::format_to; use syntax::{ - algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, AstToken, Direction, - SyntaxKind::*, SyntaxNode, SyntaxToken, T, + algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, Direction, SyntaxKind::*, + SyntaxNode, SyntaxToken, T, }; use crate::{ @@ -112,9 +112,8 @@ pub(crate) fn hover( _ => 1, })?; let token = sema.descend_into_macros(token); - - let mut range_override = None; let node = token.parent()?; + let mut range_override = None; let definition = match_ast! { match node { ast::Name(name) => NameClass::classify(&sema, &name).map(|class| match class { @@ -138,7 +137,7 @@ pub(crate) fn hover( ), _ => { // intra-doc links - if ast::Comment::cast(token.clone()).is_some() { + if token.kind() == COMMENT { cov_mark::hit!(no_highlight_on_comment_hover); let (attributes, def) = doc_attributes(&sema, &node)?; let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; @@ -233,7 +232,7 @@ fn hover_ranged( sema: &Semantics, config: &HoverConfig, ) -> Option> { - let expr = file.covering_element(range).ancestors().find_map(|it| { + let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| { match_ast! { match it { ast::Expr(expr) => Some(Either::Left(expr)), @@ -242,8 +241,13 @@ fn hover_ranged( } } })?; - hover_type_info(sema, config, &expr).map(|it| { - let range = match expr { + let res = match &expr_or_pat { + Either::Left(ast::Expr::TryExpr(try_expr)) => hover_try_expr(sema, config, try_expr), + _ => None, + }; + let res = res.or_else(|| hover_type_info(sema, config, &expr_or_pat)); + res.map(|it| { + let range = match expr_or_pat { Either::Left(it) => it.syntax().text_range(), Either::Right(it) => it.syntax().text_range(), }; @@ -251,6 +255,96 @@ fn hover_ranged( }) } +fn hover_try_expr( + sema: &Semantics, + config: &HoverConfig, + try_expr: &ast::TryExpr, +) -> Option { + let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original; + let mut ancestors = try_expr.syntax().ancestors(); + let mut body_ty = loop { + let next = ancestors.next()?; + break match_ast! { + match next { + ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db), + ast::Item(__) => return None, + ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original, + ast::EffectExpr(effect) => if matches!(effect.effect(), ast::Effect::Async(_) | ast::Effect::Try(_)| ast::Effect::Const(_)) { + sema.type_of_expr(&effect.block_expr()?.into())?.original + } else { + continue; + }, + _ => continue, + } + }; + }; + + if inner_ty == body_ty { + return None; + } + + let mut inner_ty = inner_ty; + let mut s = "Try Target".to_owned(); + + let adts = inner_ty.as_adt().zip(body_ty.as_adt()); + if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts { + let famous_defs = FamousDefs(sema, sema.scope(&try_expr.syntax()).krate()); + // special case for two options, there is no value in showing them + if let Some(option_enum) = famous_defs.core_option_Option() { + if inner == option_enum && body == option_enum { + return None; + } + } + + // special case two results to show the error variants only + if let Some(result_enum) = famous_defs.core_result_Result() { + if inner == result_enum && body == result_enum { + let error_type_args = + inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1)); + if let Some((inner, body)) = error_type_args { + inner_ty = inner; + body_ty = body; + s = "Try Error".to_owned(); + } + } + } + } + + let mut res = HoverResult::default(); + + let mut targets: Vec = Vec::new(); + let mut push_new_def = |item: hir::ModuleDef| { + if !targets.contains(&item) { + targets.push(item); + } + }; + walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def); + walk_and_push_ty(sema.db, &body_ty, &mut push_new_def); + res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); + + let inner_ty = inner_ty.display(sema.db).to_string(); + let body_ty = body_ty.display(sema.db).to_string(); + let ty_len_max = inner_ty.len().max(body_ty.len()); + + // "Propagated as: ".len() - " Type: ".len() = 8 + let static_test_len_diff = 8 - s.len() as isize; + let tpad = static_test_len_diff.max(0) as usize; + let ppad = static_test_len_diff.min(0).abs() as usize; + + res.markup = format!( + "{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}", + 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) +} + fn hover_type_info( sema: &Semantics, config: &HoverConfig, @@ -275,12 +369,14 @@ fn hover_type_info( let original = original.display(sema.db).to_string(); let adjusted = adjusted_ty.display(sema.db).to_string(); format!( - "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n", - uncoerced = original, - coerced = adjusted, + "{bt_start}Type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}", + original, + adjusted, // 6 base padding for difference of length of the two text prefixes apad = 6 + 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 { @@ -4257,4 +4353,95 @@ fn foo() { "#]], ); } + + #[test] + fn hover_try_expr_res() { + check_hover_range( + r#" +//- minicore:result +struct FooError; + +fn foo() -> Result<(), FooError> { + Ok($0Result::<(), FooError>::Ok(())?$0) +} +"#, + expect![[r#" + ```rust + () + ```"#]], + ); + check_hover_range( + r#" +//- minicore: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#" +struct NotResult(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#" +struct NotResult(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() { + check_hover_range( + r#" +//- minicore: option, try + +fn foo() -> Option<()> { + $0Some(0)?$0; + None +} +"#, + expect![[r#" + ```rust + as Try>::Output + ```"#]], + ); + } } From 8bbfd45d97219a787f72d4f39adb9a529dc2065c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 1 Sep 2021 11:25:42 +0200 Subject: [PATCH 3/3] Minor fixes --- crates/ide/src/hover.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 016d38b1eb8..8c22806b4d6 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -292,6 +292,7 @@ fn hover_try_expr( // special case for two options, there is no value in showing them if let Some(option_enum) = famous_defs.core_option_Option() { if inner == option_enum && body == option_enum { + cov_mark::hit!(hover_try_expr_opt_opt); return None; } } @@ -326,10 +327,10 @@ fn hover_try_expr( let body_ty = body_ty.display(sema.db).to_string(); let ty_len_max = inner_ty.len().max(body_ty.len()); - // "Propagated as: ".len() - " Type: ".len() = 8 - let static_test_len_diff = 8 - s.len() as isize; - let tpad = static_test_len_diff.max(0) as usize; - let ppad = static_test_len_diff.min(0).abs() as usize; + let l = "Propagated as: ".len() - " Type: ".len(); + let static_text_len_diff = l as isize - s.len() as isize; + let tpad = static_text_len_diff.max(0) as usize; + let ppad = static_text_len_diff.min(0).abs() as usize; res.markup = format!( "{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}", @@ -368,12 +369,12 @@ fn hover_type_info( 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, - // 6 base padding for difference of length of the two text prefixes - apad = 6 + adjusted.len().max(original.len()), + 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 { "" } @@ -4429,6 +4430,7 @@ fn foo() -> NotResult<(), Short> { #[test] fn hover_try_expr_option() { + cov_mark::check!(hover_try_expr_opt_opt); check_hover_range( r#" //- minicore: option, try