1- use hir:: { db:: AstDatabase , HirDisplay , Type } ;
1+ use either:: Either ;
2+ use hir:: { db:: AstDatabase , HirDisplay , InFile , Type } ;
23use ide_db:: { famous_defs:: FamousDefs , source_change:: SourceChange } ;
34use syntax:: {
45 ast:: { self , BlockExpr , ExprStmt } ,
5- AstNode ,
6+ AstNode , AstPtr ,
67} ;
78use text_edit:: TextEdit ;
89
910use crate :: { adjusted_display_range, fix, Assist , Diagnostic , DiagnosticsContext } ;
1011
1112// Diagnostic: type-mismatch
1213//
13- // This diagnostic is triggered when the type of an expression does not match
14+ // This diagnostic is triggered when the type of an expression or pattern does not match
1415// the expected type.
1516pub ( crate ) fn type_mismatch ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: TypeMismatch ) -> Diagnostic {
16- let display_range = adjusted_display_range :: < ast:: BlockExpr > (
17- ctx,
18- d. expr . clone ( ) . map ( |it| it. into ( ) ) ,
19- & |block| {
20- let r_curly_range = block. stmt_list ( ) ?. r_curly_token ( ) ?. text_range ( ) ;
21- cov_mark:: hit!( type_mismatch_on_block) ;
22- Some ( r_curly_range)
23- } ,
24- ) ;
25-
17+ let display_range = match & d. expr_or_pat {
18+ Either :: Left ( expr) => adjusted_display_range :: < ast:: BlockExpr > (
19+ ctx,
20+ expr. clone ( ) . map ( |it| it. into ( ) ) ,
21+ & |block| {
22+ let r_curly_range = block. stmt_list ( ) ?. r_curly_token ( ) ?. text_range ( ) ;
23+ cov_mark:: hit!( type_mismatch_on_block) ;
24+ Some ( r_curly_range)
25+ } ,
26+ ) ,
27+ Either :: Right ( pat) => {
28+ ctx. sema . diagnostics_display_range ( pat. clone ( ) . map ( |it| it. into ( ) ) ) . range
29+ }
30+ } ;
2631 let mut diag = Diagnostic :: new (
2732 "type-mismatch" ,
2833 format ! (
@@ -42,24 +47,38 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
4247fn fixes ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: TypeMismatch ) -> Option < Vec < Assist > > {
4348 let mut fixes = Vec :: new ( ) ;
4449
45- add_reference ( ctx, d, & mut fixes) ;
46- add_missing_ok_or_some ( ctx, d, & mut fixes) ;
47- remove_semicolon ( ctx, d, & mut fixes) ;
48- str_ref_to_owned ( ctx, d, & mut fixes) ;
50+ match & d. expr_or_pat {
51+ Either :: Left ( expr_ptr) => {
52+ add_reference ( ctx, d, expr_ptr, & mut fixes) ;
53+ add_missing_ok_or_some ( ctx, d, expr_ptr, & mut fixes) ;
54+ remove_semicolon ( ctx, d, expr_ptr, & mut fixes) ;
55+ str_ref_to_owned ( ctx, d, expr_ptr, & mut fixes) ;
56+ }
57+ Either :: Right ( _pat_ptr) => ( ) ,
58+ }
4959
5060 if fixes. is_empty ( ) {
5161 None
5262 } else {
5363 Some ( fixes)
5464 }
5565}
66+ fn add_reference_pat (
67+ ctx : & DiagnosticsContext < ' _ > ,
68+ d : & hir:: TypeMismatch ,
69+ expr_ptr : & InFile < AstPtr < ast:: Pat > > ,
70+ acc : & mut Vec < Assist > ,
71+ ) -> Option < ( ) > {
72+ None
73+ }
5674
5775fn add_reference (
5876 ctx : & DiagnosticsContext < ' _ > ,
5977 d : & hir:: TypeMismatch ,
78+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
6079 acc : & mut Vec < Assist > ,
6180) -> Option < ( ) > {
62- let range = ctx. sema . diagnostics_display_range ( d . expr . clone ( ) . map ( |it| it. into ( ) ) ) . range ;
81+ let range = ctx. sema . diagnostics_display_range ( expr_ptr . clone ( ) . map ( |it| it. into ( ) ) ) . range ;
6382
6483 let ( _, mutability) = d. expected . as_reference ( ) ?;
6584 let actual_with_ref = Type :: reference ( & d. actual , mutability) ;
@@ -71,18 +90,19 @@ fn add_reference(
7190
7291 let edit = TextEdit :: insert ( range. start ( ) , ampersands) ;
7392 let source_change =
74- SourceChange :: from_text_edit ( d . expr . file_id . original_file ( ctx. sema . db ) , edit) ;
93+ SourceChange :: from_text_edit ( expr_ptr . file_id . original_file ( ctx. sema . db ) , edit) ;
7594 acc. push ( fix ( "add_reference_here" , "Add reference here" , source_change, range) ) ;
7695 Some ( ( ) )
7796}
7897
7998fn add_missing_ok_or_some (
8099 ctx : & DiagnosticsContext < ' _ > ,
81100 d : & hir:: TypeMismatch ,
101+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
82102 acc : & mut Vec < Assist > ,
83103) -> Option < ( ) > {
84- let root = ctx. sema . db . parse_or_expand ( d . expr . file_id ) ?;
85- let expr = d . expr . value . to_node ( & root) ;
104+ let root = ctx. sema . db . parse_or_expand ( expr_ptr . file_id ) ?;
105+ let expr = expr_ptr . value . to_node ( & root) ;
86106 let expr_range = expr. syntax ( ) . text_range ( ) ;
87107 let scope = ctx. sema . scope ( expr. syntax ( ) ) ?;
88108
@@ -109,7 +129,7 @@ fn add_missing_ok_or_some(
109129 builder. insert ( expr. syntax ( ) . text_range ( ) . start ( ) , format ! ( "{variant_name}(" ) ) ;
110130 builder. insert ( expr. syntax ( ) . text_range ( ) . end ( ) , ")" . to_string ( ) ) ;
111131 let source_change =
112- SourceChange :: from_text_edit ( d . expr . file_id . original_file ( ctx. sema . db ) , builder. finish ( ) ) ;
132+ SourceChange :: from_text_edit ( expr_ptr . file_id . original_file ( ctx. sema . db ) , builder. finish ( ) ) ;
113133 let name = format ! ( "Wrap in {variant_name}" ) ;
114134 acc. push ( fix ( "wrap_in_constructor" , & name, source_change, expr_range) ) ;
115135 Some ( ( ) )
@@ -118,10 +138,11 @@ fn add_missing_ok_or_some(
118138fn remove_semicolon (
119139 ctx : & DiagnosticsContext < ' _ > ,
120140 d : & hir:: TypeMismatch ,
141+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
121142 acc : & mut Vec < Assist > ,
122143) -> Option < ( ) > {
123- let root = ctx. sema . db . parse_or_expand ( d . expr . file_id ) ?;
124- let expr = d . expr . value . to_node ( & root) ;
144+ let root = ctx. sema . db . parse_or_expand ( expr_ptr . file_id ) ?;
145+ let expr = expr_ptr . value . to_node ( & root) ;
125146 if !d. actual . is_unit ( ) {
126147 return None ;
127148 }
@@ -136,7 +157,7 @@ fn remove_semicolon(
136157
137158 let edit = TextEdit :: delete ( semicolon_range) ;
138159 let source_change =
139- SourceChange :: from_text_edit ( d . expr . file_id . original_file ( ctx. sema . db ) , edit) ;
160+ SourceChange :: from_text_edit ( expr_ptr . file_id . original_file ( ctx. sema . db ) , edit) ;
140161
141162 acc. push ( fix ( "remove_semicolon" , "Remove this semicolon" , source_change, semicolon_range) ) ;
142163 Some ( ( ) )
@@ -145,24 +166,26 @@ fn remove_semicolon(
145166fn str_ref_to_owned (
146167 ctx : & DiagnosticsContext < ' _ > ,
147168 d : & hir:: TypeMismatch ,
169+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
148170 acc : & mut Vec < Assist > ,
149171) -> Option < ( ) > {
150172 let expected = d. expected . display ( ctx. sema . db ) ;
151173 let actual = d. actual . display ( ctx. sema . db ) ;
152174
175+ // FIXME do this properly
153176 if expected. to_string ( ) != "String" || actual. to_string ( ) != "&str" {
154177 return None ;
155178 }
156179
157- let root = ctx. sema . db . parse_or_expand ( d . expr . file_id ) ?;
158- let expr = d . expr . value . to_node ( & root) ;
180+ let root = ctx. sema . db . parse_or_expand ( expr_ptr . file_id ) ?;
181+ let expr = expr_ptr . value . to_node ( & root) ;
159182 let expr_range = expr. syntax ( ) . text_range ( ) ;
160183
161184 let to_owned = format ! ( ".to_owned()" ) ;
162185
163186 let edit = TextEdit :: insert ( expr. syntax ( ) . text_range ( ) . end ( ) , to_owned) ;
164187 let source_change =
165- SourceChange :: from_text_edit ( d . expr . file_id . original_file ( ctx. sema . db ) , edit) ;
188+ SourceChange :: from_text_edit ( expr_ptr . file_id . original_file ( ctx. sema . db ) , edit) ;
166189 acc. push ( fix ( "str_ref_to_owned" , "Add .to_owned() here" , source_change, expr_range) ) ;
167190
168191 Some ( ( ) )
@@ -592,6 +615,24 @@ fn f() -> i32 {
592615 let _ = x + y;
593616 }
594617//^ error: expected i32, found ()
618+ "# ,
619+ ) ;
620+ }
621+
622+ #[ test]
623+ fn type_mismatch_pat_smoke_test ( ) {
624+ check_diagnostics (
625+ r#"
626+ fn f() {
627+ let &() = &mut ();
628+ //^^^ error: expected &mut (), found &()
629+ match &() {
630+ &9 => ()
631+ //^^ error: expected &(), found &i32
632+ //^ error: expected (), found i32
633+ //^ error: expected (), found i32
634+ }
635+ }
595636"# ,
596637 ) ;
597638 }
0 commit comments