@@ -1,10 +1,9 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Type where
33
4−import qualified Data.Char as Char
5−import qualified Data.List as List
4+import qualified Data.Maybe as Maybe
5+import Text.PrettyPrint.ANSI.Leijen (Doc, (<+>), equals, indent, text)
66
7−import qualified AST.Helpers as AstHelp
87import qualified AST.Type as Type
98import qualified AST.Variable as Var
109import qualified Reporting.Error.Helpers as Help
@@ -85,62 +84,52 @@ toReport localizer err =
8584 Mismatch info ->
8685 mismatchToReport localizer info
8786
88− InfiniteType name var tipe ->
89− let
90− prettyVar =
91− error "TODO P.pretty localizer False var"
87+ InfiniteType name overallType infiniteType ->
88+ infiniteTypeToReport localizer name overallType infiniteType
9289
93− prettyType =
94− error "TODO P.pretty localizer False tipe"
95− in
96− Report.simple "INFINITE TYPE"
97− ( "I am inferring weird self-referential type for `" ++ name ++ "`"
98− )
99− ( "TODO - do a better job\n\n"
100− ++ "The bit of the type that is self-referential looks like this:\n\n"
101− ++ error "TODO P.render (P.nest 4 (prettyVar <+> P.equals <+> prettyType))"
102− ++ "\n\nThe cause is often that the usage of `" ++ name ++ "` is flipped around.\n\n"
103− ++ "Maybe you are inserting a data structure into an element? Maybe you are giving\n"
104− ++ "a function to an argument? Either way, something is probably backwards!\n\n"
105− ++ "Try breaking the code related to `" ++ name ++ "` into smaller pieces.\n"
106− ++ "Give each piece a name and try to write down its type."
90+ BadMain tipe ->
91+ Report.report
92+ "BAD MAIN TYPE"
93+ Nothing
94+ "The 'main' value has an unsupported type."
95+ ( Help.stack
96+ [ Help.reflowParagraph $
97+ "I need an Element, Html, (Signal Element), or (Signal Html) so I can render it\
98+ \ on screen, but you gave me:"
99+ , indent 4 (RenderType.toDoc localizer tipe)
100+ ]
107101 )
108102
109− BadMain tipe ->
110− Report.simple "BAD MAIN TYPE" "The 'main' value has an unsupported type." $
111− "I need an Element, Html, (Signal Element), or (Signal Html) so I can render it\n"
112− ++ "on screen, but you gave me:\n\n"
113− ++ RenderType.tipe localizer tipe
103+
104+
105+-- TYPE MISMATCHES
114106
115107
116108mismatchToReport :: RenderType.Localizer -> Mismatch -> Report.Report
117109mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
118110 let
119111 report =
120− Report.Report "TYPE MISMATCH"
112+ Report.report "TYPE MISMATCH"
113+
114+ reasonHints =
115+ Maybe.maybeToList (fmap reasonToString maybeReason)
121116
122− pretty tipe =
123− "\n\n"
124− ++ RenderType.tipe localizer tipe
125− ++ "\n\n"
117+ cmpHint leftWords rightWords extraHints =
118+ comparisonHint localizer leftType rightType leftWords rightWords (extraHints ++ reasonHints)
126119 in
127120 case hint of
128121 CaseBranch branchNumber region ->
129122 report
130123 (Just region)
131− ( "The " ++ ordinalize (branchNumber -1) ++ " and " ++ ordinalize branchNumber
132− ++ " branches of this `case` result in different types of values."
124+ ( "The " ++ ordinalPair branchNumber
125+ ++ " branches of this `case` produce different types of values."
133126 )
134− ( "The " ++ ordinalize (branchNumber -1) ++ " branch results in this type of value:\n"
135−
136− ++ pretty leftType
137−
138− ++ "And the " ++ ordinalize branchNumber ++ " branch results in this type:\n"
139−
140− ++ pretty rightType
141−
142− ++ "These branches should match so that no matter which one we take, we always get\n"
143− ++ "back the same type of value."
127+ ( cmpHint
128+ ("The " ++ Help.ordinalize (branchNumber -1) ++ " branch has this type:")
129+ ("But the " ++ Help.ordinalize branchNumber ++ " is:")
130+ [ "All branches in a `case` must have the same type. So no matter\
131+ \ which one we take, we always get back the same type of value."
132+ ]
144133 )
145134
146135 Case ->
@@ -155,72 +144,62 @@ mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
155144 IfCondition ->
156145 report
157146 Nothing
158− ( "This condition does not evaluate to a boolean value, True or False."
159− )
160− ( "Instead of Bool, it is resulting in:\n"
161−
162− ++ pretty rightType
163−
164− ++ "Note: Elm does not have \"truthiness\" such that ints and strings and lists\n"
165− ++ "are automatically converted to booleans. Do that conversion explicitly."
147+ "This condition does not evaluate to a boolean value, True or False."
148+ ( cmpHint
149+ "You have given me an condition with this type:"
150+ "But I need it to be:"
151+ [ "Elm does not have \"truthiness\" such that ints and strings and lists\
152+ \ are automatically converted to booleans. Do that conversion explicitly."
153+ ]
166154 )
167155
168156 IfBranches ->
169157 report
170158 Nothing
171− ( "The branches of this `if` result in different types of values."
172− )
173− ( "The `then` branch results in:\n"
174−
175− ++ pretty leftType
176−
177− ++ "And the `else` branch results in:\n"
178−
179− ++ pretty rightType
180−
181− ++ "These need to match so that no matter which one we take, we always get\n"
182− ++ "back the same type of value."
159+ "The branches of this `if` produce different types of values."
160+ ( cmpHint
161+ "The `then` branch has type:"
162+ "But the `else` branch is:"
163+ [ "These need to match so that no matter which branch we take, we\
164+ \ always get back the same type of value."
165+ ]
183166 )
184167
185168 MultiIfBranch branchNumber region ->
186169 report
187170 (Just region)
188− ( "The " ++ ordinalize branchNumber ++ " branch of this `if` results in an unexpected type of value."
171+ ( "The " ++ ordinalPair branchNumber
172+ ++ " branches of this `if` produce different types of values."
189173 )
190− ( "The " ++ ordinalize (branchNumber - 1) ++ " branch results in:\n"
191−
192− ++ pretty leftType
193−
194− ++ "And the "++ ordinalize branchNumber ++ " branch results in:\n"
195−
196− ++ pretty rightType
197−
198− ++ "All branches should match so that no matter which one we take, we always get\n"
199− ++ "back the same type of value."
174+ ( cmpHint
175+ ("The " ++ Help.ordinalize (branchNumber - 1) ++ " branch has this type:")
176+ ("But the "++ Help.ordinalize branchNumber ++ " is:")
177+ [ "All the branches of an `if` need to match so that no matter which\
178+ \ one we take, we get back the same type of value overall."
179+ ]
200180 )
201181
202182 If ->
203183 report
204184 Nothing
205− ( "All the branches of this if-expression are consistent, but the overall\n"
206− ++ "type does not match how it is used elsewhere."
207− )
208− ( "The `if` evaluates to something of type:\n"
209−
210− ++ pretty leftType
211−
212− ++ "Which is fine, but the surrounding context wants it to be:\n"
213−
214− ++ pretty rightType
215−
185+ "All the branches of this `if` are consistent, but the overall\
186+ \ type does not match how it is used elsewhere."
187+ ( cmpHint
188+ "The `if` evaluates to something of type:"
189+ "Which is fine, but the surrounding context wants it to be:"
190+ []
216191 )
217192
218193 ListElement elementNumber region ->
219194 report
220195 (Just region)
221− ( "The " ++ ordinalize elementNumber ++ " element of this list is an unexpected type of value."
222− )
223− ( "All elements should be the same type of value so that we can iterate over the\nlist without running into unexpected values."
196+ ("The " ++ ordinalPair elementNumber ++ " elements are different types of values.")
197+ ( cmpHint
198+ ("The " ++ Help.ordinalize (elementNumber - 1) ++ " element has this type:")
199+ ("But the "++ Help.ordinalize elementNumber ++ " is:")
200+ [ "All elements should be the same type of value so that we can\
201+ \ iterate through the list without running into unexpected values."
202+ ]
224203 )
225204
226205 List ->
@@ -229,56 +208,43 @@ mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
229208 ( "All the elements in this list are the same type, but the overall\n"
230209 ++ "type does not match how it is used elsewhere."
231210 )
232− ( "The list has type:\n"
233−
234− ++ pretty leftType
235−
236− ++ "Which is fine, but the surrounding context wants it to be:\n"
237−
238− ++ pretty rightType
211+ ( cmpHint
212+ "The list has type:"
213+ "Which is fine, but the surrounding context wants it to be:"
214+ []
239215 )
240216
241217 BinopLeft op region ->
242218 report
243219 (Just region)
244− ("The left argument of " ++ prettyOperator op ++ " is causing a type mismatch.")
245− ( prettyOperator op ++ " is expecting:"
246−
247− ++ pretty leftType
248−
249− ++ "But the value on the left has this type:"
250−
251− ++ pretty rightType
220+ ("The left argument of " ++ prettyName op ++ " is causing a type mismatch.")
221+ ( cmpHint
222+ (prettyName op ++ " is expecting the left argument to be a:")
223+ "But the left argument is:"
224+ []
252225 )
253226
254227 BinopRight op region ->
255228 report
256229 (Just region)
257− ( "The right argument of " ++ prettyOperator op ++ " is causing a type mismatch."
258− )
259− ( prettyOperator op ++ " is expecting:"
260−
261− ++ pretty leftType
262−
263− ++ "But the value on the right has this type:"
264−
265− ++ pretty rightType
230+ ("The right argument of " ++ prettyName op ++ " is causing a type mismatch.")
231+ ( cmpHint
232+ (prettyName op ++ " is expecting the right argument to be a:")
233+ "But the right argument is:"
234+ []
266235 )
267236
268237 Binop op ->
269238 report
270239 Nothing
271− ( "The two arguments to " ++ prettyOperator op ++ " are fine, but the overall type of this expression\n"
272− ++ "does not match how it is used elsewhere."
240+ ( "The two arguments to " ++ prettyName op ++
241+ " are fine, but the overall type of this expression\
242+ \ does not match how it is used elsewhere."
273243 )
274− ( "The result of this binary operation is:\n"
275−
276− ++ pretty leftType
277−
278− ++ "Which is fine, but the surrounding context wants it to be:\n"
279−
280− ++ pretty rightType
281−
244+ ( cmpHint
245+ "The result of this binary operation is:"
246+ "Which is fine, but the surrounding context wants it to be:"
247+ []
282248 )
283249
284250 Function maybeName ->
@@ -286,30 +252,24 @@ mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
286252 Nothing
287253 ( "The return type of " ++ funcName maybeName ++ " is being used in unexpected ways."
288254 )
289− ( "The function results in a value with type:\n"
290−
291− ++ pretty leftType
292−
293− ++ "Which is fine, but the surrounding context wants it to be:\n"
294−
295− ++ pretty rightType
255+ ( cmpHint
256+ "The function results in this type of value:"
257+ "Which is fine, but the surrounding context wants it to be:"
258+ []
296259 )
297260
298261 UnexpectedArg maybeName index region ->
299262 report
300263 (Just region)
301− ( "The " ++ ordinalize index ++ " argument to " ++ funcName maybeName
264+ ( "The " ++ Help.ordinalize index ++ " argument to " ++ funcName maybeName
302265 ++ " is causing a mismatch."
303266 )
304− ( "The " ++ ordinalize index ++ " to " ++ funcName maybeName ++ " should be:"
305−
306− ++ pretty leftType
307−
308− ++ "But it is:"
309−
310− ++ pretty rightType
311−
312− ++ maybe "" reasonToString maybeReason
267+ ( cmpHint
268+ ( Help.capitalize (funcName maybeName) ++ " is expecting the "
269+ ++ Help.ordinalize index ++ " argument to be:"
270+ )
271+ "But it is:"
272+ []
313273 )
314274
315275 FunctionArity maybeName expected actual region ->
@@ -318,34 +278,29 @@ mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
318278 in
319279 report
320280 (Just region)
321− ( capitalize (funcName maybeName) ++ " is expecting " ++ show expected
281+ ( Help.capitalize (funcName maybeName) ++ " is expecting " ++ show expected
322282 ++ " argument" ++ s ++ ", but was given " ++ show actual ++ "."
323283 )
324− ( error "TODO are the types interesting here?"
325− )
284+ (text "Maybe you forgot some parentheses somewhere?")
326285
327286 BadTypeAnnotation name ->
328287 report
329288 Nothing
330− ( "The type annotation for `" ++ name ++ "` does not match its definition."
331− )
332− ( "Based on the actual definition, the type should be:\n"
333−
334− ++ pretty rightType
289+ ("The type annotation for " ++ Help.functionName name ++ " does not match its definition.")
290+ ( cmpHint
291+ "The type annotation is saying:"
292+ "But I am inferring that the definition has this type:"
293+ []
335294 )
336295
337296 Instance name ->
338297 report
339298 Nothing
340− ( "This usage of `" ++ name ++ "` is causing a type mismatch."
341− )
342− ( "Based on its definition, `" ++ name ++ "` has this type:"
343−
344− ++ pretty leftType
345−
346− ++ "But you are trying to use it as:"
347−
348− ++ pretty rightType
299+ (Help.functionName name ++ " is being used in an unexpected way.")
300+ ( cmpHint
301+ ("Based on its definition, " ++ Help.functionName name ++ " has this type:")
302+ "But you are trying to use it as:"
303+ []
349304 )
350305
351306 Literal name ->
@@ -353,9 +308,10 @@ mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
353308 Nothing
354309 ( "This " ++ name ++ " value is being used as if it is some other type of value."
355310 )
356− ( "You are trying to use this " ++ name ++ " as this type:"
357−
358− ++ pretty rightType
311+ ( cmpHint
312+ ("The " ++ name ++ " definitely has this type:")
313+ ("But it is being used as:")
314+ []
359315 )
360316
361317 Pattern patErr ->
@@ -364,71 +320,87 @@ mismatchToReport localizer (MismatchInfo hint leftType rightType maybeReason) =
364320 case patErr of
365321 PVar name -> "variable `" ++ name ++ "`"
366322 PAlias name -> "alias `" ++ name ++ "`"
367− PData name -> "`" ++ name ++ "`"
323+ PData name -> "tag `" ++ name ++ "`"
368324 PRecord -> "a record"
369325 in
370326 report
371327 Nothing
372− ( "Problem with " ++ thing ++ " in this pattern match."
328+ ( Help.capitalize thing ++ " is causing problems in this pattern match."
373329 )
374− ( "This pattern matches things of type:"
375−
376− ++ pretty leftType
377−
378− ++ "But the values it will actually be trying to match are:"
379−
380− ++ pretty rightType
330+ ( cmpHint
331+ "This pattern matches things of type:"
332+ "But the values it will actually be trying to match are:"
333+ []
381334 )
382335
383336 Shader ->
384337 report
385338 Nothing
386− ( "There is some problem with this GLSL shader."
387− )
339+ "There is some problem with this GLSL shader."
388340 ( error "TODO generic error"
389341 )
390342
391343 Range ->
392344 report
393345 Nothing
394− ( "The low and high members of this list range are not the same type of value."
395− )
396− ( "The low end of the range has type:"
397−
398− ++ pretty leftType
399−
400− ++ "But the high end is:"
401−
402− ++ pretty rightType
346+ "The low and high members of this list range are not the same type of value."
347+ ( cmpHint
348+ "The low end of the range has type:"
349+ "But the high end is:"
350+ []
403351 )
404352
405353 Lambda ->
406354 report
407355 Nothing
408− ( "This anonymous function is being used in an unexpected way."
409− )
410− ( "The anonymous function has type:"
411−
412− ++ pretty leftType
413−
414− ++ "But you are trying to use it as:"
415−
416− ++ pretty rightType
356+ "This anonymous function is being used in an unexpected way."
357+ ( cmpHint
358+ "The anonymous function has type:"
359+ "But you are trying to use it as:"
360+ []
417361 )
418362
419363 Record ->
420364 report
421365 Nothing
422− ( "This record is being used in an unexpected way."
423− )
424− ( "The record has type:"
366+ "This record is being used in an unexpected way."
367+ ( cmpHint
368+ "The record has type:"
369+ "But you are trying to use it as:"
370+ []
371+ )
372+
373+
374+comparisonHint
375+ :: RenderType.Localizer
376+ -> Type.Canonical
377+ -> Type.Canonical
378+ -> String
379+ -> String
380+ -> [String]
381+ -> Doc
382+comparisonHint localizer leftType rightType leftWords rightWords finalHints =
383+ let
384+ (leftDoc, rightDoc) =
385+ RenderType.diffToDocs localizer RenderType.Elide leftType rightType
386+ in
387+ Help.stack $
388+ [ Help.reflowParagraph leftWords
389+ , indent 4 leftDoc
390+ , Help.reflowParagraph rightWords
391+ , indent 4 rightDoc
392+ ]
393+ ++
394+ map Help.reflowParagraph finalHints
425395
426− ++ pretty leftType
427396
428− ++ "But you are trying to use it as:"
397+ordinalPair :: Int -> String
398+ordinalPair number =
399+ Help.ordinalize (number -1 ) ++ " and " ++ Help.ordinalize number
400+
429401
430− ++ pretty rightType
431− )
402+
403+-- MISMTACH REASONS
432404
433405
434406reasonToString :: Reason -> String
@@ -445,12 +417,12 @@ reasonToString reason =
445417 [] ->
446418 "These records look pretty different."
447419
448− [(exp,act)] ->
449− "Looks like a misspelling between field " ++ exp ++ " and " ++ act ++ "."
420+ [(ex,ac)] ->
421+ "Looks like a misspelling between field " ++ ex ++ " and " ++ ac ++ "."
450422
451423 potentialTypos ->
452424 "There are some potential typos in the record field names.\n"
453− ++ concatMap (\(exp,act) -> "\n " ++ exp ++ " <=> " ++ act) potentialTypos
425+ ++ concatMap (\(ex,ac) -> "\n " ++ ex ++ " <=> " ++ ac) potentialTypos
454426
455427 NotRecord ->
456428 "A record cannot be merged with other types of values."
@@ -467,29 +439,15 @@ similarNames otherKeys key =
467439 map ((,) key) (filter (\key' -> Help.distance key key' == 1) otherKeys)
468440
469441
470−
471442genericMissingFieldsMessage :: String -> [String] -> String
472443genericMissingFieldsMessage latterOrFormer fields =
473− "A record in the " ++ latterOrFormer ++ " type is missing "
474− ++
475− case fields of
476− [key] ->
477− "field `" ++ key ++ "`"
478−
479− [key1,key2] ->
480− "fields " ++ key1 ++ " and " ++ key2
444+ "A record in the " ++ latterOrFormer ++ " type is missing field"
445+ ++ Help.commaSep fields
481446
482− keys ->
483− "fields " ++ List.intercalate ", " (init keys) ++ ", and " ++ last keys
484447
485−
486−prettyOperator :: Var.Canonical -> String
487−prettyOperator (Var.Canonical _ opName) =
488− if AstHelp.isOp opName then
489− "(" ++ opName ++ ")"
490−
491− else
492− "`" ++ opName ++ "`"
448+prettyName :: Var.Canonical -> String
449+prettyName (Var.Canonical _ opName) =
450+ Help.functionName opName
493451
494452
495453funcName :: Maybe Var.Canonical -> String
@@ -499,33 +457,62 @@ funcName maybeVar =
499457 "this function"
500458
501459 Just var ->
502− "function " ++ prettyOperator var
503−
504−
505−capitalize :: String -> String
506−capitalize string =
507− case string of
508− [] -> []
509− c : cs ->
510− Char.toUpper c : cs
511−
512−
513−ordinalize :: Int -> String
514−ordinalize number =
460+ "function " ++ prettyName var
461+
462+
463+
464+-- INFINITE TYPES
465+
466+
467+infiniteTypeToReport
468+ :: RenderType.Localizer
469+ -> String
470+ -> Type.Canonical
471+ -> Type.Canonical
472+ -> Report.Report
473+infiniteTypeToReport localizer name overallType infiniteType =
474+ Report.report
475+ "INFINITE TYPE"
476+ Nothing
477+ ( "I am inferring a weird self-referential type for " ++ Help.functionName name
478+ )
479+ ( Help.stack $
480+ explainInfiniteType localizer name overallType infiniteType
481+ ++
482+ [ text $
483+ "This can be very tricky to figure out, so definitely read the debugging hints:\n"
484+ ++ Help.hintLink "infinite-type"
485+ ]
486+ )
487+
488+
489+explainInfiniteType
490+ :: RenderType.Localizer
491+ -> String
492+ -> Type.Canonical
493+ -> Type.Canonical
494+ -> [Doc]
495+explainInfiniteType localizer name overallType infiniteType =
515496 let
516− remainder10 =
517− number `mod` 10
518−
519− remainder100 =
520− number `mod` 100
521−
522− ending
523− | remainder100 `elem` [11..13] = "th"
524− | remainder10 == 1 = "st"
525− | remainder10 == 2 = "nd"
526− | remainder10 == 3 = "rd"
527− | otherwise = "th"
497+ selfReference =
498+ text "?" <+> equals <+> RenderType.toDoc localizer infiniteType
528499 in
529− show number ++ ending
530−
531−
500+ case overallType of
501+ Type.Var "?" ->
502+ [ Help.reflowParagraph $
503+ "I cannot write down the actual type of " ++ Help.functionName name ++
504+ " because it is infinitely large (that's the problem!) so here it\
505+ \ is as an equation where `?` represents the type:"
506+ , indent 4 selfReference
507+ ]
508+
509+ _ ->
510+ [ Help.reflowParagraph $
511+ "I am inferring that " ++ Help.functionName name ++ " has this type:"
512+ , indent 4 (RenderType.toDoc localizer overallType)
513+ , Help.reflowParagraph $
514+ "The problem is in the `?` which seems to contain itself. I cannot show\
515+ \ you that type directly (because it is infinitely large!) so here it is\
516+ \ as an equation:"
517+ , indent 4 selfReference
518+ ]