2020-10-20 10:04:38 -05:00
use either ::Either ;
2021-08-02 13:42:25 -05:00
use hir ::{ known , Callable , HasVisibility , HirDisplay , Semantics , TypeInfo } ;
2022-03-06 12:01:30 -06:00
use ide_db ::{ base_db ::FileRange , famous_defs ::FamousDefs , RootDatabase } ;
2021-10-03 08:31:35 -05:00
use itertools ::Itertools ;
2020-08-12 11:26:51 -05:00
use stdx ::to_lower_snake_case ;
use syntax ::{
2022-03-18 12:11:16 -05:00
ast ::{ self , AstNode , HasArgList , HasGenericParams , HasName , UnaryOp } ,
2022-02-11 16:48:01 -06:00
match_ast , Direction , NodeOrToken , SmolStr , SyntaxKind , SyntaxNode , TextRange , T ,
2019-07-19 16:20:09 -05:00
} ;
2019-12-21 11:45:46 -06:00
2020-07-16 14:51:44 -05:00
use crate ::FileId ;
2020-07-16 11:07:53 -05:00
2020-03-10 13:21:56 -05:00
#[ derive(Clone, Debug, PartialEq, Eq) ]
2020-03-31 09:02:55 -05:00
pub struct InlayHintsConfig {
2022-03-11 14:06:26 -06:00
pub render_colons : bool ,
2020-03-11 22:14:39 -05:00
pub type_hints : bool ,
pub parameter_hints : bool ,
2020-03-23 14:32:05 -05:00
pub chaining_hints : bool ,
2022-03-16 15:16:55 -05:00
pub closure_return_type_hints : bool ,
2022-03-18 12:55:03 -05:00
// FIXME: ternary option here, on off non-noisy
2022-03-18 12:11:16 -05:00
pub lifetime_elision_hints : bool ,
2021-11-13 17:12:29 -06:00
pub hide_named_constructor_hints : bool ,
2020-03-10 13:21:56 -05:00
pub max_length : Option < usize > ,
}
#[ derive(Clone, Debug, PartialEq, Eq) ]
2019-07-19 16:20:09 -05:00
pub enum InlayKind {
2019-08-04 16:28:36 -05:00
TypeHint ,
2020-01-14 11:02:01 -06:00
ParameterHint ,
2022-03-16 15:16:55 -05:00
ClosureReturnTypeHint ,
2020-03-23 14:32:05 -05:00
ChainingHint ,
2022-03-18 12:11:16 -05:00
GenericParamListHint ,
LifetimeHint ,
2019-07-19 16:20:09 -05:00
}
#[ derive(Debug) ]
pub struct InlayHint {
pub range : TextRange ,
2019-07-22 13:52:47 -05:00
pub kind : InlayKind ,
pub label : SmolStr ,
2019-07-19 16:20:09 -05:00
}
2020-05-31 04:29:19 -05:00
// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
// Editors usually render this using read-only virtual text snippets interspersed with code.
//
2022-03-18 12:11:16 -05:00
// rust-analyzer by default shows hints for
2020-05-31 04:29:19 -05:00
//
// * types of local variables
// * names of function arguments
// * types of chained expressions
//
2022-03-18 12:11:16 -05:00
// Optionally, one can enable additional hints for
//
// * return types of closure expressions with blocks
//
2020-05-31 04:29:19 -05:00
// **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
//
// |===
// | Editor | Action Name
//
// | VS Code | **Rust Analyzer: Toggle inlay hints*
// |===
2021-03-30 18:08:10 -05:00
//
// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
2019-11-18 11:02:28 -06:00
pub ( crate ) fn inlay_hints (
db : & RootDatabase ,
file_id : FileId ,
2022-02-11 16:48:01 -06:00
range_limit : Option < FileRange > ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2019-11-18 11:02:28 -06:00
) -> Vec < InlayHint > {
2020-08-12 09:32:36 -05:00
let _p = profile ::span ( " inlay_hints " ) ;
2020-02-18 11:35:10 -06:00
let sema = Semantics ::new ( db ) ;
let file = sema . parse ( file_id ) ;
2021-09-14 13:30:28 -05:00
let file = file . syntax ( ) ;
2020-02-29 16:24:50 -06:00
2022-03-16 15:16:55 -05:00
let mut acc = Vec ::new ( ) ;
2022-02-11 16:48:01 -06:00
2022-03-16 15:16:55 -05:00
let hints = | node | hints ( & mut acc , & sema , config , node ) ;
2022-03-16 07:41:35 -05:00
match range_limit {
Some ( FileRange { range , .. } ) = > match file . covering_element ( range ) {
2022-03-16 15:16:55 -05:00
NodeOrToken ::Token ( _ ) = > return acc ,
2022-03-16 07:41:35 -05:00
NodeOrToken ::Node ( n ) = > n
. descendants ( )
. filter ( | descendant | range . contains_range ( descendant . text_range ( ) ) )
2022-03-16 15:16:55 -05:00
. for_each ( hints ) ,
2022-03-16 07:41:35 -05:00
} ,
2022-03-16 15:16:55 -05:00
None = > file . descendants ( ) . for_each ( hints ) ,
2022-03-16 07:41:35 -05:00
} ;
2022-02-11 16:48:01 -06:00
2022-03-16 15:16:55 -05:00
acc
2022-02-11 16:48:01 -06:00
}
2022-03-16 15:16:55 -05:00
fn hints (
2022-02-11 16:48:01 -06:00
hints : & mut Vec < InlayHint > ,
sema : & Semantics < RootDatabase > ,
config : & InlayHintsConfig ,
node : SyntaxNode ,
) {
2022-03-16 15:16:55 -05:00
let krate = sema . scope ( & node ) . module ( ) . map ( | it | it . krate ( ) ) ;
let famous_defs = FamousDefs ( sema , krate ) ;
2022-02-11 16:48:01 -06:00
if let Some ( expr ) = ast ::Expr ::cast ( node . clone ( ) ) {
2022-03-16 15:16:55 -05:00
chaining_hints ( hints , sema , & famous_defs , config , & expr ) ;
2022-02-11 16:48:01 -06:00
match expr {
ast ::Expr ::CallExpr ( it ) = > {
2022-03-16 15:16:55 -05:00
param_name_hints ( hints , sema , config , ast ::Expr ::from ( it ) ) ;
2022-02-11 16:48:01 -06:00
}
ast ::Expr ::MethodCallExpr ( it ) = > {
2022-03-16 15:16:55 -05:00
param_name_hints ( hints , sema , config , ast ::Expr ::from ( it ) ) ;
}
ast ::Expr ::ClosureExpr ( it ) = > {
closure_ret_hints ( hints , sema , & famous_defs , config , it ) ;
2022-02-11 16:48:01 -06:00
}
_ = > ( ) ,
}
2022-03-18 12:11:16 -05:00
} else if let Some ( it ) = ast ::IdentPat ::cast ( node . clone ( ) ) {
2022-03-16 15:16:55 -05:00
bind_pat_hints ( hints , sema , config , & it ) ;
2022-03-18 12:11:16 -05:00
} else if let Some ( it ) = ast ::Fn ::cast ( node ) {
lifetime_hints ( hints , config , it ) ;
2020-01-16 07:31:34 -06:00
}
2019-07-19 16:20:09 -05:00
}
2022-03-18 12:11:16 -05:00
fn lifetime_hints (
acc : & mut Vec < InlayHint > ,
config : & InlayHintsConfig ,
func : ast ::Fn ,
) -> Option < ( ) > {
if ! config . lifetime_elision_hints {
return None ;
}
let param_list = func . param_list ( ) ? ;
let generic_param_list = func . generic_param_list ( ) ;
let ret_type = func . ret_type ( ) ;
2022-03-18 12:57:11 -05:00
let self_param = param_list . self_param ( ) . filter ( | it | it . amp_token ( ) . is_some ( ) ) ;
2022-03-18 12:11:16 -05:00
2022-03-18 12:55:03 -05:00
// FIXME: don't use already used lifetimenames
2022-03-18 12:11:16 -05:00
let mut allocated_lifetimes = vec! [ ] ;
let mut gen_name = {
2022-03-18 12:55:03 -05:00
let mut gen = ( 'a' .. ) . map ( | it | SmolStr ::from_iter ( [ '\'' , it ] ) ) ;
move | | gen . next ( ) . unwrap_or_else ( SmolStr ::default )
2022-03-18 12:11:16 -05:00
} ;
let potential_lt_refs : Vec < _ > = param_list
. params ( )
. filter_map ( | it | {
let ty = it . ty ( ) ? ;
// FIXME: look into the nested types here and check path types
match ty {
2022-03-18 12:55:03 -05:00
ast ::Type ::RefType ( r ) = > Some ( (
it . pat ( ) . and_then ( | it | match it {
ast ::Pat ::IdentPat ( p ) = > p . name ( ) ,
_ = > None ,
} ) ,
r ,
) ) ,
2022-03-18 12:11:16 -05:00
_ = > None ,
}
} )
. collect ( ) ;
enum LifetimeKind {
Elided ,
Named ( SmolStr ) ,
Static ,
}
let fetch_lt_text = | lt : Option < ast ::Lifetime > | match lt {
Some ( lt ) = > match lt . text ( ) . as_str ( ) {
" '_ " = > LifetimeKind ::Elided ,
" 'static " = > LifetimeKind ::Static ,
name = > LifetimeKind ::Named ( name . into ( ) ) ,
} ,
None = > LifetimeKind ::Elided ,
} ;
let is_elided = | lt : Option < ast ::Lifetime > | match lt {
Some ( lt ) = > matches! ( lt . text ( ) . as_str ( ) , " '_ " ) ,
None = > true ,
} ;
// allocate names
if let Some ( self_param ) = & self_param {
if is_elided ( self_param . lifetime ( ) ) {
2022-03-18 12:55:03 -05:00
allocated_lifetimes . push ( SmolStr ::new_inline ( " 'self " ) ) ;
2022-03-18 12:11:16 -05:00
}
}
2022-03-18 12:55:03 -05:00
potential_lt_refs . iter ( ) . for_each ( | ( name , it ) | {
2022-03-18 12:11:16 -05:00
// FIXME: look into the nested types here and check path types
if is_elided ( it . lifetime ( ) ) {
2022-03-18 12:55:03 -05:00
allocated_lifetimes . push (
name . as_ref ( )
. map_or_else ( | | gen_name ( ) , | it | SmolStr ::from_iter ( [ " ' " , it . text ( ) . as_str ( ) ] ) ) ,
) ;
2022-03-18 12:11:16 -05:00
}
} ) ;
// fetch output lifetime if elision rule applies
let output = if let Some ( self_param ) = & self_param {
match fetch_lt_text ( self_param . lifetime ( ) ) {
LifetimeKind ::Elided = > allocated_lifetimes . get ( 0 ) . cloned ( ) ,
LifetimeKind ::Named ( name ) = > Some ( name ) ,
LifetimeKind ::Static = > None ,
}
} else {
match potential_lt_refs . as_slice ( ) {
2022-03-18 12:55:03 -05:00
[ ( _ , r ) ] = > match fetch_lt_text ( r . lifetime ( ) ) {
2022-03-18 12:11:16 -05:00
LifetimeKind ::Elided = > allocated_lifetimes . get ( 0 ) . cloned ( ) ,
LifetimeKind ::Named ( name ) = > Some ( name ) ,
LifetimeKind ::Static = > None ,
} ,
[ .. ] = > None ,
}
} ;
// apply hints
// apply output if required
match ( & output , ret_type ) {
( Some ( output_lt ) , Some ( r ) ) = > {
if let Some ( ast ::Type ::RefType ( t ) ) = r . ty ( ) {
if t . lifetime ( ) . is_none ( ) {
let amp = t . amp_token ( ) ? ;
acc . push ( InlayHint {
range : amp . text_range ( ) ,
kind : InlayKind ::LifetimeHint ,
label : output_lt . clone ( ) ,
} ) ;
}
}
}
_ = > ( ) ,
}
let mut idx = if let Some ( self_param ) = & self_param {
if is_elided ( self_param . lifetime ( ) ) {
if let Some ( amp ) = self_param . amp_token ( ) {
let lt = allocated_lifetimes [ 0 ] . clone ( ) ;
acc . push ( InlayHint {
range : amp . text_range ( ) ,
kind : InlayKind ::LifetimeHint ,
label : lt ,
} ) ;
}
1
} else {
0
}
} else {
0
} ;
2022-03-18 12:55:03 -05:00
for ( _ , p ) in potential_lt_refs . iter ( ) {
2022-03-18 12:11:16 -05:00
if is_elided ( p . lifetime ( ) ) {
let t = p . amp_token ( ) ? ;
let lt = allocated_lifetimes [ idx ] . clone ( ) ;
acc . push ( InlayHint { range : t . text_range ( ) , kind : InlayKind ::LifetimeHint , label : lt } ) ;
idx + = 1 ;
}
}
// generate generic param list things
match ( generic_param_list , allocated_lifetimes . as_slice ( ) ) {
( _ , [ ] ) = > ( ) ,
( Some ( gpl ) , allocated_lifetimes ) = > {
let angle_tok = gpl . l_angle_token ( ) ? ;
let is_empty = gpl . generic_params ( ) . next ( ) . is_none ( ) ;
acc . push ( InlayHint {
range : angle_tok . text_range ( ) ,
kind : InlayKind ::GenericParamListHint ,
label : format ! (
" {}{} " ,
allocated_lifetimes . iter ( ) . format ( " , " ) ,
if is_empty { " " } else { " , " }
)
. into ( ) ,
} ) ;
}
( None , allocated_lifetimes ) = > acc . push ( InlayHint {
range : func . name ( ) ? . syntax ( ) . text_range ( ) ,
kind : InlayKind ::GenericParamListHint ,
label : format ! ( " <{}> " , allocated_lifetimes . iter ( ) . format ( " , " ) , ) . into ( ) ,
} ) ,
}
Some ( ( ) )
}
2022-03-16 15:16:55 -05:00
fn closure_ret_hints (
2020-03-24 13:33:00 -05:00
acc : & mut Vec < InlayHint > ,
sema : & Semantics < RootDatabase > ,
2022-03-16 15:16:55 -05:00
famous_defs : & FamousDefs ,
config : & InlayHintsConfig ,
closure : ast ::ClosureExpr ,
) -> Option < ( ) > {
if ! config . closure_return_type_hints {
return None ;
}
let closure = sema . descend_node_into_attributes ( closure . clone ( ) ) . pop ( ) ? ;
let param_list = match closure . body ( ) {
Some ( ast ::Expr ::BlockExpr ( _ ) ) = > closure . param_list ( ) ? ,
_ = > return None ,
} ;
let ty = sema . type_of_expr ( & ast ::Expr ::ClosureExpr ( closure ) ) ? . adjusted ( ) ;
let callable = ty . as_callable ( sema . db ) ? ;
let ty = callable . return_type ( ) ;
if ty . is_unit ( ) {
return None ;
}
acc . push ( InlayHint {
range : param_list . syntax ( ) . text_range ( ) ,
kind : InlayKind ::ClosureReturnTypeHint ,
label : hint_iterator ( sema , & famous_defs , config , & ty )
. unwrap_or_else ( | | ty . display_truncated ( sema . db , config . max_length ) . to_string ( ) . into ( ) ) ,
} ) ;
Some ( ( ) )
}
fn chaining_hints (
acc : & mut Vec < InlayHint > ,
sema : & Semantics < RootDatabase > ,
famous_defs : & FamousDefs ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2021-09-13 18:59:45 -05:00
expr : & ast ::Expr ,
2020-03-24 13:33:00 -05:00
) -> Option < ( ) > {
2020-03-31 09:02:55 -05:00
if ! config . chaining_hints {
2020-03-24 13:33:00 -05:00
return None ;
}
2020-07-30 09:21:30 -05:00
if matches! ( expr , ast ::Expr ::RecordExpr ( _ ) ) {
2020-03-24 13:33:00 -05:00
return None ;
}
2021-09-18 06:19:29 -05:00
let descended = sema . descend_node_into_attributes ( expr . clone ( ) ) . pop ( ) ;
let desc_expr = descended . as_ref ( ) . unwrap_or ( expr ) ;
2020-10-20 10:38:21 -05:00
2020-03-24 14:31:02 -05:00
let mut tokens = expr
. syntax ( )
2020-03-24 13:33:00 -05:00
. siblings_with_tokens ( Direction ::Next )
. filter_map ( NodeOrToken ::into_token )
. filter ( | t | match t . kind ( ) {
SyntaxKind ::WHITESPACE if ! t . text ( ) . contains ( '\n' ) = > false ,
SyntaxKind ::COMMENT = > false ,
_ = > true ,
} ) ;
// Chaining can be defined as an expression whose next sibling tokens are newline and dot
// Ignoring extra whitespace and comments
let next = tokens . next ( ) ? . kind ( ) ;
2021-02-16 17:09:31 -06:00
if next = = SyntaxKind ::WHITESPACE {
let mut next_next = tokens . next ( ) ? . kind ( ) ;
while next_next = = SyntaxKind ::WHITESPACE {
next_next = tokens . next ( ) ? . kind ( ) ;
2020-03-31 13:25:03 -05:00
}
2021-02-16 17:09:31 -06:00
if next_next = = T! [ . ] {
2021-09-18 06:19:29 -05:00
let ty = sema . type_of_expr ( desc_expr ) ? . original ;
2021-02-16 17:09:31 -06:00
if ty . is_unknown ( ) {
return None ;
}
if matches! ( expr , ast ::Expr ::PathExpr ( _ ) ) {
if let Some ( hir ::Adt ::Struct ( st ) ) = ty . as_adt ( ) {
if st . fields ( sema . db ) . is_empty ( ) {
return None ;
}
2020-03-31 13:25:03 -05:00
}
}
2021-02-16 17:09:31 -06:00
acc . push ( InlayHint {
2021-09-18 06:19:29 -05:00
range : expr . syntax ( ) . text_range ( ) ,
2021-02-16 17:09:31 -06:00
kind : InlayKind ::ChainingHint ,
label : hint_iterator ( sema , & famous_defs , config , & ty ) . unwrap_or_else ( | | {
ty . display_truncated ( sema . db , config . max_length ) . to_string ( ) . into ( )
} ) ,
} ) ;
2020-03-31 13:25:03 -05:00
}
2020-03-24 13:33:00 -05:00
}
Some ( ( ) )
}
2022-03-16 15:16:55 -05:00
fn param_name_hints (
2020-01-16 07:31:34 -06:00
acc : & mut Vec < InlayHint > ,
2020-02-18 11:35:10 -06:00
sema : & Semantics < RootDatabase > ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2020-02-29 16:24:50 -06:00
expr : ast ::Expr ,
2020-01-16 07:31:34 -06:00
) -> Option < ( ) > {
2020-03-31 09:02:55 -05:00
if ! config . parameter_hints {
2020-03-10 13:21:56 -05:00
return None ;
2020-03-10 02:55:46 -05:00
}
2021-06-03 09:22:24 -05:00
let ( callable , arg_list ) = get_callable ( sema , & expr ) ? ;
2020-07-16 14:51:44 -05:00
let hints = callable
. params ( sema . db )
. into_iter ( )
2021-06-03 09:22:24 -05:00
. zip ( arg_list . args ( ) )
2020-08-12 16:43:01 -05:00
. filter_map ( | ( ( param , _ty ) , arg ) | {
2021-09-18 06:19:29 -05:00
// Only annotate hints for expressions that exist in the original file
let range = sema . original_range_opt ( arg . syntax ( ) ) ? ;
2020-08-12 16:43:01 -05:00
let param_name = match param ? {
2020-10-29 07:02:34 -05:00
Either ::Left ( _ ) = > " self " . to_string ( ) ,
2020-08-12 16:43:01 -05:00
Either ::Right ( pat ) = > match pat {
2020-07-31 13:09:09 -05:00
ast ::Pat ::IdentPat ( it ) = > it . name ( ) ? . to_string ( ) ,
2020-08-12 16:43:01 -05:00
_ = > return None ,
} ,
} ;
2021-09-18 06:19:29 -05:00
Some ( ( param_name , arg , range ) )
2020-07-16 14:51:44 -05:00
} )
2021-09-18 06:19:29 -05:00
. filter ( | ( param_name , arg , _ ) | {
! should_hide_param_name_hint ( sema , & callable , param_name , arg )
} )
. map ( | ( param_name , _ , FileRange { range , .. } ) | InlayHint {
range ,
2020-02-29 16:24:50 -06:00
kind : InlayKind ::ParameterHint ,
2020-07-19 13:26:24 -05:00
label : param_name . into ( ) ,
2020-02-29 16:24:50 -06:00
} ) ;
acc . extend ( hints ) ;
Some ( ( ) )
}
2022-03-16 15:16:55 -05:00
fn bind_pat_hints (
2020-02-29 16:24:50 -06:00
acc : & mut Vec < InlayHint > ,
sema : & Semantics < RootDatabase > ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2021-09-18 06:19:29 -05:00
pat : & ast ::IdentPat ,
2020-02-29 16:24:50 -06:00
) -> Option < ( ) > {
2020-03-31 09:02:55 -05:00
if ! config . type_hints {
2020-03-10 13:21:56 -05:00
return None ;
2020-03-10 02:55:46 -05:00
}
2021-09-18 06:19:29 -05:00
let descended = sema . descend_node_into_attributes ( pat . clone ( ) ) . pop ( ) ;
let desc_pat = descended . as_ref ( ) . unwrap_or ( pat ) ;
let ty = sema . type_of_pat ( & desc_pat . clone ( ) . into ( ) ) ? . original ;
2020-02-29 16:24:50 -06:00
2021-10-16 06:32:55 -05:00
if should_not_display_type_hint ( sema , pat , & ty ) {
2020-02-29 16:24:50 -06:00
return None ;
}
2021-07-31 08:24:21 -05:00
2021-10-03 07:45:21 -05:00
let krate = sema . scope ( desc_pat . syntax ( ) ) . module ( ) . map ( | it | it . krate ( ) ) ;
let famous_defs = FamousDefs ( sema , krate ) ;
let label = hint_iterator ( sema , & famous_defs , config , & ty ) ;
let label = match label {
Some ( label ) = > label ,
None = > {
2021-10-03 08:31:35 -05:00
let ty_name = ty . display_truncated ( sema . db , config . max_length ) . to_string ( ) ;
2021-11-13 17:12:29 -06:00
if config . hide_named_constructor_hints
& & is_named_constructor ( sema , pat , & ty_name ) . is_some ( )
{
2021-10-03 07:45:21 -05:00
return None ;
}
2021-10-03 08:31:35 -05:00
ty_name . into ( )
2021-10-03 07:45:21 -05:00
}
} ;
2020-10-07 04:30:42 -05:00
acc . push ( InlayHint {
2021-07-31 08:24:21 -05:00
range : match pat . name ( ) {
2021-09-18 06:19:29 -05:00
Some ( name ) = > name . syntax ( ) . text_range ( ) ,
None = > pat . syntax ( ) . text_range ( ) ,
2021-07-31 08:24:21 -05:00
} ,
2020-10-07 04:30:42 -05:00
kind : InlayKind ::TypeHint ,
2021-10-03 07:45:21 -05:00
label ,
2020-10-07 04:30:42 -05:00
} ) ;
2020-10-06 12:07:34 -05:00
2020-01-16 07:31:34 -06:00
Some ( ( ) )
2019-07-21 15:28:05 -05:00
}
2020-01-16 07:31:34 -06:00
2021-10-03 08:31:35 -05:00
fn is_named_constructor (
sema : & Semantics < RootDatabase > ,
pat : & ast ::IdentPat ,
ty_name : & str ,
) -> Option < ( ) > {
let let_node = pat . syntax ( ) . parent ( ) ? ;
2021-10-03 07:45:21 -05:00
let expr = match_ast! {
2021-10-03 08:31:35 -05:00
match let_node {
2021-10-03 07:45:21 -05:00
ast ::LetStmt ( it ) = > it . initializer ( ) ,
2022-01-23 16:37:59 -06:00
ast ::LetExpr ( it ) = > it . expr ( ) ,
2021-10-03 07:45:21 -05:00
_ = > None ,
}
2021-10-03 08:31:35 -05:00
} ? ;
let expr = sema . descend_node_into_attributes ( expr . clone ( ) ) . pop ( ) . unwrap_or ( expr ) ;
// unwrap postfix expressions
let expr = match expr {
ast ::Expr ::TryExpr ( it ) = > it . expr ( ) ,
ast ::Expr ::AwaitExpr ( it ) = > it . expr ( ) ,
expr = > Some ( expr ) ,
} ? ;
let expr = match expr {
ast ::Expr ::CallExpr ( call ) = > match call . expr ( ) ? {
2021-12-21 10:26:37 -06:00
ast ::Expr ::PathExpr ( path ) = > path ,
2021-10-03 08:31:35 -05:00
_ = > return None ,
} ,
2021-12-21 10:26:37 -06:00
ast ::Expr ::PathExpr ( path ) = > path ,
2021-10-03 08:31:35 -05:00
_ = > return None ,
2021-10-03 07:45:21 -05:00
} ;
2021-10-03 08:31:35 -05:00
let path = expr . path ( ) ? ;
2021-10-03 07:45:21 -05:00
2021-12-22 09:29:26 -06:00
let callable = sema . type_of_expr ( & ast ::Expr ::PathExpr ( expr ) ) ? . original . as_callable ( sema . db ) ;
let callable_kind = callable . map ( | it | it . kind ( ) ) ;
let qual_seg = match callable_kind {
Some ( hir ::CallableKind ::Function ( _ ) | hir ::CallableKind ::TupleEnumVariant ( _ ) ) = > {
path . qualifier ( ) ? . segment ( )
}
_ = > path . segment ( ) ,
2021-12-21 10:26:37 -06:00
} ? ;
2021-12-22 09:29:26 -06:00
2021-10-03 08:31:35 -05:00
let ctor_name = match qual_seg . kind ( ) ? {
ast ::PathSegmentKind ::Name ( name_ref ) = > {
match qual_seg . generic_arg_list ( ) . map ( | it | it . generic_args ( ) ) {
Some ( generics ) = > format! ( " {} < {} > " , name_ref , generics . format ( " , " ) ) ,
None = > name_ref . to_string ( ) ,
}
}
ast ::PathSegmentKind ::Type { type_ref : Some ( ty ) , trait_ref : None } = > ty . to_string ( ) ,
_ = > return None ,
} ;
2021-10-16 06:32:55 -05:00
( ctor_name = = ty_name ) . then ( | | ( ) )
2021-10-03 07:45:21 -05:00
}
2020-10-06 12:07:34 -05:00
/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
fn hint_iterator (
2020-10-06 14:05:57 -05:00
sema : & Semantics < RootDatabase > ,
2020-10-20 10:38:21 -05:00
famous_defs : & FamousDefs ,
2020-10-06 12:07:34 -05:00
config : & InlayHintsConfig ,
2020-10-07 06:14:12 -05:00
ty : & hir ::Type ,
2020-10-07 04:30:42 -05:00
) -> Option < SmolStr > {
2020-10-06 14:05:57 -05:00
let db = sema . db ;
2021-05-05 15:55:12 -05:00
let strukt = ty . strip_references ( ) . as_adt ( ) ? ;
2021-05-23 18:42:06 -05:00
let krate = strukt . module ( db ) . krate ( ) ;
2020-10-20 10:38:21 -05:00
if krate ! = famous_defs . core ( ) ? {
2020-10-06 12:07:34 -05:00
return None ;
}
2020-10-20 10:38:21 -05:00
let iter_trait = famous_defs . core_iter_Iterator ( ) ? ;
let iter_mod = famous_defs . core_iter ( ) ? ;
2021-05-23 11:50:23 -05:00
// Assert that this struct comes from `core::iter`.
2021-07-20 09:19:02 -05:00
if ! ( strukt . visibility ( db ) = = hir ::Visibility ::Public
& & strukt . module ( db ) . path_to_root ( db ) . contains ( & iter_mod ) )
{
return None ;
}
2021-05-23 11:50:23 -05:00
2020-10-06 12:07:34 -05:00
if ty . impls_trait ( db , iter_trait , & [ ] ) {
let assoc_type_item = iter_trait . items ( db ) . into_iter ( ) . find_map ( | item | match item {
2020-10-07 06:14:12 -05:00
hir ::AssocItem ::TypeAlias ( alias ) if alias . name ( db ) = = known ::Item = > Some ( alias ) ,
2020-10-06 12:07:34 -05:00
_ = > None ,
} ) ? ;
2021-04-03 14:50:52 -05:00
if let Some ( ty ) = ty . normalize_trait_assoc_type ( db , & [ ] , assoc_type_item ) {
2020-10-06 14:05:57 -05:00
const LABEL_START : & str = " impl Iterator<Item = " ;
const LABEL_END : & str = " > " ;
2020-10-20 10:38:21 -05:00
let ty_display = hint_iterator ( sema , famous_defs , config , & ty )
2020-10-10 13:37:20 -05:00
. map ( | assoc_type_impl | assoc_type_impl . to_string ( ) )
. unwrap_or_else ( | | {
ty . display_truncated (
db ,
config
. max_length
. map ( | len | len . saturating_sub ( LABEL_START . len ( ) + LABEL_END . len ( ) ) ) ,
)
. to_string ( )
} ) ;
2020-10-07 04:30:42 -05:00
return Some ( format! ( " {} {} {} " , LABEL_START , ty_display , LABEL_END ) . into ( ) ) ;
2020-10-06 12:07:34 -05:00
}
}
None
}
2020-10-07 06:14:12 -05:00
fn pat_is_enum_variant ( db : & RootDatabase , bind_pat : & ast ::IdentPat , pat_ty : & hir ::Type ) -> bool {
if let Some ( hir ::Adt ::Enum ( enum_data ) ) = pat_ty . as_adt ( ) {
2020-05-03 07:07:57 -05:00
let pat_text = bind_pat . to_string ( ) ;
2020-02-24 01:29:34 -06:00
enum_data
. variants ( db )
. into_iter ( )
2021-11-04 12:12:05 -05:00
. map ( | variant | variant . name ( db ) . to_smol_str ( ) )
2020-02-24 01:29:34 -06:00
. any ( | enum_name | enum_name = = pat_text )
} else {
false
}
}
2020-07-31 13:09:09 -05:00
fn should_not_display_type_hint (
2020-09-13 12:24:04 -05:00
sema : & Semantics < RootDatabase > ,
2020-07-31 13:09:09 -05:00
bind_pat : & ast ::IdentPat ,
2020-10-07 06:14:12 -05:00
pat_ty : & hir ::Type ,
2020-07-31 13:09:09 -05:00
) -> bool {
2020-09-13 12:24:04 -05:00
let db = sema . db ;
2020-02-24 01:29:34 -06:00
if pat_ty . is_unknown ( ) {
return true ;
}
2020-10-07 06:14:12 -05:00
if let Some ( hir ::Adt ::Struct ( s ) ) = pat_ty . as_adt ( ) {
2021-11-04 12:12:05 -05:00
if s . fields ( db ) . is_empty ( ) & & s . name ( db ) . to_smol_str ( ) = = bind_pat . to_string ( ) {
2020-03-08 16:21:08 -05:00
return true ;
}
}
2020-02-22 14:07:09 -06:00
for node in bind_pat . syntax ( ) . ancestors ( ) {
match_ast! {
match node {
2021-06-03 09:22:24 -05:00
ast ::LetStmt ( it ) = > return it . ty ( ) . is_some ( ) ,
ast ::Param ( it ) = > return it . ty ( ) . is_some ( ) ,
2022-01-23 16:37:59 -06:00
ast ::MatchArm ( _ ) = > return pat_is_enum_variant ( db , bind_pat , pat_ty ) ,
ast ::LetExpr ( _ ) = > return pat_is_enum_variant ( db , bind_pat , pat_ty ) ,
ast ::IfExpr ( _ ) = > return false ,
ast ::WhileExpr ( _ ) = > return false ,
2020-09-13 12:24:04 -05:00
ast ::ForExpr ( it ) = > {
// We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
// Type of expr should be iterable.
2020-10-03 05:44:43 -05:00
return it . in_token ( ) . is_none ( ) | |
it . iterable ( )
2021-06-04 06:47:39 -05:00
. and_then ( | iterable_expr | sema . type_of_expr ( & iterable_expr ) )
2021-08-03 10:28:51 -05:00
. map ( TypeInfo ::original )
2021-06-04 06:47:39 -05:00
. map_or ( true , | iterable_ty | iterable_ty . is_unknown ( ) | | iterable_ty . is_unit ( ) )
2020-09-13 12:24:04 -05:00
} ,
2020-02-22 14:07:09 -06:00
_ = > ( ) ,
}
}
}
false
}
2021-06-03 09:22:24 -05:00
fn should_hide_param_name_hint (
2020-05-03 07:50:35 -05:00
sema : & Semantics < RootDatabase > ,
2020-10-07 06:14:12 -05:00
callable : & hir ::Callable ,
2020-02-23 03:49:53 -06:00
param_name : & str ,
2020-02-24 01:29:34 -06:00
argument : & ast ::Expr ,
2020-02-23 03:49:53 -06:00
) -> bool {
2021-06-04 07:09:20 -05:00
// These are to be tested in the `parameter_hint_heuristics` test
2021-06-03 09:22:24 -05:00
// hide when:
// - the parameter name is a suffix of the function's name
// - the argument is an enum whose name is equal to the parameter
2021-06-04 06:47:39 -05:00
// - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
// of argument with _ splitting it off
2021-06-03 09:22:24 -05:00
// - param starts with `ra_fixture`
2021-08-22 10:48:15 -05:00
// - param is a well known name in a unary function
2021-06-03 09:22:24 -05:00
2020-05-03 07:50:35 -05:00
let param_name = param_name . trim_start_matches ( '_' ) ;
2021-06-03 09:22:24 -05:00
if param_name . is_empty ( ) {
return true ;
}
2021-11-27 12:12:47 -06:00
if matches! ( argument , ast ::Expr ::PrefixExpr ( prefix ) if prefix . op_kind ( ) = = Some ( UnaryOp ::Not ) ) {
return false ;
}
2020-07-16 14:51:44 -05:00
let fn_name = match callable . kind ( ) {
2021-11-04 12:12:05 -05:00
hir ::CallableKind ::Function ( it ) = > Some ( it . name ( sema . db ) . to_smol_str ( ) ) ,
2021-06-03 09:22:24 -05:00
_ = > None ,
2020-07-16 14:51:44 -05:00
} ;
2021-06-03 09:22:24 -05:00
let fn_name = fn_name . as_deref ( ) ;
is_param_name_suffix_of_fn_name ( param_name , callable , fn_name )
| | is_enum_name_similar_to_param_name ( sema , argument , param_name )
| | is_argument_similar_to_param_name ( argument , param_name )
2020-06-28 06:11:41 -05:00
| | param_name . starts_with ( " ra_fixture " )
2021-06-03 09:22:24 -05:00
| | ( callable . n_params ( ) = = 1 & & is_obvious_param ( param_name ) )
2020-04-09 09:35:07 -05:00
}
2021-06-03 09:22:24 -05:00
fn is_argument_similar_to_param_name ( argument : & ast ::Expr , param_name : & str ) -> bool {
2021-06-04 06:47:39 -05:00
// check whether param_name and argument are the same or
// whether param_name is a prefix/suffix of argument(split at `_`)
let argument = match get_string_representation ( argument ) {
Some ( argument ) = > argument ,
None = > return false ,
} ;
2021-11-19 05:46:03 -06:00
// std is honestly too panic happy...
let str_split_at = | str : & str , at | str . is_char_boundary ( at ) . then ( | | argument . split_at ( at ) ) ;
2021-06-04 06:47:39 -05:00
let param_name = param_name . trim_start_matches ( '_' ) ;
let argument = argument . trim_start_matches ( '_' ) ;
2021-11-19 05:46:03 -06:00
match str_split_at ( argument , param_name . len ( ) ) {
Some ( ( prefix , rest ) ) if prefix . eq_ignore_ascii_case ( param_name ) = > {
return rest . is_empty ( ) | | rest . starts_with ( '_' ) ;
}
_ = > ( ) ,
2021-06-04 06:47:39 -05:00
}
2021-11-19 05:46:03 -06:00
match argument . len ( ) . checked_sub ( param_name . len ( ) ) . and_then ( | at | str_split_at ( argument , at ) ) {
Some ( ( rest , suffix ) ) if param_name . eq_ignore_ascii_case ( suffix ) = > {
return rest . is_empty ( ) | | rest . ends_with ( '_' ) ;
}
_ = > ( ) ,
2020-05-03 07:50:35 -05:00
}
2021-11-19 05:46:03 -06:00
false
2020-05-03 07:50:35 -05:00
}
2021-08-22 10:48:15 -05:00
/// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
2021-06-03 09:22:24 -05:00
///
/// `fn strip_suffix(suffix)` will be hidden.
/// `fn stripsuffix(suffix)` will not be hidden.
fn is_param_name_suffix_of_fn_name (
2020-10-01 13:05:39 -05:00
param_name : & str ,
callable : & Callable ,
2021-06-03 09:22:24 -05:00
fn_name : Option < & str > ,
2020-10-01 13:05:39 -05:00
) -> bool {
match ( callable . n_params ( ) , fn_name ) {
( 1 , Some ( function ) ) = > {
function = = param_name
2021-11-19 05:46:03 -06:00
| | function
. len ( )
. checked_sub ( param_name . len ( ) )
. and_then ( | at | function . is_char_boundary ( at ) . then ( | | function . split_at ( at ) ) )
. map_or ( false , | ( prefix , suffix ) | {
suffix . eq_ignore_ascii_case ( param_name ) & & prefix . ends_with ( '_' )
} )
2020-10-01 13:05:39 -05:00
}
_ = > false ,
}
}
2020-05-03 07:50:35 -05:00
fn is_enum_name_similar_to_param_name (
sema : & Semantics < RootDatabase > ,
argument : & ast ::Expr ,
param_name : & str ,
) -> bool {
2021-08-03 10:28:51 -05:00
match sema . type_of_expr ( argument ) . and_then ( | t | t . original . as_adt ( ) ) {
2021-11-04 12:12:05 -05:00
Some ( hir ::Adt ::Enum ( e ) ) = > {
to_lower_snake_case ( & e . name ( sema . db ) . to_smol_str ( ) ) = = param_name
}
2020-05-03 07:50:35 -05:00
_ = > false ,
}
}
2020-05-03 07:07:57 -05:00
fn get_string_representation ( expr : & ast ::Expr ) -> Option < String > {
match expr {
ast ::Expr ::MethodCallExpr ( method_call_expr ) = > {
2020-10-22 12:42:39 -05:00
let name_ref = method_call_expr . name_ref ( ) ? ;
2021-03-26 12:30:59 -05:00
match name_ref . text ( ) . as_str ( ) {
2021-06-04 07:09:20 -05:00
" clone " | " as_ref " = > method_call_expr . receiver ( ) . map ( | rec | rec . to_string ( ) ) ,
2020-10-22 12:42:39 -05:00
name_ref = > Some ( name_ref . to_owned ( ) ) ,
}
2020-04-09 11:26:49 -05:00
}
2021-01-27 07:20:58 -06:00
ast ::Expr ::FieldExpr ( field_expr ) = > Some ( field_expr . name_ref ( ) ? . to_string ( ) ) ,
2021-06-03 09:22:24 -05:00
ast ::Expr ::PathExpr ( path_expr ) = > Some ( path_expr . path ( ) ? . segment ( ) ? . to_string ( ) ) ,
2021-01-27 07:20:58 -06:00
ast ::Expr ::PrefixExpr ( prefix_expr ) = > get_string_representation ( & prefix_expr . expr ( ) ? ) ,
2020-05-03 07:07:57 -05:00
ast ::Expr ::RefExpr ( ref_expr ) = > get_string_representation ( & ref_expr . expr ( ) ? ) ,
2021-01-27 07:20:58 -06:00
_ = > None ,
2020-04-09 11:26:49 -05:00
}
}
2020-04-09 09:35:07 -05:00
fn is_obvious_param ( param_name : & str ) -> bool {
2021-06-03 09:22:24 -05:00
// avoid displaying hints for common functions like map, filter, etc.
// or other obvious words used in std
2020-06-27 20:02:03 -05:00
let is_obvious_param_name =
matches! ( param_name , " predicate " | " value " | " pat " | " rhs " | " other " ) ;
2020-04-09 09:35:07 -05:00
param_name . len ( ) = = 1 | | is_obvious_param_name
2020-02-23 03:49:53 -06:00
}
2021-06-03 09:22:24 -05:00
fn get_callable (
sema : & Semantics < RootDatabase > ,
expr : & ast ::Expr ,
) -> Option < ( hir ::Callable , ast ::ArgList ) > {
2020-01-14 11:02:01 -06:00
match expr {
2021-06-03 09:22:24 -05:00
ast ::Expr ::CallExpr ( expr ) = > {
2021-09-18 06:19:29 -05:00
let descended = sema . descend_node_into_attributes ( expr . clone ( ) ) . pop ( ) ;
let expr = descended . as_ref ( ) . unwrap_or ( expr ) ;
2021-08-03 10:28:51 -05:00
sema . type_of_expr ( & expr . expr ( ) ? ) ? . original . as_callable ( sema . db ) . zip ( expr . arg_list ( ) )
2021-06-03 09:22:24 -05:00
}
ast ::Expr ::MethodCallExpr ( expr ) = > {
2021-09-18 06:19:29 -05:00
let descended = sema . descend_node_into_attributes ( expr . clone ( ) ) . pop ( ) ;
let expr = descended . as_ref ( ) . unwrap_or ( expr ) ;
2021-06-03 09:22:24 -05:00
sema . resolve_method_call_as_callable ( expr ) . zip ( expr . arg_list ( ) )
}
2020-01-14 11:02:01 -06:00
_ = > None ,
}
}
2019-07-21 15:28:05 -05:00
2019-07-19 16:20:09 -05:00
#[ cfg(test) ]
mod tests {
2020-08-21 06:19:31 -05:00
use expect_test ::{ expect , Expect } ;
2022-02-11 16:48:01 -06:00
use ide_db ::base_db ::FileRange ;
2022-03-18 12:11:16 -05:00
use itertools ::Itertools ;
2022-02-11 16:48:01 -06:00
use syntax ::{ TextRange , TextSize } ;
2020-06-30 11:04:25 -05:00
use test_utils ::extract_annotations ;
2019-07-19 16:20:09 -05:00
2020-10-02 10:34:31 -05:00
use crate ::{ fixture , inlay_hints ::InlayHintsConfig } ;
2020-06-30 11:04:25 -05:00
2022-03-16 15:16:55 -05:00
const DISABLED_CONFIG : InlayHintsConfig = InlayHintsConfig {
render_colons : false ,
type_hints : false ,
parameter_hints : false ,
chaining_hints : false ,
2022-03-18 12:11:16 -05:00
lifetime_elision_hints : false ,
2022-03-16 15:16:55 -05:00
hide_named_constructor_hints : false ,
closure_return_type_hints : false ,
max_length : None ,
} ;
2021-01-06 04:54:28 -06:00
const TEST_CONFIG : InlayHintsConfig = InlayHintsConfig {
type_hints : true ,
parameter_hints : true ,
chaining_hints : true ,
2022-03-16 15:16:55 -05:00
closure_return_type_hints : true ,
2022-03-18 12:11:16 -05:00
lifetime_elision_hints : true ,
2022-03-16 15:16:55 -05:00
.. DISABLED_CONFIG
2021-01-06 04:54:28 -06:00
} ;
2021-10-03 08:31:35 -05:00
#[ track_caller ]
2020-06-30 11:04:25 -05:00
fn check ( ra_fixture : & str ) {
2021-01-06 04:54:28 -06:00
check_with_config ( TEST_CONFIG , ra_fixture ) ;
2020-06-30 11:04:25 -05:00
}
2021-10-03 08:31:35 -05:00
#[ track_caller ]
2021-06-04 06:47:39 -05:00
fn check_params ( ra_fixture : & str ) {
check_with_config (
2022-03-16 15:16:55 -05:00
InlayHintsConfig { parameter_hints : true , .. DISABLED_CONFIG } ,
2021-06-04 06:47:39 -05:00
ra_fixture ,
) ;
}
2021-10-03 08:31:35 -05:00
#[ track_caller ]
2021-06-04 06:47:39 -05:00
fn check_types ( ra_fixture : & str ) {
2022-03-16 15:16:55 -05:00
check_with_config ( InlayHintsConfig { type_hints : true , .. DISABLED_CONFIG } , ra_fixture ) ;
2021-06-04 06:47:39 -05:00
}
2021-10-03 08:31:35 -05:00
#[ track_caller ]
2021-06-04 06:47:39 -05:00
fn check_chains ( ra_fixture : & str ) {
2022-03-16 15:16:55 -05:00
check_with_config ( InlayHintsConfig { chaining_hints : true , .. DISABLED_CONFIG } , ra_fixture ) ;
2021-06-04 06:47:39 -05:00
}
2021-10-03 08:31:35 -05:00
#[ track_caller ]
2020-07-09 09:12:53 -05:00
fn check_with_config ( config : InlayHintsConfig , ra_fixture : & str ) {
2021-10-16 06:32:55 -05:00
let ( analysis , file_id ) = fixture ::file ( ra_fixture ) ;
2022-03-18 12:11:16 -05:00
let mut expected = extract_annotations ( & * analysis . file_text ( file_id ) . unwrap ( ) ) ;
2022-02-11 16:48:01 -06:00
let inlay_hints = analysis . inlay_hints ( & config , file_id , None ) . unwrap ( ) ;
2022-03-18 12:11:16 -05:00
let actual = inlay_hints
. into_iter ( )
. map ( | it | ( it . range , it . label . to_string ( ) ) )
. sorted_by_key ( | ( range , _ ) | range . start ( ) )
. collect ::< Vec < _ > > ( ) ;
expected . sort_by_key ( | ( range , _ ) | range . start ( ) ) ;
2020-07-16 14:51:44 -05:00
assert_eq! ( expected , actual , " \n Expected: \n {:#?} \n \n Actual: \n {:#?} " , expected , actual ) ;
2020-06-30 11:04:25 -05:00
}
2019-12-07 12:14:01 -06:00
2021-10-03 08:31:35 -05:00
#[ track_caller ]
2020-07-09 09:12:53 -05:00
fn check_expect ( config : InlayHintsConfig , ra_fixture : & str , expect : Expect ) {
2021-10-16 06:32:55 -05:00
let ( analysis , file_id ) = fixture ::file ( ra_fixture ) ;
2022-02-11 16:48:01 -06:00
let inlay_hints = analysis . inlay_hints ( & config , file_id , None ) . unwrap ( ) ;
2020-06-30 14:55:21 -05:00
expect . assert_debug_eq ( & inlay_hints )
}
2020-03-10 02:55:46 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn hints_disabled ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
2022-03-16 15:16:55 -05:00
InlayHintsConfig { render_colons : true , .. DISABLED_CONFIG } ,
2020-03-10 02:55:46 -05:00
r #"
2020-06-30 11:04:25 -05:00
fn foo ( a : i32 , b : i32 ) -> i32 { a + b }
2021-06-04 06:47:39 -05:00
fn main ( ) {
let _x = foo ( 4 , 4 ) ;
} " #,
) ;
}
2021-06-04 07:09:20 -05:00
// Parameter hint tests
2021-06-04 06:47:39 -05:00
#[ test ]
fn param_hints_only ( ) {
check_params (
r #"
fn foo ( a : i32 , b : i32 ) -> i32 { a + b }
2020-06-30 11:04:25 -05:00
fn main ( ) {
let _x = foo (
4 ,
//^ a
4 ,
//^ b
) ;
} " #,
) ;
2020-03-10 02:55:46 -05:00
}
2020-10-01 13:05:39 -05:00
#[ test ]
fn param_name_similar_to_fn_name_still_hints ( ) {
2021-06-04 06:47:39 -05:00
check_params (
2020-10-01 13:05:39 -05:00
r #"
fn max ( x : i32 , y : i32 ) -> i32 { x + y }
fn main ( ) {
let _x = max (
4 ,
//^ x
4 ,
//^ y
) ;
} " #,
) ;
}
#[ test ]
fn param_name_similar_to_fn_name ( ) {
2021-06-04 06:47:39 -05:00
check_params (
2020-10-01 13:05:39 -05:00
r #"
fn param_with_underscore ( with_underscore : i32 ) -> i32 { with_underscore }
2021-06-04 07:09:20 -05:00
fn main ( ) {
let _x = param_with_underscore (
4 ,
) ;
} " #,
) ;
check_params (
r #"
fn param_with_underscore ( underscore : i32 ) -> i32 { underscore }
2020-10-01 13:05:39 -05:00
fn main ( ) {
let _x = param_with_underscore (
4 ,
) ;
} " #,
) ;
}
#[ test ]
fn param_name_same_as_fn_name ( ) {
2021-06-04 06:47:39 -05:00
check_params (
2020-10-01 13:05:39 -05:00
r #"
fn foo ( foo : i32 ) -> i32 { foo }
fn main ( ) {
let _x = foo (
4 ,
) ;
} " #,
) ;
}
#[ test ]
fn never_hide_param_when_multiple_params ( ) {
2021-06-04 06:47:39 -05:00
check_params (
2020-10-01 13:05:39 -05:00
r #"
2021-06-04 07:09:20 -05:00
fn foo ( foo : i32 , bar : i32 ) -> i32 { bar + baz }
2020-10-01 13:05:39 -05:00
fn main ( ) {
let _x = foo (
4 ,
2021-06-04 07:09:20 -05:00
//^ foo
2020-10-01 13:05:39 -05:00
8 ,
2021-06-04 07:09:20 -05:00
//^ bar
2020-10-01 13:05:39 -05:00
) ;
} " #,
) ;
}
2020-03-10 02:55:46 -05:00
#[ test ]
2021-06-04 07:09:20 -05:00
fn param_hints_look_through_as_ref_and_clone ( ) {
2021-06-04 06:47:39 -05:00
check_params (
2020-07-09 09:12:53 -05:00
r #"
2021-06-04 07:09:20 -05:00
fn foo ( bar : i32 , baz : f32 ) { }
2021-06-04 06:47:39 -05:00
2020-07-09 09:12:53 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let bar = 3 ;
let baz = & " baz " ;
let fez = 1.0 ;
2021-06-04 07:09:20 -05:00
foo ( bar . clone ( ) , bar . clone ( ) ) ;
//^^^^^^^^^^^ baz
foo ( bar . as_ref ( ) , bar . as_ref ( ) ) ;
//^^^^^^^^^^^^ baz
2021-06-04 06:47:39 -05:00
}
" #,
2020-03-10 02:55:46 -05:00
) ;
}
#[ test ]
2021-06-04 06:47:39 -05:00
fn self_param_hints ( ) {
check_params (
2020-07-09 09:12:53 -05:00
r #"
2021-06-04 06:47:39 -05:00
struct Foo ;
impl Foo {
fn foo ( self : Self ) { }
fn bar ( self : & Self ) { }
}
2020-07-09 09:12:53 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
Foo ::foo ( Foo ) ;
//^^^ self
Foo ::bar ( & Foo ) ;
//^^^^ self
}
" #,
)
2020-03-10 02:55:46 -05:00
}
2020-06-30 11:04:25 -05:00
2019-12-07 16:54:18 -06:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn param_name_hints_show_for_literals ( ) {
check_params (
r #" pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
2019-12-07 16:54:18 -06:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
test (
2021-06-04 07:09:20 -05:00
0xa_b ,
//^^^^^ a
0xa_b ,
//^^^^^ b
2021-06-04 06:47:39 -05:00
) ;
2019-12-07 16:54:18 -06:00
} " #,
2021-06-04 06:47:39 -05:00
)
2019-12-07 16:54:18 -06:00
}
2019-07-19 16:20:09 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn function_call_parameter_hint ( ) {
2021-06-04 07:09:20 -05:00
check_params (
2019-07-19 16:20:09 -05:00
r #"
2021-06-18 15:38:19 -05:00
//- minicore: option
2021-06-04 06:47:39 -05:00
struct FileId { }
struct SmolStr { }
2019-07-19 16:20:09 -05:00
2021-06-04 06:47:39 -05:00
struct TextRange { }
struct SyntaxKind { }
struct NavigationTarget { }
2019-07-19 16:20:09 -05:00
2021-06-04 06:47:39 -05:00
struct Test { }
2019-07-26 06:10:29 -05:00
2021-06-04 06:47:39 -05:00
impl Test {
fn method ( & self , mut param : i32 ) -> i32 { param * 2 }
2019-07-26 06:10:29 -05:00
2021-06-04 06:47:39 -05:00
fn from_syntax (
file_id : FileId ,
name : SmolStr ,
focus_range : Option < TextRange > ,
full_range : TextRange ,
kind : SyntaxKind ,
docs : Option < String > ,
) -> NavigationTarget {
NavigationTarget { }
2019-07-27 16:50:26 -05:00
}
2021-06-04 06:47:39 -05:00
}
2019-07-27 16:50:26 -05:00
2021-06-04 06:47:39 -05:00
fn test_func ( mut foo : i32 , bar : i32 , msg : & str , _ : i32 , last : i32 ) -> i32 {
foo + bar
}
2019-12-23 09:53:35 -06:00
2021-06-04 06:47:39 -05:00
fn main ( ) {
let not_literal = 1 ;
let _ : i32 = test_func ( 1 , 2 , " hello " , 3 , not_literal ) ;
//^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
let t : Test = Test { } ;
t . method ( 123 ) ;
//^^^ param
Test ::method ( & t , 3456 ) ;
//^^ self ^^^^ param
Test ::from_syntax (
FileId { } ,
//^^^^^^^^^ file_id
" impl " . into ( ) ,
//^^^^^^^^^^^^^ name
None ,
//^^^^ focus_range
TextRange { } ,
//^^^^^^^^^^^^ full_range
SyntaxKind { } ,
//^^^^^^^^^^^^^ kind
None ,
//^^^^ docs
) ;
2019-07-27 16:50:26 -05:00
} " #,
) ;
}
#[ test ]
2021-06-04 07:09:20 -05:00
fn parameter_hint_heuristics ( ) {
check_params (
2019-07-27 16:50:26 -05:00
r #"
2021-06-04 07:09:20 -05:00
fn check ( ra_fixture_thing : & str ) { }
2021-06-04 06:47:39 -05:00
fn map ( f : i32 ) { }
fn filter ( predicate : i32 ) { }
2019-07-27 16:50:26 -05:00
2021-06-04 07:09:20 -05:00
fn strip_suffix ( suffix : & str ) { }
fn stripsuffix ( suffix : & str ) { }
fn same ( same : u32 ) { }
fn same2 ( _same2 : u32 ) { }
2020-04-09 05:47:48 -05:00
2020-05-03 07:50:35 -05:00
fn enum_matches_param_name ( completion_kind : CompletionKind ) { }
2020-04-08 16:48:16 -05:00
2021-06-04 07:09:20 -05:00
fn foo ( param : u32 ) { }
fn bar ( param_eter : u32 ) { }
2020-04-09 05:47:48 -05:00
2020-05-03 07:50:35 -05:00
enum CompletionKind {
Keyword ,
}
2021-06-04 07:09:20 -05:00
fn non_ident_pat ( ( a , b ) : ( u32 , u32 ) ) { }
2020-02-23 03:49:53 -06:00
2021-06-04 07:09:20 -05:00
fn main ( ) {
2021-11-19 05:46:03 -06:00
const PARAM : u32 = 0 ;
foo ( PARAM ) ;
2021-11-27 12:12:47 -06:00
foo ( ! PARAM ) ;
// ^^^^^^ param
2021-06-04 07:09:20 -05:00
check ( " " ) ;
2021-01-09 02:33:28 -06:00
2021-06-04 07:09:20 -05:00
map ( 0 ) ;
filter ( 0 ) ;
2020-04-08 16:48:16 -05:00
2021-06-04 07:09:20 -05:00
strip_suffix ( " " ) ;
stripsuffix ( " " ) ;
//^^ suffix
same ( 0 ) ;
same2 ( 0 ) ;
2020-04-18 02:53:48 -05:00
2020-05-03 07:50:35 -05:00
enum_matches_param_name ( CompletionKind ::Keyword ) ;
2021-06-04 07:09:20 -05:00
let param = 0 ;
foo ( param ) ;
let param_end = 0 ;
foo ( param_end ) ;
let start_param = 0 ;
foo ( start_param ) ;
let param2 = 0 ;
foo ( param2 ) ;
//^^^^^^ param
let param_eter = 0 ;
bar ( param_eter ) ;
let param_eter_end = 0 ;
bar ( param_eter_end ) ;
let start_param_eter = 0 ;
bar ( start_param_eter ) ;
let param_eter2 = 0 ;
bar ( param_eter2 ) ;
//^^^^^^^^^^^ param_eter
non_ident_pat ( ( 0 , 0 ) ) ;
2020-02-23 03:49:53 -06:00
} " #,
) ;
}
2020-03-08 16:21:08 -05:00
2021-06-04 06:47:39 -05:00
// Type-Hint tests
2020-03-08 16:21:08 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn type_hints_only ( ) {
check_types (
2020-03-08 16:21:08 -05:00
r #"
2021-06-04 06:47:39 -05:00
fn foo ( a : i32 , b : i32 ) -> i32 { a + b }
2020-03-08 16:21:08 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let _x = foo ( 4 , 4 ) ;
//^^ i32
2020-03-08 16:21:08 -05:00
} " #,
) ;
}
2020-03-24 13:33:00 -05:00
2021-07-31 08:24:21 -05:00
#[ test ]
fn type_hints_bindings_after_at ( ) {
check_types (
r #"
//- minicore: option
fn main ( ) {
let ref foo @ bar @ ref mut baz = 0 ;
//^^^ &i32
//^^^ i32
//^^^ &mut i32
let [ x @ .. ] = [ 0 ] ;
//^ [i32; 1]
if let x @ Some ( _ ) = Some ( 0 ) { }
//^ Option<i32>
let foo @ ( bar , baz ) = ( 3 , 3 ) ;
//^^^ (i32, i32)
//^^^ i32
//^^^ i32
} " #,
) ;
}
2020-03-24 13:33:00 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn default_generic_types_should_not_be_displayed ( ) {
check (
2020-03-24 13:33:00 -05:00
r #"
2021-06-04 06:47:39 -05:00
struct Test < K , T = u8 > { k : K , t : T }
2020-06-30 14:55:21 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let zz = Test { t : 23 u8 , k : 33 } ;
//^^ Test<i32>
let zz_ref = & zz ;
//^^^^^^ &Test<i32>
let test = | | zz ;
//^^^^ || -> Test<i32>
} " #,
2020-06-30 14:55:21 -05:00
) ;
2020-03-24 13:33:00 -05:00
}
#[ test ]
2021-06-04 06:47:39 -05:00
fn shorten_iterators_in_associated_params ( ) {
check_types (
2020-03-24 13:33:00 -05:00
r #"
2021-06-17 03:28:44 -05:00
//- minicore: iterators
2021-06-04 06:47:39 -05:00
use core ::iter ;
pub struct SomeIter < T > { }
impl < T > SomeIter < T > {
pub fn new ( ) -> Self { SomeIter { } }
pub fn push ( & mut self , t : T ) { }
}
impl < T > Iterator for SomeIter < T > {
type Item = T ;
fn next ( & mut self ) -> Option < Self ::Item > {
None
}
}
2020-03-24 13:33:00 -05:00
2020-06-30 11:04:25 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let mut some_iter = SomeIter ::new ( ) ;
2021-07-31 08:24:21 -05:00
//^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
2021-06-04 06:47:39 -05:00
some_iter . push ( iter ::repeat ( 2 ) . take ( 2 ) ) ;
let iter_of_iters = some_iter . take ( 2 ) ;
//^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
}
" #,
2020-03-24 13:33:00 -05:00
) ;
}
#[ test ]
2021-06-04 06:47:39 -05:00
fn infer_call_method_return_associated_types_with_generic ( ) {
check_types (
2020-03-24 13:33:00 -05:00
r #"
2021-06-04 06:47:39 -05:00
pub trait Default {
fn default ( ) -> Self ;
}
pub trait Foo {
type Bar : Default ;
}
2020-03-31 13:25:03 -05:00
2021-06-04 06:47:39 -05:00
pub fn quux < T : Foo > ( ) -> T ::Bar {
let y = Default ::default ( ) ;
//^ <T as Foo>::Bar
y
}
" #,
) ;
}
#[ test ]
fn fn_hints ( ) {
check_types (
r #"
2021-06-17 03:18:37 -05:00
//- minicore: fn, sized
2021-06-04 06:47:39 -05:00
fn foo ( ) -> impl Fn ( ) { loop { } }
fn foo1 ( ) -> impl Fn ( f64 ) { loop { } }
fn foo2 ( ) -> impl Fn ( f64 , f64 ) { loop { } }
fn foo3 ( ) -> impl Fn ( f64 , f64 ) -> u32 { loop { } }
fn foo4 ( ) -> & 'static dyn Fn ( f64 , f64 ) -> u32 { loop { } }
fn foo5 ( ) -> & 'static dyn Fn ( & 'static dyn Fn ( f64 , f64 ) -> u32 , f64 ) -> u32 { loop { } }
fn foo6 ( ) -> impl Fn ( f64 , f64 ) -> u32 + Sized { loop { } }
fn foo7 ( ) -> * const ( impl Fn ( f64 , f64 ) -> u32 + Sized ) { loop { } }
fn main ( ) {
let foo = foo ( ) ;
// ^^^ impl Fn()
let foo = foo1 ( ) ;
// ^^^ impl Fn(f64)
let foo = foo2 ( ) ;
// ^^^ impl Fn(f64, f64)
let foo = foo3 ( ) ;
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo4 ( ) ;
// ^^^ &dyn Fn(f64, f64) -> u32
let foo = foo5 ( ) ;
// ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
let foo = foo6 ( ) ;
2021-08-03 02:58:55 -05:00
// ^^^ impl Fn(f64, f64) -> u32
2021-06-04 06:47:39 -05:00
let foo = foo7 ( ) ;
2021-08-03 02:58:55 -05:00
// ^^^ *const impl Fn(f64, f64) -> u32
2020-06-30 11:04:25 -05:00
}
2021-06-04 06:47:39 -05:00
" #,
)
}
2022-02-11 16:48:01 -06:00
#[ test ]
fn check_hint_range_limit ( ) {
let fixture = r #"
//- minicore: fn, sized
fn foo ( ) -> impl Fn ( ) { loop { } }
fn foo1 ( ) -> impl Fn ( f64 ) { loop { } }
fn foo2 ( ) -> impl Fn ( f64 , f64 ) { loop { } }
fn foo3 ( ) -> impl Fn ( f64 , f64 ) -> u32 { loop { } }
fn foo4 ( ) -> & 'static dyn Fn ( f64 , f64 ) -> u32 { loop { } }
fn foo5 ( ) -> & 'static dyn Fn ( & 'static dyn Fn ( f64 , f64 ) -> u32 , f64 ) -> u32 { loop { } }
fn foo6 ( ) -> impl Fn ( f64 , f64 ) -> u32 + Sized { loop { } }
fn foo7 ( ) -> * const ( impl Fn ( f64 , f64 ) -> u32 + Sized ) { loop { } }
fn main ( ) {
let foo = foo ( ) ;
let foo = foo1 ( ) ;
let foo = foo2 ( ) ;
let foo = foo3 ( ) ;
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo4 ( ) ;
// ^^^ &dyn Fn(f64, f64) -> u32
let foo = foo5 ( ) ;
let foo = foo6 ( ) ;
let foo = foo7 ( ) ;
}
" #;
let ( analysis , file_id ) = fixture ::file ( fixture ) ;
let expected = extract_annotations ( & * analysis . file_text ( file_id ) . unwrap ( ) ) ;
let inlay_hints = analysis
. inlay_hints (
2022-03-16 15:16:55 -05:00
& InlayHintsConfig { type_hints : true , .. DISABLED_CONFIG } ,
2022-02-11 16:48:01 -06:00
file_id ,
Some ( FileRange {
file_id ,
range : TextRange ::new ( TextSize ::from ( 500 ) , TextSize ::from ( 600 ) ) ,
} ) ,
)
. unwrap ( ) ;
let actual =
inlay_hints . into_iter ( ) . map ( | it | ( it . range , it . label . to_string ( ) ) ) . collect ::< Vec < _ > > ( ) ;
assert_eq! ( expected , actual , " \n Expected: \n {:#?} \n \n Actual: \n {:#?} " , expected , actual ) ;
}
2021-08-03 07:01:00 -05:00
#[ test ]
fn fn_hints_ptr_rpit_fn_parentheses ( ) {
check_types (
r #"
//- minicore: fn, sized
trait Trait { }
fn foo1 ( ) -> * const impl Fn ( ) { loop { } }
fn foo2 ( ) -> * const ( impl Fn ( ) + Sized ) { loop { } }
fn foo3 ( ) -> * const ( impl Fn ( ) + ? Sized ) { loop { } }
fn foo4 ( ) -> * const ( impl Sized + Fn ( ) ) { loop { } }
fn foo5 ( ) -> * const ( impl ? Sized + Fn ( ) ) { loop { } }
fn foo6 ( ) -> * const ( impl Fn ( ) + Trait ) { loop { } }
fn foo7 ( ) -> * const ( impl Fn ( ) + Sized + Trait ) { loop { } }
fn foo8 ( ) -> * const ( impl Fn ( ) + ? Sized + Trait ) { loop { } }
fn foo9 ( ) -> * const ( impl Fn ( ) -> u8 + ? Sized ) { loop { } }
fn foo10 ( ) -> * const ( impl Fn ( ) + Sized + ? Sized ) { loop { } }
fn main ( ) {
let foo = foo1 ( ) ;
// ^^^ *const impl Fn()
let foo = foo2 ( ) ;
// ^^^ *const impl Fn()
let foo = foo3 ( ) ;
// ^^^ *const (impl Fn() + ?Sized)
let foo = foo4 ( ) ;
// ^^^ *const impl Fn()
let foo = foo5 ( ) ;
// ^^^ *const (impl Fn() + ?Sized)
let foo = foo6 ( ) ;
// ^^^ *const (impl Fn() + Trait)
let foo = foo7 ( ) ;
// ^^^ *const (impl Fn() + Trait)
let foo = foo8 ( ) ;
// ^^^ *const (impl Fn() + Trait + ?Sized)
let foo = foo9 ( ) ;
// ^^^ *const (impl Fn() -> u8 + ?Sized)
let foo = foo10 ( ) ;
// ^^^ *const impl Fn()
}
" #,
)
}
2021-06-04 06:47:39 -05:00
#[ test ]
fn unit_structs_have_no_type_hints ( ) {
check_types (
r #"
2021-06-18 15:33:01 -05:00
//- minicore: result
2021-06-04 06:47:39 -05:00
struct SyntheticSyntax ;
2020-03-24 13:33:00 -05:00
2020-06-30 11:04:25 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
match Ok ( ( ) ) {
Ok ( _ ) = > ( ) ,
Err ( SyntheticSyntax ) = > ( ) ,
}
2020-06-30 11:04:25 -05:00
} " #,
2020-06-30 14:55:21 -05:00
) ;
2020-03-24 13:33:00 -05:00
}
#[ test ]
2021-06-04 06:47:39 -05:00
fn let_statement ( ) {
check_types (
2020-03-24 13:33:00 -05:00
r #"
2021-06-04 06:47:39 -05:00
#[ derive(PartialEq) ]
enum Option < T > { None , Some ( T ) }
#[ derive(PartialEq) ]
struct Test { a : Option < u32 > , b : u8 }
2020-06-30 14:55:21 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
struct InnerStruct { }
let test = 54 ;
//^^^^ i32
let test : i32 = 33 ;
let mut test = 33 ;
2021-07-31 08:24:21 -05:00
//^^^^ i32
2021-06-04 06:47:39 -05:00
let _ = 22 ;
let test = " test " ;
//^^^^ &str
let test = InnerStruct { } ;
//^^^^ InnerStruct
let test = unresolved ( ) ;
let test = ( 42 , 'a' ) ;
//^^^^ (i32, char)
let ( a , ( b , ( c , ) ) = ( 2 , ( 3 , ( 9.2 , ) ) ;
//^ i32 ^ i32 ^ f64
let & x = & 92 ;
//^ i32
} " #,
) ;
}
#[ test ]
fn if_expr ( ) {
check_types (
r #"
2021-06-18 15:38:19 -05:00
//- minicore: option
2021-06-04 06:47:39 -05:00
struct Test { a : Option < u32 > , b : u8 }
fn main ( ) {
let test = Some ( Test { a : Some ( 3 ) , b : 1 } ) ;
//^^^^ Option<Test>
if let None = & test { } ;
if let test = & test { } ;
//^^^^ &Option<Test>
if let Some ( test ) = & test { } ;
//^^^^ &Test
if let Some ( Test { a , b } ) = & test { } ;
//^ &Option<u32> ^ &u8
if let Some ( Test { a : x , b : y } ) = & test { } ;
//^ &Option<u32> ^ &u8
if let Some ( Test { a : Some ( x ) , b : y } ) = & test { } ;
//^ &u32 ^ &u8
if let Some ( Test { a : None , b : y } ) = & test { } ;
//^ &u8
if let Some ( Test { b : y , .. } ) = & test { } ;
//^ &u8
if test = = None { }
} " #,
) ;
}
#[ test ]
fn while_expr ( ) {
check_types (
r #"
2021-06-18 15:38:19 -05:00
//- minicore: option
2021-06-04 06:47:39 -05:00
struct Test { a : Option < u32 > , b : u8 }
fn main ( ) {
let test = Some ( Test { a : Some ( 3 ) , b : 1 } ) ;
//^^^^ Option<Test>
while let Some ( Test { a : Some ( x ) , b : y } ) = & test { } ;
//^ &u32 ^ &u8
} " #,
) ;
}
#[ test ]
fn match_arm_list ( ) {
check_types (
r #"
2021-06-18 15:38:19 -05:00
//- minicore: option
2021-06-04 06:47:39 -05:00
struct Test { a : Option < u32 > , b : u8 }
fn main ( ) {
match Some ( Test { a : Some ( 3 ) , b : 1 } ) {
None = > ( ) ,
test = > ( ) ,
//^^^^ Option<Test>
Some ( Test { a : Some ( x ) , b : y } ) = > ( ) ,
//^ u32 ^ u8
_ = > { }
}
} " #,
2020-06-30 14:55:21 -05:00
) ;
2020-03-24 13:33:00 -05:00
}
2020-09-13 12:24:04 -05:00
#[ test ]
fn complete_for_hint ( ) {
2021-06-04 06:47:39 -05:00
check_types (
2020-09-13 12:24:04 -05:00
r #"
2021-06-17 03:18:37 -05:00
//- minicore: iterator
2020-10-03 00:37:58 -05:00
pub struct Vec < T > { }
impl < T > Vec < T > {
pub fn new ( ) -> Self { Vec { } }
pub fn push ( & mut self , t : T ) { }
}
impl < T > IntoIterator for Vec < T > {
type Item = T ;
}
2020-09-13 12:24:04 -05:00
fn main ( ) {
2020-10-03 00:37:58 -05:00
let mut data = Vec ::new ( ) ;
2021-07-31 08:24:21 -05:00
//^^^^ Vec<&str>
2020-10-03 00:37:58 -05:00
data . push ( " foo " ) ;
for i in data {
//^ &str
let z = i ;
//^ &str
2020-09-13 12:24:04 -05:00
}
2020-10-03 00:37:58 -05:00
}
2020-10-06 07:40:27 -05:00
" #,
) ;
}
#[ test ]
fn multi_dyn_trait_bounds ( ) {
2021-06-04 06:47:39 -05:00
check_types (
2020-10-06 07:40:27 -05:00
r #"
pub struct Vec < T > { }
impl < T > Vec < T > {
pub fn new ( ) -> Self { Vec { } }
}
pub struct Box < T > { }
trait Display { }
trait Sync { }
fn main ( ) {
2021-10-03 08:31:35 -05:00
// The block expression wrapping disables the constructor hint hiding logic
let _v = { Vec ::< Box < & ( dyn Display + Sync ) > > ::new ( ) } ;
2020-10-06 07:40:27 -05:00
//^^ Vec<Box<&(dyn Display + Sync)>>
2021-10-03 08:31:35 -05:00
let _v = { Vec ::< Box < * const ( dyn Display + Sync ) > > ::new ( ) } ;
2020-10-06 07:40:27 -05:00
//^^ Vec<Box<*const (dyn Display + Sync)>>
2021-10-03 08:31:35 -05:00
let _v = { Vec ::< Box < dyn Display + Sync > > ::new ( ) } ;
2020-10-06 07:40:27 -05:00
//^^ Vec<Box<dyn Display + Sync>>
}
2020-10-06 12:07:34 -05:00
" #,
) ;
}
#[ test ]
fn shorten_iterator_hints ( ) {
2021-06-04 06:47:39 -05:00
check_types (
2020-10-07 03:14:42 -05:00
r #"
2021-06-17 03:28:44 -05:00
//- minicore: iterators
2020-10-07 04:30:42 -05:00
use core ::iter ;
2020-10-06 12:07:34 -05:00
2020-10-06 14:05:57 -05:00
struct MyIter ;
2020-10-07 03:14:42 -05:00
impl Iterator for MyIter {
2020-10-06 14:05:57 -05:00
type Item = ( ) ;
fn next ( & mut self ) -> Option < Self ::Item > {
None
}
}
2020-10-06 12:07:34 -05:00
fn main ( ) {
2020-10-06 14:05:57 -05:00
let _x = MyIter ;
//^^ MyIter
2020-10-06 12:07:34 -05:00
let _x = iter ::repeat ( 0 ) ;
//^^ impl Iterator<Item = i32>
fn generic < T : Clone > ( t : T ) {
let _x = iter ::repeat ( t ) ;
//^^ impl Iterator<Item = T>
2020-10-07 04:30:42 -05:00
let _chained = iter ::repeat ( t ) . take ( 10 ) ;
//^^^^^^^^ impl Iterator<Item = T>
}
}
" #,
) ;
}
2021-10-03 07:45:21 -05:00
#[ test ]
2021-12-21 10:26:37 -06:00
fn skip_constructor_and_enum_type_hints ( ) {
2021-11-13 17:39:34 -06:00
check_with_config (
InlayHintsConfig {
2022-03-11 14:06:26 -06:00
render_colons : true ,
2021-11-13 17:39:34 -06:00
type_hints : true ,
parameter_hints : true ,
chaining_hints : true ,
hide_named_constructor_hints : true ,
2022-03-16 15:16:55 -05:00
.. DISABLED_CONFIG
2021-11-13 17:39:34 -06:00
} ,
2021-10-03 07:45:21 -05:00
r #"
2021-12-21 10:26:37 -06:00
//- minicore: try, option
2021-10-03 07:45:21 -05:00
use core ::ops ::ControlFlow ;
2021-12-22 09:29:26 -06:00
mod x {
pub mod y { pub struct Foo ; }
pub struct Foo ;
pub enum AnotherEnum {
Variant ( )
} ;
}
2021-10-03 07:45:21 -05:00
struct Struct ;
struct TupleStruct ( ) ;
impl Struct {
fn new ( ) -> Self {
Struct
}
fn try_new ( ) -> ControlFlow < ( ) , Self > {
ControlFlow ::Continue ( Struct )
}
}
struct Generic < T > ( T ) ;
impl Generic < i32 > {
fn new ( ) -> Self {
Generic ( 0 )
}
}
2021-12-21 10:26:37 -06:00
enum Enum {
Variant ( u32 )
}
fn times2 ( value : i32 ) -> i32 {
2 * value
}
2021-10-03 07:45:21 -05:00
fn main ( ) {
2021-12-21 10:26:37 -06:00
let enumb = Enum ::Variant ( 0 ) ;
2021-12-22 09:29:26 -06:00
let strukt = x ::Foo ;
let strukt = x ::y ::Foo ;
2021-12-21 10:26:37 -06:00
let strukt = Struct ;
2021-10-03 07:45:21 -05:00
let strukt = Struct ::new ( ) ;
2021-12-21 10:26:37 -06:00
2021-10-03 07:45:21 -05:00
let tuple_struct = TupleStruct ( ) ;
2021-12-21 10:26:37 -06:00
2021-10-03 07:45:21 -05:00
let generic0 = Generic ::new ( ) ;
2021-12-21 10:26:37 -06:00
// ^^^^^^^^ Generic<i32>
let generic1 = Generic ( 0 ) ;
// ^^^^^^^^ Generic<i32>
let generic2 = Generic ::< i32 > ::new ( ) ;
let generic3 = < Generic < i32 > > ::new ( ) ;
let generic4 = Generic ::< i32 > ( 0 ) ;
let option = Some ( 0 ) ;
// ^^^^^^ Option<i32>
let func = times2 ;
// ^^^^ fn times2(i32) -> i32
let closure = | x : i32 | x * 2 ;
// ^^^^^^^ |i32| -> i32
2021-10-03 07:45:21 -05:00
}
fn fallible ( ) -> ControlFlow < ( ) > {
let strukt = Struct ::try_new ( ) ? ;
}
" #,
) ;
}
2021-11-13 17:12:29 -06:00
#[ test ]
fn shows_constructor_type_hints_when_enabled ( ) {
2021-11-13 17:39:34 -06:00
check_types (
2021-11-13 17:12:29 -06:00
r #"
//- minicore: try
use core ::ops ::ControlFlow ;
struct Struct ;
struct TupleStruct ( ) ;
impl Struct {
fn new ( ) -> Self {
Struct
}
fn try_new ( ) -> ControlFlow < ( ) , Self > {
ControlFlow ::Continue ( Struct )
}
}
struct Generic < T > ( T ) ;
impl Generic < i32 > {
fn new ( ) -> Self {
Generic ( 0 )
}
}
fn main ( ) {
let strukt = Struct ::new ( ) ;
// ^^^^^^ Struct
let tuple_struct = TupleStruct ( ) ;
// ^^^^^^^^^^^^ TupleStruct
let generic0 = Generic ::new ( ) ;
// ^^^^^^^^ Generic<i32>
let generic1 = Generic ::< i32 > ::new ( ) ;
// ^^^^^^^^ Generic<i32>
let generic2 = < Generic < i32 > > ::new ( ) ;
// ^^^^^^^^ Generic<i32>
}
fn fallible ( ) -> ControlFlow < ( ) > {
let strukt = Struct ::try_new ( ) ? ;
// ^^^^^^ Struct
}
" #,
) ;
}
2020-10-07 04:30:42 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn closures ( ) {
check (
2020-10-07 04:30:42 -05:00
r #"
2021-06-04 06:47:39 -05:00
fn main ( ) {
let mut start = 0 ;
2021-07-31 08:24:21 -05:00
//^^^^^ i32
2022-03-16 15:16:55 -05:00
( 0 .. 2 ) . for_each ( | increment | { start + = increment ; } ) ;
2021-06-04 06:47:39 -05:00
//^^^^^^^^^ i32
2020-10-07 04:30:42 -05:00
2021-06-04 06:47:39 -05:00
let multiply =
2021-11-22 11:27:03 -06:00
//^^^^^^^^ |i32, i32| -> i32
2021-06-04 06:47:39 -05:00
| a , b | a * b
//^ i32 ^ i32
2022-03-16 15:16:55 -05:00
2021-06-04 06:47:39 -05:00
;
2020-10-07 04:30:42 -05:00
2021-06-04 06:47:39 -05:00
let _ : i32 = multiply ( 1 , 2 ) ;
let multiply_ref = & multiply ;
2021-11-22 11:27:03 -06:00
//^^^^^^^^^^^^ &|i32, i32| -> i32
2021-06-04 06:47:39 -05:00
let return_42 = | | 42 ;
//^^^^^^^^^ || -> i32
2022-03-16 15:16:55 -05:00
| | { 42 } ;
//^^ i32
2021-06-04 06:47:39 -05:00
} " #,
) ;
2020-10-06 12:07:34 -05:00
}
2021-06-04 06:47:39 -05:00
#[ test ]
fn hint_truncation ( ) {
check_with_config (
InlayHintsConfig { max_length : Some ( 8 ) , .. TEST_CONFIG } ,
r #"
struct Smol < T > ( T ) ;
struct VeryLongOuterName < T > ( T ) ;
2020-10-06 12:07:34 -05:00
2020-10-07 04:30:42 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let a = Smol ( 0 u32 ) ;
//^ Smol<u32>
let b = VeryLongOuterName ( 0 usize ) ;
//^ VeryLongOuterName<…>
let c = Smol ( Smol ( 0 u32 ) )
//^ Smol<Smol<…>>
} " #,
) ;
}
// Chaining hint tests
#[ test ]
fn chaining_hints_ignore_comments ( ) {
check_expect (
2022-03-16 15:16:55 -05:00
InlayHintsConfig { type_hints : false , chaining_hints : true , .. DISABLED_CONFIG } ,
2021-06-04 06:47:39 -05:00
r #"
struct A ( B ) ;
impl A { fn into_b ( self ) -> B { self . 0 } }
struct B ( C ) ;
impl B { fn into_c ( self ) -> C { self . 0 } }
struct C ;
fn main ( ) {
let c = A ( B ( C ) )
. into_b ( ) // This is a comment
// This is another comment
. into_c ( ) ;
2020-10-07 04:30:42 -05:00
}
2020-10-03 00:37:58 -05:00
" #,
2020-10-07 04:30:42 -05:00
expect! [ [ r #"
[
InlayHint {
2021-06-17 03:18:37 -05:00
range : 147 .. 172 ,
2020-10-07 04:30:42 -05:00
kind : ChainingHint ,
2021-06-04 06:47:39 -05:00
label : " B " ,
2020-10-07 04:30:42 -05:00
} ,
InlayHint {
2021-06-17 03:18:37 -05:00
range : 147 .. 154 ,
2020-10-07 04:30:42 -05:00
kind : ChainingHint ,
2021-06-04 06:47:39 -05:00
label : " A " ,
2020-10-07 04:30:42 -05:00
} ,
]
" #]],
2020-09-13 12:24:04 -05:00
) ;
}
2020-10-10 11:51:02 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn chaining_hints_without_newlines ( ) {
check_chains (
r #"
struct A ( B ) ;
impl A { fn into_b ( self ) -> B { self . 0 } }
struct B ( C ) ;
impl B { fn into_c ( self ) -> C { self . 0 } }
struct C ;
fn main ( ) {
let c = A ( B ( C ) ) . into_b ( ) . into_c ( ) ;
} " #,
) ;
}
#[ test ]
fn struct_access_chaining_hints ( ) {
check_expect (
2022-03-16 15:16:55 -05:00
InlayHintsConfig { chaining_hints : true , .. DISABLED_CONFIG } ,
2020-10-10 11:51:02 -05:00
r #"
2021-06-04 06:47:39 -05:00
struct A { pub b : B }
struct B { pub c : C }
struct C ( pub bool ) ;
struct D ;
2020-10-10 11:51:02 -05:00
2021-06-04 06:47:39 -05:00
impl D {
fn foo ( & self ) -> i32 { 42 }
2020-10-10 11:51:02 -05:00
}
fn main ( ) {
2021-06-04 06:47:39 -05:00
let x = A { b : B { c : C ( true ) } }
. b
. c
. 0 ;
let x = D
. foo ( ) ;
} " #,
expect! [ [ r #"
[
InlayHint {
2021-06-17 03:18:37 -05:00
range : 143 .. 190 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " C " ,
} ,
InlayHint {
2021-06-17 03:18:37 -05:00
range : 143 .. 179 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " B " ,
} ,
]
" #]],
2020-10-22 12:42:39 -05:00
) ;
}
#[ test ]
2021-06-04 06:47:39 -05:00
fn generic_chaining_hints ( ) {
check_expect (
2022-03-16 15:16:55 -05:00
InlayHintsConfig { chaining_hints : true , .. DISABLED_CONFIG } ,
2020-10-22 12:42:39 -05:00
r #"
2021-06-04 06:47:39 -05:00
struct A < T > ( T ) ;
struct B < T > ( T ) ;
struct C < T > ( T ) ;
struct X < T , R > ( T , R ) ;
2020-10-22 12:42:39 -05:00
2021-06-04 06:47:39 -05:00
impl < T > A < T > {
fn new ( t : T ) -> Self { A ( t ) }
fn into_b ( self ) -> B < T > { B ( self . 0 ) }
}
impl < T > B < T > {
fn into_c ( self ) -> C < T > { C ( self . 0 ) }
}
2020-10-22 12:42:39 -05:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let c = A ::new ( X ( 42 , true ) )
. into_b ( )
. into_c ( ) ;
2020-10-22 12:42:39 -05:00
}
2020-10-10 11:51:02 -05:00
" #,
2021-06-04 06:47:39 -05:00
expect! [ [ r #"
[
InlayHint {
2021-06-17 03:18:37 -05:00
range : 246 .. 283 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " B<X<i32, bool>> " ,
} ,
InlayHint {
2021-06-17 03:18:37 -05:00
range : 246 .. 265 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " A<X<i32, bool>> " ,
} ,
]
" #]],
2020-10-10 11:51:02 -05:00
) ;
}
2020-10-28 06:29:42 -05:00
#[ test ]
2021-06-04 06:47:39 -05:00
fn shorten_iterator_chaining_hints ( ) {
check_expect (
2022-03-16 15:16:55 -05:00
InlayHintsConfig { chaining_hints : true , .. DISABLED_CONFIG } ,
2020-10-28 06:29:42 -05:00
r #"
2021-06-17 03:28:44 -05:00
//- minicore: iterators
2021-06-04 06:47:39 -05:00
use core ::iter ;
2020-10-28 06:29:42 -05:00
2021-06-04 06:47:39 -05:00
struct MyIter ;
2020-10-28 06:29:42 -05:00
2021-06-04 06:47:39 -05:00
impl Iterator for MyIter {
type Item = ( ) ;
fn next ( & mut self ) -> Option < Self ::Item > {
None
2020-10-28 06:29:42 -05:00
}
2020-10-29 07:02:34 -05:00
}
2021-01-12 13:19:13 -06:00
fn main ( ) {
2021-06-04 06:47:39 -05:00
let _x = MyIter . by_ref ( )
. take ( 5 )
. by_ref ( )
. take ( 5 )
. by_ref ( ) ;
2021-01-12 13:19:13 -06:00
}
2020-10-29 07:02:34 -05:00
" #,
2021-06-04 06:47:39 -05:00
expect! [ [ r #"
[
InlayHint {
2021-06-17 03:18:37 -05:00
range : 174 .. 241 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " impl Iterator<Item = ()> " ,
} ,
InlayHint {
2021-06-17 03:18:37 -05:00
range : 174 .. 224 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " impl Iterator<Item = ()> " ,
} ,
InlayHint {
2021-06-17 03:18:37 -05:00
range : 174 .. 206 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " impl Iterator<Item = ()> " ,
} ,
InlayHint {
2021-06-17 03:18:37 -05:00
range : 174 .. 189 ,
2021-06-04 06:47:39 -05:00
kind : ChainingHint ,
label : " &mut MyIter " ,
} ,
]
" #]],
) ;
2021-01-27 07:20:58 -06:00
}
2021-09-18 06:19:29 -05:00
#[ test ]
fn hints_in_attr_call ( ) {
check_expect (
TEST_CONFIG ,
r #"
//- proc_macros: identity, input_replace
struct Struct ;
impl Struct {
fn chain ( self ) -> Self {
self
}
}
#[ proc_macros::identity ]
fn main ( ) {
let strukt = Struct ;
strukt
. chain ( )
. chain ( )
. chain ( ) ;
Struct ::chain ( strukt ) ;
}
" #,
expect! [ [ r #"
[
InlayHint {
range : 124 .. 130 ,
kind : TypeHint ,
label : " Struct " ,
} ,
InlayHint {
range : 145 .. 185 ,
kind : ChainingHint ,
label : " Struct " ,
} ,
InlayHint {
range : 145 .. 168 ,
kind : ChainingHint ,
label : " Struct " ,
} ,
InlayHint {
range : 222 .. 228 ,
kind : ParameterHint ,
label : " self " ,
} ,
]
" #]],
) ;
}
2022-03-18 12:11:16 -05:00
#[ test ]
2022-03-18 12:57:11 -05:00
fn hints_lifetimes ( ) {
2022-03-18 12:11:16 -05:00
check (
r #"
fn empty ( ) { }
fn no_gpl ( a : & ( ) ) { }
//^^^^^^<'a>
// ^'a
fn empty_gpl < > ( a : & ( ) ) { }
// ^'a ^'a
fn partial < ' b > ( a : & ( ) , b : & ' b ( ) ) { }
// ^'a, $ ^'a
2022-03-18 12:55:03 -05:00
fn partial < ' a > ( a : & ' a ( ) , b : & ( ) ) { }
// ^'b, $ ^'b
2022-03-18 12:11:16 -05:00
fn single_ret ( a : & ( ) ) -> & ( ) { }
// ^^^^^^^^^^<'a>
// ^'a ^'a
fn full_mul ( a : & ( ) , b : & ( ) ) { }
// ^^^^^^^^<'a, 'b>
// ^'a ^'b
fn foo < ' c > ( a : & ' c ( ) ) -> & ( ) { }
// ^'c
impl ( ) {
fn foo ( & self ) -> & ( ) { }
2022-03-18 12:55:03 -05:00
// ^^^<'self>
// ^'self ^'self
2022-03-18 12:11:16 -05:00
fn foo ( & self , a : & ( ) ) -> & ( ) { }
2022-03-18 12:55:03 -05:00
// ^^^<'self, 'a>
// ^'self ^'a ^'self$
2022-03-18 12:11:16 -05:00
}
" #,
) ;
}
2019-07-19 16:20:09 -05:00
}