Skip to content

Commit 863d44a

Browse files
VLanvinfacebook-github-bot
authored andcommitted
Better type mismatch analysis
Summary: Changes are located in the new module `TypeMismatch.scala`, as well as in `Show.scala` and `TcDiagnostics.scala` ## Finding mismatches The main logic is entirely contained in `TypeMismatch.scala`. This module operates in two steps: it first finds a mismatch between two types (`findMismatch`) and then formats it (`explainMismatch`). A mismatch is essentially a path in the two types, where the final step is a basic type incompatibility. Because the two types do not necessarily have the same form (because of, e.g., unions), `findMismatch` rebuilds the two types to only keep the incompatibility, building the mismatch path along the way. `explainMismatch` then uses the reconstructed types as well as the mismatch path to provide an explanation, inlining it in the unfolding of the expression's type. ## Scoring `findMismatch` is also based on a basic scoring system: it assigns a matching score (0-100) to the pair of arguments it is given. This system is fairly simple for now, and used only for choosing union candidates (or concluding there is no candidate). In the future, we can build upon it to provide better info to the user, especially with maps and tuples. ## Displaying For now, the new error message and the old error message are displayed together, while we gather user feedback. Reviewed By: ilya-klyuchnikov Differential Revision: D69245458 fbshipit-source-id: aa0f022791a7efd76d0c27739de62544de372de5
1 parent bfd3f5f commit 863d44a

File tree

70 files changed

+3297
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3297
-89
lines changed

crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type.pretty

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types
22
┌─ check/src/any_fun_type.erl:46:21
33
44
46 │ to_f_any_neg2(F) -> F.
5-
│ ^ F.
5+
│ ^
6+
│ │
7+
│ F.
68
Expression has type: 'f0' | fun((atom()) -> pid()) | 'f1'
79
Context expected type: fun()
10+
11+
12+
Because in the expression's type:
13+
Here the type is: 'f0'
14+
Context expects type: fun()
815

916
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
1017
┌─ check/src/any_fun_type.erl:49:23
@@ -25,6 +32,12 @@ Expression has type: fun((term()) -> term())
2532
Context expected type: f2()
2633
2734

35+
Because in the expression's type:
36+
Here the type is: fun((term()) -> term())
37+
Context expects type: fun((term(), term()) -> term())
38+
39+
------------------------------ Detailed message ------------------------------
40+
2841
fun((term()) -> term()) is not compatible with f2()
2942
because
3043
fun((term()) -> term()) is not compatible with fun((term(), term()) -> term())
@@ -40,6 +53,14 @@ Expression has type: f5('a' | 'b')
4053
Context expected type: f4('a')
4154
4255

56+
Because in the expression's type:
57+
fun((term()) ->
58+
Here the type is: 'b'
59+
Context expects type: 'a'
60+
)
61+
62+
------------------------------ Detailed message ------------------------------
63+
4364
f5('a' | 'b') is not compatible with f4('a')
4465
because
4566
fun((term()) -> 'a' | 'b') is not compatible with f4('a')
@@ -67,6 +88,14 @@ Expression has type: fun((term()) -> 'a' | 'b')
6788
Context expected type: f4('a')
6889
6990

91+
Because in the expression's type:
92+
fun((term()) ->
93+
Here the type is: 'b'
94+
Context expects type: 'a'
95+
)
96+
97+
------------------------------ Detailed message ------------------------------
98+
7099
fun((term()) -> 'a' | 'b') is not compatible with f4('a')
71100
because
72101
fun((term()) -> 'a' | 'b') is not compatible with fun((...) -> 'a')

crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ Expression has type: string() | dynamic()
5555
Context expected type: 'anything'
5656
5757

58+
Because in the expression's type:
59+
Here the type is: string()
60+
Context expects type: 'anything'
61+
62+
------------------------------ Detailed message ------------------------------
63+
5864
string() | dynamic() is not compatible with 'anything'
5965
because
6066
string() is not compatible with 'anything'

crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ Expression has type: 'false' | 'true'
1717
Context expected type: 'true'
1818
1919

20+
Because in the expression's type:
21+
Here the type is: 'false'
22+
Context expects type: 'true'
23+
24+
------------------------------ Detailed message ------------------------------
25+
2026
'false' | 'true' is not compatible with 'true'
2127
because
2228
'false' is not compatible with 'true'

crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ Expression has type: {'p', none() | pid() | reference()}
1717
Context expected type: {'a', atom()} | {'p', pid()}
1818
1919

20+
Because in the expression's type:
21+
{ 'p',
22+
Here the type is: reference()
23+
Context expects type: pid()
24+
}
25+
26+
------------------------------ Detailed message ------------------------------
27+
2028
{'p', none() | pid() | reference()} is not compatible with {'a', atom()} | {'p', pid()}
2129
because
2230
at tuple index 2:
@@ -37,6 +45,13 @@ Expression has type: 'undefined' | none() | 'restarting'
3745
Context expected type: {'p', pid()} | 'undefined'
3846
3947

