2020-07-16 14:51:44 -05:00
use hir ::{ Adt , Callable , HirDisplay , Semantics , Type } ;
2020-02-06 05:52:32 -06:00
use ra_ide_db ::RootDatabase ;
2019-12-21 11:45:46 -06:00
use ra_prof ::profile ;
2019-07-19 16:20:09 -05:00
use ra_syntax ::{
2020-07-30 13:51:43 -05:00
ast ::{ self , ArgListOwner , AstNode } ,
2020-06-30 11:04:25 -05:00
match_ast , Direction , NodeOrToken , SmolStr , SyntaxKind , TextRange , T ,
2019-07-19 16:20:09 -05:00
} ;
2020-05-03 13:35:21 -05:00
use stdx ::to_lower_snake_case ;
2019-12-21 11:45:46 -06:00
2020-07-16 14:51:44 -05:00
use crate ::FileId ;
use ast ::NameOwner ;
use either ::Either ;
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 {
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 ,
2020-03-10 13:21:56 -05:00
pub max_length : Option < usize > ,
}
2020-03-31 09:02:55 -05:00
impl Default for InlayHintsConfig {
2020-03-10 13:21:56 -05:00
fn default ( ) -> Self {
2020-03-24 14:31:02 -05:00
Self { type_hints : true , parameter_hints : true , chaining_hints : true , max_length : None }
2020-03-10 13:21:56 -05:00
}
}
#[ 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 ,
2020-03-23 14:32:05 -05:00
ChainingHint ,
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.
//
// rust-analyzer shows hits for
//
// * types of local variables
// * names of function arguments
// * types of chained expressions
//
// **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*
// |===
2019-11-18 11:02:28 -06:00
pub ( crate ) fn inlay_hints (
db : & RootDatabase ,
file_id : FileId ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2019-11-18 11:02:28 -06:00
) -> Vec < InlayHint > {
2020-02-29 16:24:50 -06:00
let _p = profile ( " inlay_hints " ) ;
2020-02-18 11:35:10 -06:00
let sema = Semantics ::new ( db ) ;
let file = sema . parse ( file_id ) ;
2020-02-29 16:24:50 -06:00
2020-01-16 07:31:34 -06:00
let mut res = Vec ::new ( ) ;
for node in file . syntax ( ) . descendants ( ) {
2020-03-23 14:32:05 -05:00
if let Some ( expr ) = ast ::Expr ::cast ( node . clone ( ) ) {
2020-03-31 09:02:55 -05:00
get_chaining_hints ( & mut res , & sema , config , expr ) ;
2020-03-23 14:32:05 -05:00
}
2020-02-29 16:24:50 -06:00
match_ast! {
match node {
2020-03-31 09:02:55 -05:00
ast ::CallExpr ( it ) = > { get_param_name_hints ( & mut res , & sema , config , ast ::Expr ::from ( it ) ) ; } ,
ast ::MethodCallExpr ( it ) = > { get_param_name_hints ( & mut res , & sema , config , ast ::Expr ::from ( it ) ) ; } ,
2020-07-31 13:09:09 -05:00
ast ::IdentPat ( it ) = > { get_bind_pat_hints ( & mut res , & sema , config , it ) ; } ,
2020-02-29 16:24:50 -06:00
_ = > ( ) ,
}
}
2020-01-16 07:31:34 -06:00
}
res
2019-07-19 16:20:09 -05:00
}
2020-03-24 13:33:00 -05:00
fn get_chaining_hints (
acc : & mut Vec < InlayHint > ,
sema : & Semantics < RootDatabase > ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2020-03-24 13:33:00 -05:00
expr : ast ::Expr ,
) -> 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 ;
}
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 ( ) ;
let next_next = tokens . next ( ) ? . kind ( ) ;
2020-06-30 11:04:25 -05:00
if next = = SyntaxKind ::WHITESPACE & & next_next = = T! [ . ] {
2020-03-31 13:25:03 -05:00
let ty = sema . type_of_expr ( & expr ) ? ;
if ty . is_unknown ( ) {
return None ;
}
if matches! ( expr , ast ::Expr ::PathExpr ( _ ) ) {
if let Some ( Adt ::Struct ( st ) ) = ty . as_adt ( ) {
if st . fields ( sema . db ) . is_empty ( ) {
return None ;
}
}
}
2020-03-31 09:02:55 -05:00
let label = ty . display_truncated ( sema . db , config . max_length ) . to_string ( ) ;
2020-03-24 13:33:00 -05:00
acc . push ( InlayHint {
range : expr . syntax ( ) . text_range ( ) ,
kind : InlayKind ::ChainingHint ,
label : label . into ( ) ,
} ) ;
}
Some ( ( ) )
}
2020-02-29 16:24:50 -06:00
fn get_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
}
2020-02-29 16:24:50 -06:00
let args = match & expr {
ast ::Expr ::CallExpr ( expr ) = > expr . arg_list ( ) ? . args ( ) ,
ast ::Expr ::MethodCallExpr ( expr ) = > expr . arg_list ( ) ? . args ( ) ,
_ = > return None ,
2020-01-16 07:31:34 -06:00
} ;
2020-02-29 16:24:50 -06:00
2020-07-16 14:51:44 -05:00
let callable = get_callable ( sema , & expr ) ? ;
let hints = callable
. params ( sema . db )
. into_iter ( )
2020-02-29 16:24:50 -06:00
. zip ( args )
2020-07-16 14:51:44 -05:00
. filter_map ( | ( ( param , _ty ) , arg ) | match param ? {
Either ::Left ( self_param ) = > Some ( ( self_param . to_string ( ) , arg ) ) ,
Either ::Right ( pat ) = > {
let param_name = match pat {
2020-07-31 13:09:09 -05:00
ast ::Pat ::IdentPat ( it ) = > it . name ( ) ? . to_string ( ) ,
2020-07-16 14:51:44 -05:00
it = > it . to_string ( ) ,
} ;
Some ( ( param_name , arg ) )
}
} )
. filter ( | ( param_name , arg ) | should_show_param_name_hint ( sema , & callable , & param_name , & arg ) )
2020-02-29 16:24:50 -06:00
. map ( | ( param_name , arg ) | InlayHint {
range : arg . syntax ( ) . text_range ( ) ,
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 ( ( ) )
}
fn get_bind_pat_hints (
acc : & mut Vec < InlayHint > ,
sema : & Semantics < RootDatabase > ,
2020-03-31 09:02:55 -05:00
config : & InlayHintsConfig ,
2020-07-31 13:09:09 -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
}
2020-02-29 16:24:50 -06:00
let ty = sema . type_of_pat ( & pat . clone ( ) . into ( ) ) ? ;
if should_not_display_type_hint ( sema . db , & pat , & ty ) {
return None ;
}
acc . push ( InlayHint {
range : pat . syntax ( ) . text_range ( ) ,
kind : InlayKind ::TypeHint ,
2020-03-31 09:02:55 -05:00
label : ty . display_truncated ( sema . db , config . max_length ) . to_string ( ) . into ( ) ,
2020-02-29 16:24:50 -06: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
2020-07-31 13:09:09 -05:00
fn pat_is_enum_variant ( db : & RootDatabase , bind_pat : & ast ::IdentPat , pat_ty : & Type ) -> bool {
2020-02-24 01:29:34 -06:00
if let Some ( 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 ( )
. map ( | variant | variant . name ( db ) . to_string ( ) )
. any ( | enum_name | enum_name = = pat_text )
} else {
false
}
}
2020-07-31 13:09:09 -05:00
fn should_not_display_type_hint (
db : & RootDatabase ,
bind_pat : & ast ::IdentPat ,
pat_ty : & Type ,
) -> bool {
2020-02-24 01:29:34 -06:00
if pat_ty . is_unknown ( ) {
return true ;
}
2020-03-08 16:21:08 -05:00
if let Some ( Adt ::Struct ( s ) ) = pat_ty . as_adt ( ) {
2020-05-03 07:07:57 -05:00
if s . fields ( db ) . is_empty ( ) & & s . name ( db ) . to_string ( ) = = 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 {
ast ::LetStmt ( it ) = > {
2020-07-30 13:51:43 -05:00
return it . ty ( ) . is_some ( )
2020-02-22 14:07:09 -06:00
} ,
ast ::Param ( it ) = > {
2020-07-30 13:51:43 -05:00
return it . ty ( ) . is_some ( )
2020-02-22 14:07:09 -06:00
} ,
2020-02-24 01:29:34 -06:00
ast ::MatchArm ( _it ) = > {
return pat_is_enum_variant ( db , bind_pat , pat_ty ) ;
} ,
ast ::IfExpr ( it ) = > {
return it . condition ( ) . and_then ( | condition | condition . pat ( ) ) . is_some ( )
& & pat_is_enum_variant ( db , bind_pat , pat_ty ) ;
} ,
ast ::WhileExpr ( it ) = > {
return it . condition ( ) . and_then ( | condition | condition . pat ( ) ) . is_some ( )
& & pat_is_enum_variant ( db , bind_pat , pat_ty ) ;
} ,
2020-02-22 14:07:09 -06:00
_ = > ( ) ,
}
}
}
false
}
2020-05-03 07:07:57 -05:00
fn should_show_param_name_hint (
2020-05-03 07:50:35 -05:00
sema : & Semantics < RootDatabase > ,
2020-07-16 14:51:44 -05:00
callable : & 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 {
2020-05-03 07:50:35 -05:00
let param_name = param_name . trim_start_matches ( '_' ) ;
2020-07-16 14:51:44 -05:00
let fn_name = match callable . kind ( ) {
hir ::CallableKind ::Function ( it ) = > Some ( it . name ( sema . db ) . to_string ( ) ) ,
2020-07-17 03:57:49 -05:00
hir ::CallableKind ::TupleStruct ( _ )
| hir ::CallableKind ::TupleEnumVariant ( _ )
| hir ::CallableKind ::Closure = > None ,
2020-07-16 14:51:44 -05:00
} ;
2020-04-09 05:47:48 -05:00
if param_name . is_empty ( )
2020-07-16 14:51:44 -05:00
| | Some ( param_name ) = = fn_name . as_ref ( ) . map ( | s | s . trim_start_matches ( '_' ) )
2020-05-03 07:50:35 -05:00
| | is_argument_similar_to_param_name ( sema , argument , param_name )
2020-06-28 06:11:41 -05:00
| | param_name . starts_with ( " ra_fixture " )
2020-04-09 05:47:48 -05:00
{
2020-02-23 03:49:53 -06:00
return false ;
}
// avoid displaying hints for common functions like map, filter, etc.
2020-04-08 16:48:16 -05:00
// or other obvious words used in std
2020-07-16 14:51:44 -05:00
! ( callable . n_params ( ) = = 1 & & is_obvious_param ( param_name ) )
2020-04-09 09:35:07 -05:00
}
2020-05-03 07:50:35 -05:00
fn is_argument_similar_to_param_name (
sema : & Semantics < RootDatabase > ,
argument : & ast ::Expr ,
param_name : & str ,
) -> bool {
if is_enum_name_similar_to_param_name ( sema , argument , param_name ) {
return true ;
2020-05-04 02:58:21 -05:00
}
match get_string_representation ( argument ) {
None = > false ,
Some ( repr ) = > {
let argument_string = repr . trim_start_matches ( '_' ) ;
argument_string . starts_with ( param_name ) | | argument_string . ends_with ( param_name )
}
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 {
match sema . type_of_expr ( argument ) . and_then ( | t | t . as_adt ( ) ) {
2020-05-03 13:35:21 -05:00
Some ( Adt ::Enum ( e ) ) = > to_lower_snake_case ( & e . name ( sema . db ) . to_string ( ) ) = = 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 ) = > {
Some ( method_call_expr . name_ref ( ) ? . to_string ( ) )
2020-04-09 11:26:49 -05:00
}
2020-05-03 07:07:57 -05:00
ast ::Expr ::RefExpr ( ref_expr ) = > get_string_representation ( & ref_expr . expr ( ) ? ) ,
_ = > Some ( expr . to_string ( ) ) ,
2020-04-09 11:26:49 -05:00
}
}
2020-04-09 09:35:07 -05:00
fn is_obvious_param ( param_name : & str ) -> bool {
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
}
2020-07-16 14:51:44 -05:00
fn get_callable ( sema : & Semantics < RootDatabase > , expr : & ast ::Expr ) -> Option < Callable > {
2020-01-14 11:02:01 -06:00
match expr {
2020-07-16 14:51:44 -05:00
ast ::Expr ::CallExpr ( expr ) = > sema . type_of_expr ( & expr . expr ( ) ? ) ? . as_callable ( sema . db ) ,
ast ::Expr ::MethodCallExpr ( expr ) = > sema . resolve_method_call_as_callable ( expr ) ,
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-06-30 14:55:21 -05:00
use expect ::{ expect , Expect } ;
2020-06-30 11:04:25 -05:00
use test_utils ::extract_annotations ;
2019-07-19 16:20:09 -05:00
2020-06-30 11:04:25 -05:00
use crate ::{ inlay_hints ::InlayHintsConfig , mock_analysis ::single_file } ;
fn check ( ra_fixture : & str ) {
2020-07-09 09:12:53 -05:00
check_with_config ( InlayHintsConfig ::default ( ) , ra_fixture ) ;
2020-06-30 11:04:25 -05:00
}
2020-07-09 09:12:53 -05:00
fn check_with_config ( config : InlayHintsConfig , ra_fixture : & str ) {
2020-06-30 11:04:25 -05:00
let ( analysis , file_id ) = single_file ( ra_fixture ) ;
let expected = extract_annotations ( & * analysis . file_text ( file_id ) . unwrap ( ) ) ;
let inlay_hints = analysis . inlay_hints ( file_id , & config ) . unwrap ( ) ;
let actual =
inlay_hints . into_iter ( ) . map ( | it | ( it . range , it . label . to_string ( ) ) ) . collect ::< Vec < _ > > ( ) ;
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
2020-07-09 09:12:53 -05:00
fn check_expect ( config : InlayHintsConfig , ra_fixture : & str , expect : Expect ) {
2020-06-30 14:55:21 -05:00
let ( analysis , file_id ) = single_file ( ra_fixture ) ;
let inlay_hints = analysis . inlay_hints ( file_id , & config ) . unwrap ( ) ;
expect . assert_debug_eq ( & inlay_hints )
}
2020-03-10 02:55:46 -05:00
#[ test ]
fn param_hints_only ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
2020-07-09 09:12:53 -05:00
InlayHintsConfig {
parameter_hints : true ,
type_hints : false ,
chaining_hints : false ,
max_length : None ,
} ,
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 }
fn main ( ) {
let _x = foo (
4 ,
//^ a
4 ,
//^ b
) ;
} " #,
) ;
2020-03-10 02:55:46 -05:00
}
#[ test ]
fn hints_disabled ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
InlayHintsConfig {
type_hints : false ,
parameter_hints : false ,
chaining_hints : false ,
max_length : None ,
} ,
2020-07-09 09:12:53 -05:00
r #"
fn foo ( a : i32 , b : i32 ) -> i32 { a + b }
fn main ( ) {
let _x = foo ( 4 , 4 ) ;
} " #,
2020-03-10 02:55:46 -05:00
) ;
}
#[ test ]
fn type_hints_only ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
InlayHintsConfig {
type_hints : true ,
parameter_hints : false ,
chaining_hints : false ,
max_length : None ,
2020-03-10 02:55:46 -05:00
} ,
2020-07-09 09:12:53 -05:00
r #"
fn foo ( a : i32 , b : i32 ) -> i32 { a + b }
fn main ( ) {
let _x = foo ( 4 , 4 ) ;
//^^ i32
} " #,
2020-06-30 11:04:25 -05:00
) ;
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 ]
2019-12-19 08:18:09 -06:00
fn default_generic_types_should_not_be_displayed ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-12-07 16:54:18 -06:00
r #"
2020-06-30 11:04:25 -05:00
struct Test < K , T = u8 > { k : K , t : T }
2019-12-07 16:54:18 -06:00
fn main ( ) {
2020-05-29 12:14:04 -05:00
let zz = Test { t : 23 u8 , k : 33 } ;
2020-06-30 11:04:25 -05:00
//^^ Test<i32>
2020-01-22 08:44:05 -06:00
let zz_ref = & zz ;
2020-06-30 11:04:25 -05:00
//^^^^^^ &Test<i32>
2020-07-20 15:50:41 -05:00
let test = | | zz ;
//^^^^ || -> Test<i32>
2019-12-07 16:54:18 -06:00
} " #,
) ;
}
2019-07-19 16:20:09 -05:00
#[ test ]
2019-07-27 16:50:26 -05:00
fn let_statement ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-07-19 16:20:09 -05:00
r #"
2019-07-27 16:50:26 -05:00
#[ derive(PartialEq) ]
2020-06-30 11:04:25 -05:00
enum Option < T > { None , Some ( T ) }
2019-07-27 16:50:26 -05:00
#[ derive(PartialEq) ]
2020-06-30 11:04:25 -05:00
struct Test { a : Option < u32 > , b : u8 }
2019-07-19 16:20:09 -05:00
fn main ( ) {
struct InnerStruct { }
let test = 54 ;
2020-06-30 11:04:25 -05:00
//^^^^ i32
2019-07-26 06:10:29 -05:00
let test : i32 = 33 ;
let mut test = 33 ;
2020-06-30 11:04:25 -05:00
//^^^^^^^^ i32
2019-07-26 06:10:29 -05:00
let _ = 22 ;
let test = " test " ;
2020-06-30 11:04:25 -05:00
//^^^^ &str
2019-07-19 16:20:09 -05:00
let test = InnerStruct { } ;
2019-07-26 06:10:29 -05:00
2020-06-30 11:04:25 -05:00
let test = unresolved ( ) ;
2019-07-26 06:10:29 -05:00
2019-07-19 16:20:09 -05:00
let test = ( 42 , 'a' ) ;
2020-06-30 11:04:25 -05:00
//^^^^ (i32, char)
let ( a , ( b , ( c , ) ) = ( 2 , ( 3 , ( 9.2 , ) ) ;
//^ i32 ^ i32 ^ f64
2019-12-07 12:14:01 -06:00
let & x = & 92 ;
2020-06-30 11:04:25 -05:00
//^ i32
2019-07-27 16:50:26 -05:00
} " #,
2019-07-21 15:28:05 -05:00
) ;
2019-07-27 16:50:26 -05:00
}
#[ test ]
2019-12-23 09:53:35 -06:00
fn closure_parameters ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-07-27 16:50:26 -05:00
r #"
fn main ( ) {
let mut start = 0 ;
2020-06-30 11:04:25 -05:00
//^^^^^^^^^ i32
( 0 .. 2 ) . for_each ( | increment | { start + = increment ; } ) ;
//^^^^^^^^^ i32
2019-12-23 09:53:35 -06:00
2020-06-30 11:04:25 -05:00
let multiply =
//^^^^^^^^ |…| -> i32
| a , b | a * b
//^ i32 ^ i32
;
let _ : i32 = multiply ( 1 , 2 ) ;
2020-01-22 08:44:05 -06:00
let multiply_ref = & multiply ;
2020-06-30 11:04:25 -05:00
//^^^^^^^^^^^^ &|…| -> i32
2019-12-23 09:53:35 -06:00
let return_42 = | | 42 ;
2020-06-30 11:04:25 -05:00
//^^^^^^^^^ || -> i32
2019-07-27 16:50:26 -05:00
} " #,
) ;
}
#[ test ]
fn for_expression ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-07-27 16:50:26 -05:00
r #"
fn main ( ) {
let mut start = 0 ;
2020-06-30 11:04:25 -05:00
//^^^^^^^^^ i32
for increment in 0 .. 2 { start + = increment ; }
//^^^^^^^^^ i32
2019-07-27 16:50:26 -05:00
} " #,
) ;
}
#[ test ]
fn if_expr ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-07-27 16:50:26 -05:00
r #"
2020-06-30 11:04:25 -05:00
enum Option < T > { None , Some ( T ) }
use Option ::* ;
2019-07-27 16:50:26 -05:00
2020-06-30 11:04:25 -05:00
struct Test { a : Option < u32 > , b : u8 }
2020-02-24 01:29:34 -06:00
2019-07-27 16:50:26 -05:00
fn main ( ) {
2020-02-24 01:29:34 -06:00
let test = Some ( Test { a : Some ( 3 ) , b : 1 } ) ;
2020-06-30 11:04:25 -05:00
//^^^^ Option<Test>
2020-02-24 01:29:34 -06:00
if let None = & test { } ;
2019-07-27 16:50:26 -05:00
if let test = & test { } ;
2020-06-30 11:04:25 -05:00
//^^^^ &Option<Test>
2020-02-24 01:29:34 -06:00
if let Some ( test ) = & test { } ;
2020-06-30 11:04:25 -05:00
//^^^^ &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
2020-02-24 01:29:34 -06:00
if let Some ( Test { b : y , .. } ) = & test { } ;
2020-06-30 11:04:25 -05:00
//^ &u8
2020-02-24 01:29:34 -06:00
if test = = None { }
2019-07-27 16:50:26 -05:00
} " #,
) ;
}
#[ test ]
fn while_expr ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-07-27 16:50:26 -05:00
r #"
2020-06-30 11:04:25 -05:00
enum Option < T > { None , Some ( T ) }
use Option ::* ;
2019-07-27 16:50:26 -05:00
2020-06-30 11:04:25 -05:00
struct Test { a : Option < u32 > , b : u8 }
2020-02-24 01:29:34 -06:00
2019-07-27 16:50:26 -05:00
fn main ( ) {
2020-02-24 01:29:34 -06:00
let test = Some ( Test { a : Some ( 3 ) , b : 1 } ) ;
2020-06-30 11:04:25 -05:00
//^^^^ Option<Test>
while let Some ( Test { a : Some ( x ) , b : y } ) = & test { } ;
//^ &u32 ^ &u8
2019-07-27 16:50:26 -05:00
} " #,
) ;
}
#[ test ]
fn match_arm_list ( ) {
2020-06-30 11:04:25 -05:00
check (
2019-07-27 16:50:26 -05:00
r #"
2020-06-30 11:04:25 -05:00
enum Option < T > { None , Some ( T ) }
use Option ::* ;
2019-07-27 16:50:26 -05:00
2020-06-30 11:04:25 -05:00
struct Test { a : Option < u32 > , b : u8 }
2020-02-24 01:29:34 -06:00
2019-07-27 16:50:26 -05:00
fn main ( ) {
2020-02-24 01:29:34 -06:00
match Some ( Test { a : Some ( 3 ) , b : 1 } ) {
None = > ( ) ,
2019-07-27 16:50:26 -05:00
test = > ( ) ,
2020-06-30 11:04:25 -05:00
//^^^^ Option<Test>
2020-02-24 01:29:34 -06:00
Some ( Test { a : Some ( x ) , b : y } ) = > ( ) ,
2020-06-30 11:04:25 -05:00
//^ u32 ^ u8
2019-07-27 16:50:26 -05:00
_ = > { }
}
} " #,
) ;
2019-07-19 16:20:09 -05:00
}
2019-11-19 10:40:38 -06:00
#[ test ]
fn hint_truncation ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
2020-07-09 09:12:53 -05:00
InlayHintsConfig { max_length : Some ( 8 ) , .. Default ::default ( ) } ,
2019-11-19 10:40:38 -06:00
r #"
struct Smol < T > ( T ) ;
struct VeryLongOuterName < T > ( T ) ;
fn main ( ) {
let a = Smol ( 0 u32 ) ;
2020-06-30 11:04:25 -05:00
//^ Smol<u32>
2019-11-19 10:40:38 -06:00
let b = VeryLongOuterName ( 0 usize ) ;
2020-06-30 11:04:25 -05:00
//^ VeryLongOuterName<…>
2019-11-19 10:40:38 -06:00
let c = Smol ( Smol ( 0 u32 ) )
2020-06-30 11:04:25 -05:00
//^ Smol<Smol<…>>
2019-11-19 10:40:38 -06:00
} " #,
) ;
}
2020-01-14 11:02:01 -06:00
#[ test ]
fn function_call_parameter_hint ( ) {
2020-06-30 11:04:25 -05:00
check (
2020-01-14 11:02:01 -06:00
r #"
2020-06-30 11:04:25 -05:00
enum Option < T > { None , Some ( T ) }
use Option ::* ;
2020-02-23 03:49:53 -06:00
struct FileId { }
struct SmolStr { }
struct TextRange { }
struct SyntaxKind { }
struct NavigationTarget { }
2020-01-14 11:02:01 -06:00
struct Test { }
impl Test {
2020-06-30 11:04:25 -05:00
fn method ( & self , mut param : i32 ) -> i32 { param * 2 }
2020-02-23 03:49:53 -06:00
fn from_syntax (
file_id : FileId ,
name : SmolStr ,
2020-06-30 11:04:25 -05:00
focus_range : Option < TextRange > ,
2020-02-23 03:49:53 -06:00
full_range : TextRange ,
kind : SyntaxKind ,
2020-06-30 11:04:25 -05:00
docs : Option < String > ,
2020-02-23 03:49:53 -06:00
) -> NavigationTarget {
NavigationTarget { }
}
2020-01-14 11:02:01 -06:00
}
2020-01-18 06:40:32 -06:00
fn test_func ( mut foo : i32 , bar : i32 , msg : & str , _ : i32 , last : i32 ) -> i32 {
2020-01-14 11:02:01 -06:00
foo + bar
}
fn main ( ) {
let not_literal = 1 ;
2020-06-30 11:04:25 -05:00
//^^^^^^^^^^^ i32
let _ : i32 = test_func ( 1 , 2 , " hello " , 3 , not_literal ) ;
//^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
2020-01-14 11:02:01 -06:00
let t : Test = Test { } ;
t . method ( 123 ) ;
2020-06-30 11:04:25 -05:00
//^^^ param
Test ::method ( & t , 3456 ) ;
//^^ &self ^^^^ param
2020-02-23 03:49:53 -06:00
Test ::from_syntax (
FileId { } ,
2020-06-30 11:04:25 -05:00
//^^^^^^^^^ file_id
2020-02-23 03:49:53 -06:00
" impl " . into ( ) ,
2020-06-30 11:04:25 -05:00
//^^^^^^^^^^^^^ name
2020-02-24 01:29:34 -06:00
None ,
2020-06-30 11:04:25 -05:00
//^^^^ focus_range
2020-02-23 03:49:53 -06:00
TextRange { } ,
2020-06-30 11:04:25 -05:00
//^^^^^^^^^^^^ full_range
2020-02-23 03:49:53 -06:00
SyntaxKind { } ,
2020-06-30 11:04:25 -05:00
//^^^^^^^^^^^^^ kind
2020-02-24 01:29:34 -06:00
None ,
2020-06-30 11:04:25 -05:00
//^^^^ docs
2020-02-23 03:49:53 -06:00
) ;
2020-01-14 11:02:01 -06:00
} " #,
) ;
}
2020-02-23 03:49:53 -06:00
#[ test ]
fn omitted_parameters_hints_heuristics ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
2020-07-09 09:12:53 -05:00
InlayHintsConfig { max_length : Some ( 8 ) , .. Default ::default ( ) } ,
2020-02-23 03:49:53 -06:00
r #"
fn map ( f : i32 ) { }
fn filter ( predicate : i32 ) { }
struct TestVarContainer {
test_var : i32 ,
}
2020-05-03 07:07:57 -05:00
impl TestVarContainer {
fn test_var ( & self ) -> i32 {
self . test_var
}
}
2020-02-23 03:49:53 -06:00
struct Test { }
impl Test {
fn map ( self , f : i32 ) -> Self {
self
}
fn filter ( self , predicate : i32 ) -> Self {
self
}
2020-04-08 16:48:16 -05:00
fn field ( self , value : i32 ) -> Self {
self
}
2020-02-23 03:49:53 -06:00
fn no_hints_expected ( & self , _ : i32 , test_var : i32 ) { }
2020-04-09 05:47:48 -05:00
fn frob ( & self , frob : bool ) { }
2020-02-23 03:49:53 -06:00
}
2020-04-08 16:48:16 -05:00
struct Param { }
2020-04-08 18:07:21 -05:00
fn different_order ( param : & Param ) { }
fn different_order_mut ( param : & mut Param ) { }
2020-04-18 02:53:48 -05:00
fn has_underscore ( _param : bool ) { }
2020-05-03 07:50:35 -05:00
fn enum_matches_param_name ( completion_kind : CompletionKind ) { }
2020-04-08 16:48:16 -05:00
2020-04-09 05:47:48 -05:00
fn twiddle ( twiddle : bool ) { }
2020-04-18 02:53:48 -05:00
fn doo ( _doo : bool ) { }
2020-04-09 05:47:48 -05:00
2020-05-03 07:50:35 -05:00
enum CompletionKind {
Keyword ,
}
2020-02-23 03:49:53 -06:00
fn main ( ) {
let container : TestVarContainer = TestVarContainer { test_var : 42 } ;
let test : Test = Test { } ;
map ( 22 ) ;
filter ( 33 ) ;
2020-04-08 16:48:16 -05:00
let test_processed : Test = test . map ( 1 ) . filter ( 2 ) . field ( 3 ) ;
2020-02-23 03:49:53 -06:00
let test_var : i32 = 55 ;
test_processed . no_hints_expected ( 22 , test_var ) ;
test_processed . no_hints_expected ( 33 , container . test_var ) ;
2020-05-03 07:07:57 -05:00
test_processed . no_hints_expected ( 44 , container . test_var ( ) ) ;
2020-04-09 05:47:48 -05:00
test_processed . frob ( false ) ;
twiddle ( true ) ;
2020-04-18 02:53:48 -05:00
doo ( true ) ;
2020-04-08 16:48:16 -05:00
2020-05-03 07:07:57 -05:00
let mut param_begin : Param = Param { } ;
2020-04-08 18:07:21 -05:00
different_order ( & param_begin ) ;
different_order ( & mut param_begin ) ;
2020-04-08 16:48:16 -05:00
2020-04-18 02:53:48 -05:00
let param : bool = true ;
has_underscore ( param ) ;
2020-05-03 07:50:35 -05:00
enum_matches_param_name ( CompletionKind ::Keyword ) ;
2020-04-08 16:48:16 -05:00
let a : f64 = 7.0 ;
let b : f64 = 4.0 ;
let _ : f64 = a . div_euclid ( b ) ;
let _ : f64 = a . abs_sub ( b ) ;
2020-02-23 03:49:53 -06:00
} " #,
) ;
}
2020-03-08 16:21:08 -05:00
#[ test ]
fn unit_structs_have_no_type_hints ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
2020-07-09 09:12:53 -05:00
InlayHintsConfig { max_length : Some ( 8 ) , .. Default ::default ( ) } ,
2020-03-08 16:21:08 -05:00
r #"
2020-06-30 11:04:25 -05:00
enum Result < T , E > { Ok ( T ) , Err ( E ) }
use Result ::* ;
2020-03-08 16:21:08 -05:00
struct SyntheticSyntax ;
fn main ( ) {
match Ok ( ( ) ) {
Ok ( _ ) = > ( ) ,
Err ( SyntheticSyntax ) = > ( ) ,
}
} " #,
) ;
}
2020-03-24 13:33:00 -05:00
#[ test ]
fn chaining_hints_ignore_comments ( ) {
2020-06-30 14:55:21 -05:00
check_expect (
2020-07-09 09:12:53 -05:00
InlayHintsConfig {
parameter_hints : false ,
type_hints : false ,
chaining_hints : true ,
max_length : None ,
} ,
2020-03-24 13:33:00 -05:00
r #"
2020-06-30 14:55:21 -05:00
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
. into_c ( ) ;
}
" #,
expect! [ [ r #"
[
InlayHint {
range : 147 .. 172 ,
kind : ChainingHint ,
label : " B " ,
} ,
InlayHint {
range : 147 .. 154 ,
kind : ChainingHint ,
label : " A " ,
} ,
]
" #]],
) ;
2020-03-24 13:33:00 -05:00
}
#[ test ]
fn chaining_hints_without_newlines ( ) {
2020-06-30 11:04:25 -05:00
check_with_config (
2020-07-09 09:12:53 -05:00
InlayHintsConfig {
parameter_hints : false ,
type_hints : false ,
chaining_hints : true ,
max_length : None ,
} ,
2020-03-24 13:33:00 -05:00
r #"
2020-06-30 11:04:25 -05:00
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 ;
2020-03-24 13:33:00 -05:00
2020-06-30 11:04:25 -05:00
fn main ( ) {
let c = A ( B ( C ) ) . into_b ( ) . into_c ( ) ;
} " #,
2020-03-24 13:33:00 -05:00
) ;
}
#[ test ]
fn struct_access_chaining_hints ( ) {
2020-06-30 14:55:21 -05:00
check_expect (
2020-07-09 09:12:53 -05:00
InlayHintsConfig {
parameter_hints : false ,
type_hints : false ,
chaining_hints : true ,
max_length : None ,
} ,
2020-03-24 13:33:00 -05:00
r #"
2020-06-30 11:04:25 -05:00
struct A { pub b : B }
struct B { pub c : C }
struct C ( pub bool ) ;
struct D ;
2020-03-31 13:25:03 -05:00
2020-06-30 11:04:25 -05:00
impl D {
fn foo ( & self ) -> i32 { 42 }
}
2020-03-24 13:33:00 -05:00
2020-06-30 11:04:25 -05:00
fn main ( ) {
let x = A { b : B { c : C ( true ) } }
. b
. c
. 0 ;
let x = D
. foo ( ) ;
} " #,
2020-06-30 14:55:21 -05:00
expect! [ [ r #"
[
InlayHint {
range : 143 .. 190 ,
kind : ChainingHint ,
label : " C " ,
} ,
InlayHint {
range : 143 .. 179 ,
kind : ChainingHint ,
label : " B " ,
} ,
]
" #]],
) ;
2020-03-24 13:33:00 -05:00
}
#[ test ]
fn generic_chaining_hints ( ) {
2020-06-30 14:55:21 -05:00
check_expect (
2020-07-09 09:12:53 -05:00
InlayHintsConfig {
parameter_hints : false ,
type_hints : false ,
chaining_hints : true ,
max_length : None ,
} ,
2020-03-24 13:33:00 -05:00
r #"
2020-06-30 14:55:21 -05:00
struct A < T > ( T ) ;
struct B < T > ( T ) ;
struct C < T > ( T ) ;
struct X < T , R > ( T , R ) ;
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 ) }
}
fn main ( ) {
let c = A ::new ( X ( 42 , true ) )
. into_b ( )
. into_c ( ) ;
}
" #,
expect! [ [ r #"
[
InlayHint {
range : 246 .. 283 ,
kind : ChainingHint ,
label : " B<X<i32, bool>> " ,
} ,
InlayHint {
range : 246 .. 265 ,
kind : ChainingHint ,
label : " A<X<i32, bool>> " ,
} ,
]
" #]],
) ;
2020-03-24 13:33:00 -05:00
}
2019-07-19 16:20:09 -05:00
}