@@ -12,8 +12,8 @@ use ide_db::{
1212use itertools:: Itertools ;
1313use stdx:: format_to;
1414use 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
1919use 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$}\n Propagated 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+
254349fn 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\n Type: {:>apad$}\n Coerced 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$}\n Coerced 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