48+
Because in the expression's type:
49+
Here the type is: 'restarting'
50+
Context expects type: {'p', pid()} | 'undefined'
51+
No candidate matches in the expected union.
52+
53+
------------------------------ Detailed message ------------------------------
54+
4055
'undefined' | none() | 'restarting' is not compatible with {'p', pid()} | 'undefined'
4156
because
4257
'restarting' is not compatible with {'p', pid()} | 'undefined'
@@ -54,6 +69,12 @@ Expression has type: #{dynamic() => dynamic()} | none()
5469
Context expected type: [T]
5570
5671

72+
Because in the expression's type:
73+
Here the type is: #{dynamic() => dynamic()}
74+
Context expects type: [T]
75+
76+
------------------------------ Detailed message ------------------------------
77+
5778
#{dynamic() => dynamic()} | none() is not compatible with [T]
5879
because
5980
#{dynamic() => dynamic()} is not compatible with [T]

crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ Expression has type: #{a => 'b', c => 'd'}
99
Context expected type: #{a => 'b'}
1010
1111

12+
Because in the expression's type:
13+
Here the type is: #{c => ...}
14+
Context expects type: #{...}
15+
The expected map has no corresponding key for: c.
16+
17+
------------------------------ Detailed message ------------------------------
18+
1219
key `c` is declared in the former but not in the latter and the latter map has no default association
1320

1421
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
@@ -22,6 +29,14 @@ Expression has type: #{a => 'b', c => 'd'}
2229
Context expected type: #{a => 'b', atom() => number()}
2330
2431

32+
Because in the expression's type:
33+
#{ c =>
34+
Here the type is: 'd'
35+
Context expects type: number()
36+
, ... }
37+
38+
------------------------------ Detailed message ------------------------------
39+
2540
#{a => 'b', c => 'd'} is not compatible with #{a => 'b', atom() => number()}
2641
because
2742
#{a => 'b', c => 'd'} is not compatible with #{a => 'b', atom() => number()}
@@ -40,6 +55,13 @@ Expression has type: #{a => 'b', atom() => atom()}
4055
Context expected type: #{a => 'b', c => 'd'}
4156
4257

58+
Because in the expression's type:
59+
Here the type is: #{atom() => atom()}
60+
Context expects type: #{...} (no default association)
61+
The expected map has no default association while the type of the expression has one.
62+
63+
------------------------------ Detailed message ------------------------------
64+
4365
#{a => 'b', atom() => atom()} is not compatible with #{a => 'b', c => 'd'}
4466
key c is not present in the former map but is incompatible with its default association
4567
because
@@ -56,6 +78,15 @@ Expression has type: #{a => 'b', atom() => number()}
5678
Context expected type: #{a => 'b', c => 'd', atom() => number()}
5779
5880

81+
Because in the expression's type:
82+
#{ atom() =>
83+
Here the type is: number()
84+
Context expects type: 'd'
85+
, ... }
86+
The context introduces a new association c => 'd' which is incompatible with the expression's default association.
87+
88+
------------------------------ Detailed message ------------------------------
89+
5990
#{a => 'b', atom() => number()} is not compatible with #{a => 'b', c => 'd', atom() => number()}
6091
key c is not present in the former map but is incompatible with its default association
6192
because
@@ -72,6 +103,13 @@ Expression has type: #{a => 'b', atom() => atom()}
72103
Context expected type: #{a => 'b'}
73104
74105

106+
Because in the expression's type:
107+
Here the type is: #{atom() => atom()}
108+
Context expects type: #{...} (no default association)
109+
The expected map has no default association while the type of the expression has one.
110+
111+
------------------------------ Detailed message ------------------------------
112+
75113
#{a => 'b', atom() => atom()} is not compatible with #{a => 'b'}
76114
because
77115
#{a => 'b', atom() => atom()} is not compatible with #{a => 'b'}
@@ -88,6 +126,13 @@ Expression has type: #{a => 'b', dynamic(atom()) => atom()}
88126
Context expected type: #{a => 'b'}
89127
90128

129+
Because in the expression's type:
130+
Here the type is: #{dynamic(atom()) => atom()}
131+
Context expects type: #{...} (no default association)
132+
The expected map has no default association while the type of the expression has one.
133+
134+
------------------------------ Detailed message ------------------------------
135+
91136
#{a => 'b', dynamic(atom()) => atom()} is not compatible with #{a => 'b'}
92137
because
93138
#{a => 'b', dynamic(atom()) => atom()} is not compatible with #{a => 'b'}
@@ -104,6 +149,13 @@ Expression has type: #{a => 'b', atom() => dynamic(atom())}
104149
Context expected type: #{a => 'b'}
105150
106151

152+
Because in the expression's type:
153+
Here the type is: #{atom() => dynamic(atom())}
154+
Context expects type: #{...} (no default association)
155+
The expected map has no default association while the type of the expression has one.
156+
157+
------------------------------ Detailed message ------------------------------
158+
107159
#{a => 'b', atom() => dynamic(atom())} is not compatible with #{a => 'b'}
108160
because
109161
#{a => 'b', atom() => dynamic(atom())} is not compatible with #{a => 'b'}
@@ -160,6 +212,14 @@ Expression has type: #{atom() => binary()}
160212
Context expected type: #{a => binary(), atom() => atom()}
161213
162214

