1
- use hir:: { db:: AstDatabase , HirDisplay , Type } ;
1
+ use either:: Either ;
2
+ use hir:: { db:: AstDatabase , HirDisplay , InFile , Type } ;
2
3
use ide_db:: { famous_defs:: FamousDefs , source_change:: SourceChange } ;
3
4
use syntax:: {
4
5
ast:: { self , BlockExpr , ExprStmt } ,
5
- AstNode ,
6
+ AstNode , AstPtr ,
6
7
} ;
7
8
use text_edit:: TextEdit ;
8
9
9
10
use crate :: { adjusted_display_range, fix, Assist , Diagnostic , DiagnosticsContext } ;
10
11
11
12
// Diagnostic: type-mismatch
12
13
//
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
14
15
// the expected type.
15
16
pub ( 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
+ } ;
26
31
let mut diag = Diagnostic :: new (
27
32
"type-mismatch" ,
28
33
format ! (
@@ -42,24 +47,38 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
42
47
fn fixes ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: TypeMismatch ) -> Option < Vec < Assist > > {
43
48
let mut fixes = Vec :: new ( ) ;
44
49
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
+ }
49
59
50
60
if fixes. is_empty ( ) {
51
61
None
52
62
} else {
53
63
Some ( fixes)
54
64
}
55
65
}
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
+ }
56
74
57
75
fn add_reference (
58
76
ctx : & DiagnosticsContext < ' _ > ,
59
77
d : & hir:: TypeMismatch ,
78
+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
60
79
acc : & mut Vec < Assist > ,
61
80
) -> 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 ;
63
82
64
83
let ( _, mutability) = d. expected . as_reference ( ) ?;
65
84
let actual_with_ref = Type :: reference ( & d. actual , mutability) ;
@@ -71,18 +90,19 @@ fn add_reference(
71
90
72
91
let edit = TextEdit :: insert ( range. start ( ) , ampersands) ;
73
92
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) ;
75
94
acc. push ( fix ( "add_reference_here" , "Add reference here" , source_change, range) ) ;
76
95
Some ( ( ) )
77
96
}
78
97
79
98
fn add_missing_ok_or_some (
80
99
ctx : & DiagnosticsContext < ' _ > ,
81
100
d : & hir:: TypeMismatch ,
101
+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
82
102
acc : & mut Vec < Assist > ,
83
103
) -> 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) ;
86
106
let expr_range = expr. syntax ( ) . text_range ( ) ;
87
107
let scope = ctx. sema . scope ( expr. syntax ( ) ) ?;
88
108
@@ -109,7 +129,7 @@ fn add_missing_ok_or_some(
109
129
builder. insert ( expr. syntax ( ) . text_range ( ) . start ( ) , format ! ( "{variant_name}(" ) ) ;
110
130
builder. insert ( expr. syntax ( ) . text_range ( ) . end ( ) , ")" . to_string ( ) ) ;
111
131
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 ( ) ) ;
113
133
let name = format ! ( "Wrap in {variant_name}" ) ;
114
134
acc. push ( fix ( "wrap_in_constructor" , & name, source_change, expr_range) ) ;
115
135
Some ( ( ) )
@@ -118,10 +138,11 @@ fn add_missing_ok_or_some(
118
138
fn remove_semicolon (
119
139
ctx : & DiagnosticsContext < ' _ > ,
120
140
d : & hir:: TypeMismatch ,
141
+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
121
142
acc : & mut Vec < Assist > ,
122
143
) -> 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) ;
125
146
if !d. actual . is_unit ( ) {
126
147
return None ;
127
148
}
@@ -136,7 +157,7 @@ fn remove_semicolon(
136
157
137
158
let edit = TextEdit :: delete ( semicolon_range) ;
138
159
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) ;
140
161
141
162
acc. push ( fix ( "remove_semicolon" , "Remove this semicolon" , source_change, semicolon_range) ) ;
142
163
Some ( ( ) )
@@ -145,24 +166,26 @@ fn remove_semicolon(
145
166
fn str_ref_to_owned (
146
167
ctx : & DiagnosticsContext < ' _ > ,
147
168
d : & hir:: TypeMismatch ,
169
+ expr_ptr : & InFile < AstPtr < ast:: Expr > > ,
148
170
acc : & mut Vec < Assist > ,
149
171
) -> Option < ( ) > {
150
172
let expected = d. expected . display ( ctx. sema . db ) ;
151
173
let actual = d. actual . display ( ctx. sema . db ) ;
152
174
175
+ // FIXME do this properly
153
176
if expected. to_string ( ) != "String" || actual. to_string ( ) != "&str" {
154
177
return None ;
155
178
}
156
179
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) ;
159
182
let expr_range = expr. syntax ( ) . text_range ( ) ;
160
183
161
184
let to_owned = format ! ( ".to_owned()" ) ;
162
185
163
186
let edit = TextEdit :: insert ( expr. syntax ( ) . text_range ( ) . end ( ) , to_owned) ;
164
187
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) ;
166
189
acc. push ( fix ( "str_ref_to_owned" , "Add .to_owned() here" , source_change, expr_range) ) ;
167
190
168
191
Some ( ( ) )
@@ -592,6 +615,24 @@ fn f() -> i32 {
592
615
let _ = x + y;
593
616
}
594
617
//^ 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
+ }
595
636
"# ,
596
637
) ;
597
638
}
0 commit comments