215+
Because in the expression's type:
216+
#{ atom() =>
217+
Here the type is: binary()
218+
Context expects type: atom()
219+
, ... }
220+
221+
------------------------------ Detailed message ------------------------------
222+
163223
#{atom() => binary()} is not compatible with #{a => binary(), atom() => atom()}
164224
the default associations are not compatible
165225
because
@@ -176,6 +236,14 @@ Expression has type: #{a := 'b', atom() => binary()}
176236
Context expected type: #{a => 'b', {c, d} => atom() | binary(), term() => atom()}
177237
178238

239+
Because in the expression's type:
240+
#{ atom() =>
241+
Here the type is: binary()
242+
Context expects type: atom()
243+
, ... }
244+
245+
------------------------------ Detailed message ------------------------------
246+
179247
#{a := 'b', atom() => binary()} is not compatible with #{a => 'b', {c, d} => atom() | binary(), term() => atom()}
180248
the default associations are not compatible
181249
because
@@ -192,6 +260,14 @@ Expression has type: #{a := 'b', atom() => binary()}
192260
Context expected type: #{a => 'b', {c, d} => atom() | binary(), atom() => atom()}
193261
194262

263+
Because in the expression's type:
264+
#{ atom() =>
265+
Here the type is: binary()
266+
Context expects type: atom()
267+
, ... }
268+
269+
------------------------------ Detailed message ------------------------------
270+
195271
#{a := 'b', atom() => binary()} is not compatible with #{a => 'b', {c, d} => atom() | binary(), atom() => atom()}
196272
the default associations are not compatible
197273
because
@@ -208,6 +284,14 @@ Expression has type: #{dynamic() => atom()}
208284
Context expected type: #{dynamic() => binary()}
209285
210286

287+
Because in the expression's type:
288+
#{ dynamic() =>
289+
Here the type is: atom()
290+
Context expects type: binary()
291+
, ... }
292+
293+
------------------------------ Detailed message ------------------------------
294+
211295
#{dynamic() => atom()} is not compatible with #{dynamic() => binary()}
212296
the default associations are not compatible
213297
because

crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ Expression has type: [number()]
99
Context expected type: [atom()]
1010
1111

12+
Because in the expression's type:
13+
[
14+
Here the type is: number()
15+
Context expects type: atom()
16+
]
17+
18+
------------------------------ Detailed message ------------------------------
19+
1220
[number()] is not compatible with [atom()]
1321
because
1422
number() is not compatible with atom()
@@ -56,6 +64,14 @@ Expression has type: [number()]
5664
Context expected type: [binary()]
5765
5866

67+
Because in the expression's type:
68+
[
69+
Here the type is: number()
70+
Context expects type: binary()
71+
]
72+
73+
------------------------------ Detailed message ------------------------------
74+
5975
[number()] is not compatible with [binary()]
6076
because
6177
number() is not compatible with binary()
@@ -152,6 +168,14 @@ Expression has type: [atom()]
152168
Context expected type: [binary()]
153169
154170

171+
Because in the expression's type:
172+
[
173+
Here the type is: atom()
174+
Context expects type: binary()
175+
]
176+
177+
------------------------------ Detailed message ------------------------------
178+
155179
[atom()] is not compatible with [binary()]
156180
because
157181
atom() is not compatible with binary()
@@ -167,6 +191,14 @@ Expression has type: ['true']
167191
Context expected type: ['false']
168192
169193

194+
Because in the expression's type:
195+
[
196+
Here the type is: 'true'
197+
Context expects type: 'false'
198+
]
199+
200+
------------------------------ Detailed message ------------------------------
201+
170202
['true'] is not compatible with ['false']
171203
because
172204
'true' is not compatible with 'false'
@@ -182,6 +214,14 @@ Expression has type: ['true']
182214
Context expected type: ['false']
183215
184216

217+
Because in the expression's type:
218+
[
219+
Here the type is: 'true'
220+
Context expects type: 'false'
221+
]
222+
223+
------------------------------ Detailed message ------------------------------
224+
185225
['true'] is not compatible with ['false']
186226
because
187227
'true' is not compatible with 'false'
@@ -197,6 +237,14 @@ Expression has type: [number()]
197237
Context expected type: [atom()]
198238
199239

240+
Because in the expression's type:
241+
[
242+
Here the type is: number()
243+
Context expects type: atom()
244+
]
245+
246+
------------------------------ Detailed message ------------------------------
247+
200248
[number()] is not compatible with [atom()]
201249
because
202250
number() is not compatible with atom()
@@ -212,6 +260,14 @@ Expression has type: [binary() | 'undefined']
212260
Context expected type: [binary()]
213261
214262

263+
Because in the expression's type:
264+
[
265+
Here the type is: 'undefined'
266+
Context expects type: binary()
267+
]
268+
269+
------------------------------ Detailed message ------------------------------
270+
215271
[binary() | 'undefined'] is not compatible with [binary()]
216272
because
217273
binary() | 'undefined' is not compatible with binary()

0 commit comments

Comments
 (0)