1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Type
4  ( Error(..)
5  -- expectations
6  , Expected(..)
7  , Context(..)
8  , SubContext(..)
9  , MaybeName(..)
10  , Category(..)
11  , PExpected(..)
12  , PContext(..)
13  , PCategory(..)
14  , typeReplace
15  , ptypeReplace
16  -- make reports
17  , toReport
18  )
19  where
20
21
22import Prelude hiding (round)
23import qualified Data.Map as Map
24import Data.Monoid ((<>))
25import qualified Data.Name as Name
26
27import qualified AST.Canonical as Can
28import qualified Data.Index as Index
29import qualified Reporting.Annotation as A
30import qualified Reporting.Doc as D
31import qualified Reporting.Render.Code as Code
32import qualified Reporting.Render.Type as RT
33import qualified Reporting.Render.Type.Localizer as L
34import qualified Reporting.Report as Report
35import qualified Reporting.Suggest as Suggest
36import qualified Type.Error as T
37
38
39
40-- ERRORS
41
42
43data Error
44  = BadExpr A.Region Category T.Type (Expected T.Type)
45  | BadPattern A.Region PCategory T.Type (PExpected T.Type)
46  | InfiniteType A.Region Name.Name T.Type
47
48
49
50-- EXPRESSION EXPECTATIONS
51
52
53data Expected tipe
54  = NoExpectation tipe
55  | FromContext A.Region Context tipe
56  | FromAnnotation Name.Name Int SubContext tipe
57
58
59data Context
60  = ListEntry Index.ZeroBased
61  | Negate
62  | OpLeft Name.Name
63  | OpRight Name.Name
64  | IfCondition
65  | IfBranch Index.ZeroBased
66  | CaseBranch Index.ZeroBased
67  | CallArity MaybeName Int
68  | CallArg MaybeName Index.ZeroBased
69  | RecordAccess A.Region (Maybe Name.Name) A.Region Name.Name
70  | RecordUpdateKeys Name.Name (Map.Map Name.Name Can.FieldUpdate)
71  | RecordUpdateValue Name.Name
72  | Destructure
73
74
75data SubContext
76  = TypedIfBranch Index.ZeroBased
77  | TypedCaseBranch Index.ZeroBased
78  | TypedBody
79
80
81data MaybeName
82  = FuncName Name.Name
83  | CtorName Name.Name
84  | OpName Name.Name
85  | NoName
86
87
88data Category
89  = List
90  | Number
91  | Float
92  | String
93  | Char
94  | If
95  | Case
96  | CallResult MaybeName
97  | Lambda
98  | Accessor Name.Name
99  | Access Name.Name
100  | Record
101  | Tuple
102  | Unit
103  | Shader
104  | Effects
105  | Local Name.Name
106  | Foreign Name.Name
107
108
109
110-- PATTERN EXPECTATIONS
111
112
113data PExpected tipe
114  = PNoExpectation tipe
115  | PFromContext A.Region PContext tipe
116
117
118data PContext
119  = PTypedArg Name.Name Index.ZeroBased
120  | PCaseMatch Index.ZeroBased
121  | PCtorArg Name.Name Index.ZeroBased
122  | PListEntry Index.ZeroBased
123  | PTail
124
125
126data PCategory
127  = PRecord
128  | PUnit
129  | PTuple
130  | PList
131  | PCtor Name.Name
132  | PInt
133  | PStr
134  | PChr
135  | PBool
136
137
138
139-- HELPERS
140
141
142typeReplace :: Expected a -> b -> Expected b
143typeReplace expectation tipe =
144  case expectation of
145    NoExpectation _ ->
146      NoExpectation tipe
147
148    FromContext region context _ ->
149      FromContext region context tipe
150
151    FromAnnotation name arity context _ ->
152      FromAnnotation name arity context tipe
153
154
155ptypeReplace :: PExpected a -> b -> PExpected b
156ptypeReplace expectation tipe =
157  case expectation of
158    PNoExpectation _ ->
159      PNoExpectation tipe
160
161    PFromContext region context _ ->
162      PFromContext region context tipe
163
164
165
166-- TO REPORT
167
168
169toReport :: Code.Source -> L.Localizer -> Error -> Report.Report
170toReport source localizer err =
171  case err of
172    BadExpr region category actualType expected ->
173      toExprReport source localizer region category actualType expected
174
175    BadPattern region category tipe expected ->
176      toPatternReport source localizer region category tipe expected
177
178    InfiniteType region name overallType ->
179      toInfiniteReport source localizer region name overallType
180
181
182
183-- TO PATTERN REPORT
184
185
186toPatternReport :: Code.Source -> L.Localizer -> A.Region -> PCategory -> T.Type -> PExpected T.Type -> Report.Report
187toPatternReport source localizer patternRegion category tipe expected =
188  Report.Report "TYPE MISMATCH" patternRegion [] $
189  case expected of
190    PNoExpectation expectedType ->
191      Code.toSnippet source patternRegion Nothing $
192        ( "This pattern is being used in an unexpected way:"
193        , patternTypeComparison localizer tipe expectedType
194            (addPatternCategory "It is" category)
195            "But it needs to match:"
196            []
197        )
198
199    PFromContext region context expectedType ->
200      Code.toSnippet source region (Just patternRegion) $
201        case context of
202          PTypedArg name index ->
203            ( D.reflow $
204                "The " <> D.ordinal index <> " argument to `" <> Name.toChars name <> "` is weird."
205            , patternTypeComparison localizer tipe expectedType
206                (addPatternCategory "The argument is a pattern that matches" category)
207                ( "But the type annotation on `" <> Name.toChars name
208                  <> "` says the " <> D.ordinal index <> " argument should be:"
209                )
210                []
211            )
212
213          PCaseMatch index ->
214            if index == Index.first then
215              (
216                D.reflow $
217                  "The 1st pattern in this `case` causing a mismatch:"
218              ,
219                patternTypeComparison localizer tipe expectedType
220                  (addPatternCategory "The first pattern is trying to match" category)
221                  "But the expression between `case` and `of` is:"
222                  [ D.reflow $
223                      "These can never match! Is the pattern the problem? Or is it the expression?"
224                  ]
225              )
226            else
227              ( D.reflow $
228                  "The " <> D.ordinal index <> " pattern in this `case` does not match the previous ones."
229              , patternTypeComparison localizer tipe expectedType
230                  (addPatternCategory ("The " <> D.ordinal index <> " pattern is trying to match") category)
231                  "But all the previous patterns match:"
232                  [ D.link "Note"
233                      "A `case` expression can only handle one type of value, so you may want to use"
234                      "custom-types"
235                      "to handle “mixing” types."
236                  ]
237              )
238
239          PCtorArg name index ->
240            ( D.reflow $
241                "The " <> D.ordinal index <> " argument to `" <> Name.toChars name <> "` is weird."
242            , patternTypeComparison localizer tipe expectedType
243                (addPatternCategory "It is trying to match" category)
244                ( "But `" <> Name.toChars name <> "` needs its "
245                  <> D.ordinal index <> " argument to be:"
246                )
247                []
248            )
249
250          PListEntry index ->
251            ( D.reflow $
252                "The " <> D.ordinal index <> " pattern in this list does not match all the previous ones:"
253            , patternTypeComparison localizer tipe expectedType
254                (addPatternCategory ("The " <> D.ordinal index <> " pattern is trying to match") category)
255                "But all the previous patterns in the list are:"
256                [ D.link "Hint"
257                    "Everything in a list must be the same type of value. This way, we never\
258                    \ run into unexpected values partway through a List.map, List.foldl, etc. Read"
259                    "custom-types"
260                    "to learn how to “mix” types."
261                ]
262            )
263
264          PTail ->
265            ( D.reflow $
266                "The pattern after (::) is causing issues."
267            , patternTypeComparison localizer tipe expectedType
268                (addPatternCategory "The pattern after (::) is trying to match" category)
269                "But it needs to match lists like this:"
270                []
271            )
272
273
274
275-- PATTERN HELPERS
276
277
278patternTypeComparison :: L.Localizer -> T.Type -> T.Type -> String -> String -> [D.Doc] -> D.Doc
279patternTypeComparison localizer actual expected iAmSeeing insteadOf contextHints =
280  let
281    (actualDoc, expectedDoc, problems) =
282      T.toComparison localizer actual expected
283  in
284  D.stack $
285    [ D.reflow iAmSeeing
286    , D.indent 4 actualDoc
287    , D.reflow insteadOf
288    , D.indent 4 expectedDoc
289    ]
290    ++ problemsToHint problems
291    ++ contextHints
292
293
294addPatternCategory :: String -> PCategory -> String
295addPatternCategory iAmTryingToMatch category =
296  iAmTryingToMatch <>
297    case category of
298      PRecord -> " record values of type:"
299      PUnit -> " unit values:"
300      PTuple -> " tuples of type:"
301      PList -> " lists of type:"
302      PCtor name -> " `" <> Name.toChars name <> "` values of type:"
303      PInt -> " integers:"
304      PStr -> " strings:"
305      PChr -> " characters:"
306      PBool -> " booleans:"
307
308
309
310-- EXPR HELPERS
311
312
313typeComparison :: L.Localizer -> T.Type -> T.Type -> String -> String -> [D.Doc] -> D.Doc
314typeComparison localizer actual expected iAmSeeing insteadOf contextHints =
315  let
316    (actualDoc, expectedDoc, problems) =
317      T.toComparison localizer actual expected
318  in
319  D.stack $
320    [ D.reflow iAmSeeing
321    , D.indent 4 actualDoc
322    , D.reflow insteadOf
323    , D.indent 4 expectedDoc
324    ]
325    ++ contextHints
326    ++ problemsToHint problems
327
328
329loneType :: L.Localizer -> T.Type -> T.Type -> D.Doc -> [D.Doc] -> D.Doc
330loneType localizer actual expected iAmSeeing furtherDetails =
331  let
332    (actualDoc, _, problems) =
333      T.toComparison localizer actual expected
334  in
335  D.stack $
336    [ iAmSeeing
337    , D.indent 4 actualDoc
338    ]
339    ++ furtherDetails
340    ++ problemsToHint problems
341
342
343addCategory :: String -> Category -> String
344addCategory thisIs category =
345  case category of
346    Local name -> "This `" <> Name.toChars name <> "` value is a:"
347    Foreign name -> "This `" <> Name.toChars name <> "` value is a:"
348    Access field -> "The value at ." <> Name.toChars field <> " is a:"
349    Accessor field -> "This ." <> Name.toChars field <> " field access function has type:"
350    If -> "This `if` expression produces:"
351    Case -> "This `case` expression produces:"
352    List -> thisIs <> " a list of type:"
353    Number -> thisIs <> " a number of type:"
354    Float -> thisIs <> " a float of type:"
355    String -> thisIs <> " a string of type:"
356    Char -> thisIs <> " a character of type:"
357    Lambda -> thisIs <> " an anonymous function of type:"
358    Record -> thisIs <> " a record of type:"
359    Tuple -> thisIs <> " a tuple of type:"
360    Unit -> thisIs <> " a unit value:"
361    Shader -> thisIs <> " a GLSL shader of type:"
362    Effects -> thisIs <> " a thing for CORE LIBRARIES ONLY."
363    CallResult maybeName ->
364      case maybeName of
365        NoName -> thisIs <> ":"
366        FuncName name -> "This `" <> Name.toChars name <> "` call produces:"
367        CtorName name -> "This `" <> Name.toChars name <> "` call produces:"
368        OpName _ -> thisIs <> ":"
369
370
371problemsToHint :: [T.Problem] -> [D.Doc]
372problemsToHint problems =
373  case problems of
374    [] ->
375      []
376
377    problem : _ ->
378      problemToHint problem
379
380
381problemToHint :: T.Problem -> [D.Doc]
382problemToHint problem =
383  case problem of
384    T.IntFloat ->
385      [ D.fancyLink "Note" ["Read"] "implicit-casts"
386          ["to","learn","why","Elm","does","not","implicitly","convert"
387          ,"Ints","to","Floats.","Use",D.green "toFloat","and"
388          ,D.green "round","to","do","explicit","conversions."
389          ]
390      ]
391
392    T.StringFromInt ->
393      [ D.toFancyHint
394          ["Want","to","convert","an","Int","into","a","String?"
395          ,"Use","the",D.green "String.fromInt","function!"
396          ]
397      ]
398
399    T.StringFromFloat ->
400      [ D.toFancyHint
401          ["Want","to","convert","a","Float","into","a","String?"
402          ,"Use","the",D.green "String.fromFloat","function!"
403          ]
404      ]
405
406    T.StringToInt ->
407      [ D.toFancyHint
408          ["Want","to","convert","a","String","into","an","Int?"
409          ,"Use","the",D.green "String.toInt","function!"
410          ]
411      ]
412
413    T.StringToFloat ->
414      [ D.toFancyHint
415          ["Want","to","convert","a","String","into","a","Float?"
416          ,"Use","the",D.green "String.toFloat","function!"
417          ]
418      ]
419
420    T.AnythingToBool ->
421      [ D.toSimpleHint $
422          "Elm does not have “truthiness” such that ints and strings and lists\
423          \ are automatically converted to booleans. Do that conversion explicitly!"
424      ]
425
426    T.AnythingFromMaybe ->
427      [ D.toFancyHint
428          ["Use",D.green "Maybe.withDefault","to","handle","possible","errors."
429          ,"Longer","term,","it","is","usually","better","to","write","out","the"
430          ,"full","`case`","though!"
431          ]
432      ]
433
434    T.ArityMismatch x y ->
435      [ D.toSimpleHint $
436          if x < y then
437            "It looks like it takes too few arguments. I was expecting " ++ show (y - x) ++ " more."
438          else
439            "It looks like it takes too many arguments. I see " ++ show (x - y) ++ " extra."
440      ]
441
442    T.BadFlexSuper direction super _ tipe ->
443      case tipe of
444        T.Lambda _ _ _   -> badFlexSuper direction super tipe
445        T.Infinite       -> []
446        T.Error          -> []
447        T.FlexVar _      -> []
448        T.FlexSuper s _  -> badFlexFlexSuper super s
449        T.RigidVar y     -> badRigidVar y (toASuperThing super)
450        T.RigidSuper s _ -> badRigidSuper s (toASuperThing super)
451        T.Type _ _ _     -> badFlexSuper direction super tipe
452        T.Record _ _     -> badFlexSuper direction super tipe
453        T.Unit           -> badFlexSuper direction super tipe
454        T.Tuple _ _ _    -> badFlexSuper direction super tipe
455        T.Alias _ _ _ _  -> badFlexSuper direction super tipe
456
457    T.BadRigidVar x tipe ->
458      case tipe of
459        T.Lambda _ _ _   -> badRigidVar x "a function"
460        T.Infinite       -> []
461        T.Error          -> []
462        T.FlexVar _      -> []
463        T.FlexSuper s _  -> badRigidVar x (toASuperThing s)
464        T.RigidVar y     -> badDoubleRigid x y
465        T.RigidSuper _ y -> badDoubleRigid x y
466        T.Type _ n _     -> badRigidVar x ("a `" ++ Name.toChars n ++ "` value")
467        T.Record _ _     -> badRigidVar x "a record"
468        T.Unit           -> badRigidVar x "a unit value"
469        T.Tuple _ _ _    -> badRigidVar x "a tuple"
470        T.Alias _ n _ _  -> badRigidVar x ("a `" ++ Name.toChars n ++ "` value")
471
472    T.BadRigidSuper super x tipe ->
473      case tipe of
474        T.Lambda _ _ _   -> badRigidSuper super "a function"
475        T.Infinite       -> []
476        T.Error          -> []
477        T.FlexVar _      -> []
478        T.FlexSuper s _  -> badRigidSuper super (toASuperThing s)
479        T.RigidVar y     -> badDoubleRigid x y
480        T.RigidSuper _ y -> badDoubleRigid x y
481        T.Type _ n _     -> badRigidSuper super ("a `" ++ Name.toChars n ++ "` value")
482        T.Record _ _     -> badRigidSuper super "a record"
483        T.Unit           -> badRigidSuper super "a unit value"
484        T.Tuple _ _ _    -> badRigidSuper super "a tuple"
485        T.Alias _ n _ _  -> badRigidSuper super ("a `" ++ Name.toChars n ++ "` value")
486
487    T.FieldsMissing fields ->
488      case map (D.green . D.fromName) fields of
489        [] ->
490          []
491
492        [f1] ->
493          [ D.toFancyHint ["Looks","like","the",f1,"field","is","missing."]
494          ]
495
496        fieldDocs ->
497          [ D.toFancyHint $
498              ["Looks","like","fields"] ++ D.commaSep "and" id fieldDocs ++ ["are","missing."]
499          ]
500
501
502    T.FieldTypo typo possibilities ->
503      case Suggest.sort (Name.toChars typo) Name.toChars possibilities of
504        [] ->
505          []
506
507        nearest:_ ->
508          [ D.toFancyHint $
509              ["Seems","like","a","record","field","typo.","Maybe"
510              ,D.dullyellow (D.fromName typo),"should","be"
511              ,D.green (D.fromName nearest) <> "?"
512              ]
513          , D.toSimpleHint
514              "Can more type annotations be added? Type annotations always help me give\
515              \ more specific messages, and I think they could help a lot in this case!"
516          ]
517
518
519
520-- BAD RIGID HINTS
521
522
523badRigidVar :: Name.Name -> String -> [D.Doc]
524badRigidVar name aThing =
525  [ D.toSimpleHint $
526      "Your type annotation uses type variable `" ++ Name.toChars name ++
527      "` which means ANY type of value can flow through, but your code is saying it specifically wants "
528      ++ aThing ++ ". Maybe change your type annotation to\
529      \ be more specific? Maybe change the code to be more general?"
530  , D.reflowLink "Read" "type-annotations" "for more advice!"
531  ]
532
533
534badDoubleRigid :: Name.Name -> Name.Name -> [D.Doc]
535badDoubleRigid x y =
536  [ D.toSimpleHint $
537      "Your type annotation uses `" ++ Name.toChars x ++ "` and `" ++ Name.toChars y ++
538      "` as separate type variables. Your code seems to be saying they are the\
539      \ same though. Maybe they should be the same in your type annotation?\
540      \ Maybe your code uses them in a weird way?"
541  , D.reflowLink "Read" "type-annotations" "for more advice!"
542  ]
543
544
545toASuperThing :: T.Super -> String
546toASuperThing super =
547  case super of
548    T.Number     -> "a `number` value"
549    T.Comparable -> "a `comparable` value"
550    T.CompAppend -> "a `compappend` value"
551    T.Appendable -> "an `appendable` value"
552
553
554
555-- BAD SUPER HINTS
556
557
558badFlexSuper :: T.Direction -> T.Super -> T.Type -> [D.Doc]
559badFlexSuper direction super tipe =
560  case super of
561    T.Comparable ->
562      case tipe of
563        T.Record _ _ ->
564          [ D.link "Hint"
565              "I do not know how to compare records. I can only compare ints, floats,\
566              \ chars, strings, lists of comparable values, and tuples of comparable values.\
567              \ Check out" "comparing-records" "for ideas on how to proceed."
568          ]
569
570        T.Type _ name _ ->
571          [ D.toSimpleHint $
572              "I do not know how to compare `" ++ Name.toChars name ++ "` values. I can only\
573              \ compare ints, floats, chars, strings, lists of comparable values, and tuples\
574              \ of comparable values."
575          , D.reflowLink
576              "Check out" "comparing-custom-types" "for ideas on how to proceed."
577          ]
578
579        _ ->
580          [ D.toSimpleHint $
581              "I only know how to compare ints, floats, chars, strings, lists of\
582              \ comparable values, and tuples of comparable values."
583          ]
584
585    T.Appendable ->
586      [ D.toSimpleHint "I only know how to append strings and lists."
587      ]
588
589    T.CompAppend ->
590      [ D.toSimpleHint "Only strings and lists are both comparable and appendable."
591      ]
592
593    T.Number ->
594      case tipe of
595        T.Type home name _ | T.isString home name ->
596          case direction of
597            T.Have ->
598              [ D.toFancyHint ["Try","using",D.green "String.fromInt","to","convert","it","to","a","string?"]
599              ]
600
601            T.Need ->
602              [ D.toFancyHint ["Try","using",D.green "String.toInt","to","convert","it","to","an","integer?"]
603              ]
604
605        _ ->
606          [ D.toFancyHint ["Only",D.green "Int","and",D.green "Float","values","work","as","numbers."]
607          ]
608
609
610badRigidSuper :: T.Super -> String -> [D.Doc]
611badRigidSuper super aThing =
612  let
613    (superType, manyThings) =
614      case super of
615        T.Number -> ("number", "ints AND floats")
616        T.Comparable -> ("comparable", "ints, floats, chars, strings, lists, and tuples")
617        T.Appendable -> ("appendable", "strings AND lists")
618        T.CompAppend -> ("compappend", "strings AND lists")
619  in
620  [ D.toSimpleHint $
621      "The `" ++ superType ++ "` in your type annotation is saying that "
622      ++ manyThings ++ " can flow through, but your code is saying it specifically wants "
623      ++ aThing ++ ". Maybe change your type annotation to\
624      \ be more specific? Maybe change the code to be more general?"
625  , D.reflowLink "Read" "type-annotations" "for more advice!"
626  ]
627
628
629badFlexFlexSuper :: T.Super -> T.Super -> [D.Doc]
630badFlexFlexSuper s1 s2 =
631  let
632    likeThis super =
633      case super of
634        T.Number -> "a number"
635        T.Comparable -> "comparable"
636        T.CompAppend -> "a compappend"
637        T.Appendable -> "appendable"
638  in
639    [ D.toSimpleHint $
640        "There are no values in Elm that are both "
641        ++ likeThis s1 ++ " and " ++ likeThis s2 ++ "."
642    ]
643
644
645
646-- TO EXPR REPORT
647
648
649toExprReport :: Code.Source -> L.Localizer -> A.Region -> Category -> T.Type -> Expected T.Type -> Report.Report
650toExprReport source localizer exprRegion category tipe expected =
651  case expected of
652    NoExpectation expectedType ->
653      Report.Report "TYPE MISMATCH" exprRegion [] $
654        Code.toSnippet source exprRegion Nothing
655          ( "This expression is being used in an unexpected way:"
656          , typeComparison localizer tipe expectedType
657              (addCategory "It is" category)
658              "But you are trying to use it as:"
659              []
660          )
661
662    FromAnnotation name _arity subContext expectedType ->
663      let
664        thing =
665          case subContext of
666            TypedIfBranch index   -> D.ordinal index <> " branch of this `if` expression:"
667            TypedCaseBranch index -> D.ordinal index <> " branch of this `case` expression:"
668            TypedBody             -> "body of the `" <> Name.toChars name <> "` definition:"
669
670        itIs =
671          case subContext of
672            TypedIfBranch index   -> "The " <> D.ordinal index <> " branch is"
673            TypedCaseBranch index -> "The " <> D.ordinal index <> " branch is"
674            TypedBody             -> "The body is"
675      in
676      Report.Report "TYPE MISMATCH" exprRegion [] $
677        Code.toSnippet source exprRegion Nothing $
678          ( D.reflow ("Something is off with the " <> thing)
679          , typeComparison localizer tipe expectedType
680              (addCategory itIs category)
681              ("But the type annotation on `" <> Name.toChars name <> "` says it should be:")
682              []
683          )
684
685    FromContext region context expectedType ->
686      let
687        mismatch (maybeHighlight, problem, thisIs, insteadOf, furtherDetails) =
688          Report.Report "TYPE MISMATCH" exprRegion [] $
689            Code.toSnippet source region maybeHighlight
690              ( D.reflow problem
691              , typeComparison localizer tipe expectedType (addCategory thisIs category) insteadOf furtherDetails
692              )
693
694        badType (maybeHighlight, problem, thisIs, furtherDetails) =
695          Report.Report "TYPE MISMATCH" exprRegion [] $
696            Code.toSnippet source region maybeHighlight
697              ( D.reflow problem
698              , loneType localizer tipe expectedType (D.reflow (addCategory thisIs category)) furtherDetails
699              )
700
701        custom maybeHighlight docPair =
702          Report.Report "TYPE MISMATCH" exprRegion [] $
703            Code.toSnippet source region maybeHighlight docPair
704      in
705      case context of
706        ListEntry index ->
707          let ith = D.ordinal index in
708          mismatch
709          ( Just exprRegion
710          , "The " <> ith <> " element of this list does not match all the previous elements:"
711          , "The " <> ith <> " element is"
712          , "But all the previous elements in the list are:"
713          , [ D.link "Hint"
714                "Everything in a list must be the same type of value. This way, we never\
715                \ run into unexpected values partway through a List.map, List.foldl, etc. Read"
716                "custom-types"
717                "to learn how to “mix” types."
718            ]
719          )
720
721        Negate ->
722          badType
723          ( Just exprRegion
724          , "I do not know how to negate this type of value:"
725          , "It is"
726          , [ D.fillSep
727                ["But","I","only","now","how","to","negate"
728                ,D.dullyellow "Int","and",D.dullyellow "Float","values."
729                ]
730            ]
731          )
732
733        OpLeft op ->
734          custom (Just exprRegion) $
735            opLeftToDocs localizer category op tipe expectedType
736
737        OpRight op ->
738          case opRightToDocs localizer category op tipe expectedType of
739            EmphBoth details ->
740              custom Nothing details
741
742            EmphRight details ->
743              custom (Just exprRegion) details
744
745        IfCondition ->
746          badType
747          ( Just exprRegion
748          , "This `if` condition does not evaluate to a boolean value, True or False."
749          , "It is"
750          , [ D.fillSep ["But","I","need","this","`if`","condition","to","be","a",D.dullyellow "Bool","value."]
751            ]
752          )
753
754        IfBranch index ->
755          let ith = D.ordinal index in
756          mismatch
757          ( Just exprRegion
758          , "The " <> ith <> " branch of this `if` does not match all the previous branches:"
759          , "The " <> ith <> " branch is"
760          , "But all the previous branches result in:"
761          , [ D.link "Hint"
762                "All branches in an `if` must produce the same type of values. This way, no\
763                \ matter which branch we take, the result is always a consistent shape. Read"
764                "custom-types"
765                "to learn how to “mix” types."
766            ]
767          )
768
769        CaseBranch index ->
770          let ith = D.ordinal index in
771          mismatch
772          ( Just exprRegion
773          , "The " <> ith <> " branch of this `case` does not match all the previous branches:"
774          , "The " <> ith <> " branch is"
775          , "But all the previous branches result in:"
776          , [ D.link "Hint"
777                "All branches in a `case` must produce the same type of values. This way, no\
778                \ matter which branch we take, the result is always a consistent shape. Read"
779                "custom-types"
780                "to learn how to “mix” types."
781            ]
782          )
783
784        CallArity maybeFuncName numGivenArgs ->
785          Report.Report "TOO MANY ARGS" exprRegion [] $
786          Code.toSnippet source region (Just exprRegion) $
787          case countArgs tipe of
788            0 ->
789              let
790                thisValue =
791                  case maybeFuncName of
792                    NoName        -> "This value"
793                    FuncName name -> "The `" <> Name.toChars name <> "` value"
794                    CtorName name -> "The `" <> Name.toChars name <> "` value"
795                    OpName op     -> "The (" <> Name.toChars op <> ") operator"
796              in
797              ( D.reflow $ thisValue <> " is not a function, but it was given " <> D.args numGivenArgs <> "."
798              , D.reflow $ "Are there any missing commas? Or missing parentheses?"
799              )
800
801            n ->
802              let
803                thisFunction =
804                  case maybeFuncName of
805                    NoName        -> "This function"
806                    FuncName name -> "The `" <> Name.toChars name <> "` function"
807                    CtorName name -> "The `" <> Name.toChars name <> "` constructor"
808                    OpName op     -> "The (" <> Name.toChars op <> ") operator"
809              in
810              ( D.reflow $ thisFunction <> " expects " <> D.args n <> ", but it got " <> show numGivenArgs <> " instead."
811              , D.reflow $ "Are there any missing commas? Or missing parentheses?"
812              )
813
814        CallArg maybeFuncName index ->
815          let
816            ith = D.ordinal index
817
818            thisFunction =
819              case maybeFuncName of
820                NoName        -> "this function"
821                FuncName name -> "`" <> Name.toChars name <> "`"
822                CtorName name -> "`" <> Name.toChars name <> "`"
823                OpName op     -> "(" <> Name.toChars op <> ")"
824          in
825          mismatch
826          ( Just exprRegion
827          , "The " <> ith <> " argument to " <> thisFunction <> " is not what I expect:"
828          , "This argument is"
829          , "But " <> thisFunction <> " needs the " <> ith <> " argument to be:"
830          ,
831            if Index.toHuman index == 1 then
832              []
833            else
834              [ D.toSimpleHint $
835                 "I always figure out the argument types from left to right. If an argument\
836                  \ is acceptable, I assume it is “correct” and move on. So the problem may\
837                  \ actually be in one of the previous arguments!"
838              ]
839          )
840
841        RecordAccess recordRegion maybeName fieldRegion field ->
842          case T.iteratedDealias tipe of
843            T.Record fields ext ->
844              custom (Just fieldRegion)
845                ( D.reflow $
846                    "This "
847                    <> maybe "" (\n -> "`" <> Name.toChars n <> "`") maybeName
848                    <> " record does not have a `" <> Name.toChars field <> "` field:"
849                , case Suggest.sort (Name.toChars field) (Name.toChars . fst) (Map.toList fields) of
850                    [] ->
851                      D.reflow "In fact, it is a record with NO fields!"
852
853                    f:fs ->
854                      D.stack
855                        [ D.reflow $
856                            "This is usually a typo. Here are the "
857                            <> maybe "" (\n -> "`" <> Name.toChars n <> "`") maybeName
858                            <> " fields that are most similar:"
859                        , toNearbyRecord localizer f fs ext
860                        , D.fillSep
861                            ["So","maybe",D.dullyellow (D.fromName field)
862                            ,"should","be",D.green (D.fromName (fst f)) <> "?"
863                            ]
864                        ]
865                )
866
867            _ ->
868              badType
869              ( Just recordRegion
870              , "This is not a record, so it has no fields to access!"
871              , "It is"
872              , [ D.fillSep
873                    ["But","I","need","a","record","with","a"
874                    ,D.dullyellow (D.fromName field),"field!"
875                    ]
876                ]
877              )
878
879        RecordUpdateKeys record expectedFields ->
880          case T.iteratedDealias tipe of
881            T.Record actualFields ext ->
882              case Map.lookupMin (Map.difference expectedFields actualFields) of
883                Nothing ->
884                  mismatch
885                  ( Nothing
886                  , "Something is off with this record update:"
887                  , "The `" <> Name.toChars record <> "` record is"
888                  , "But this update needs it to be compatable with:"
889                  , [ D.reflow
890                        "Do you mind creating an <http://sscce.org/> that produces this error message and\
891                        \ sharing it at <https://github.com/elm/error-message-catalog/issues> so we\
892                        \ can try to give better advice here?"
893                    ]
894                  )
895
896                Just (field, Can.FieldUpdate fieldRegion _) ->
897                  let
898                    rStr = "`" <> Name.toChars record <> "`"
899                    fStr = "`" <> Name.toChars field <> "`"
900                  in
901                  custom (Just fieldRegion)
902                    ( D.reflow $
903                        "The " <> rStr <> " record does not have a " <> fStr <> " field:"
904                    , case Suggest.sort (Name.toChars field) (Name.toChars . fst) (Map.toList actualFields) of
905                        [] ->
906                          D.reflow $ "In fact, " <> rStr <> " is a record with NO fields!"
907
908                        f:fs ->
909                          D.stack
910                            [ D.reflow $
911                                "This is usually a typo. Here are the " <> rStr <> " fields that are most similar:"
912                            , toNearbyRecord localizer f fs ext
913                            , D.fillSep
914                                ["So","maybe",D.dullyellow (D.fromName field)
915                                ,"should","be",D.green (D.fromName (fst f)) <> "?"
916                                ]
917                            ]
918                    )
919
920            _ ->
921              badType
922              ( Just exprRegion
923              , "This is not a record, so it has no fields to update!"
924              , "It is"
925              , [ D.reflow $ "But I need a record!"
926                ]
927              )
928
929        RecordUpdateValue field ->
930          mismatch
931          ( Just exprRegion
932          , "I cannot update the `" <> Name.toChars field <> "` field like this:"
933          , "You are trying to update `" <> Name.toChars field <> "` to be"
934          , "But it should be:"
935          , [ D.toSimpleNote
936                "The record update syntax does not allow you to change the type of fields.\
937                \ You can achieve that with record constructors or the record literal syntax."
938            ]
939          )
940
941        Destructure ->
942          mismatch
943          ( Nothing
944          , "This definition is causing issues:"
945          , "You are defining"
946          , "But then trying to destructure it as:"
947          , []
948          )
949
950
951
952-- HELPERS
953
954
955countArgs :: T.Type -> Int
956countArgs tipe =
957  case tipe of
958    T.Lambda _ _ stuff ->
959      1 + length stuff
960
961    _ ->
962      0
963
964
965
966-- FIELD NAME HELPERS
967
968
969toNearbyRecord :: L.Localizer -> (Name.Name, T.Type) -> [(Name.Name, T.Type)] -> T.Extension -> D.Doc
970toNearbyRecord localizer f fs ext =
971  D.indent 4 $
972    if length fs <= 3 then
973      RT.vrecord (map (fieldToDocs localizer) (f:fs)) (extToDoc ext)
974    else
975      RT.vrecordSnippet (fieldToDocs localizer f) (map (fieldToDocs localizer) (take 3 fs))
976
977
978fieldToDocs :: L.Localizer -> (Name.Name, T.Type) -> (D.Doc, D.Doc)
979fieldToDocs localizer (name, tipe) =
980  ( D.fromName name
981  , T.toDoc localizer RT.None tipe
982  )
983
984
985extToDoc :: T.Extension -> Maybe D.Doc
986extToDoc ext =
987  case ext of
988    T.Closed      -> Nothing
989    T.FlexOpen  x -> Just (D.fromName x)
990    T.RigidOpen x -> Just (D.fromName x)
991
992
993
994-- OP LEFT
995
996
997opLeftToDocs :: L.Localizer -> Category -> Name.Name -> T.Type -> T.Type -> (D.Doc, D.Doc)
998opLeftToDocs localizer category op tipe expected =
999  case op of
1000    "+"
1001      | isString tipe -> badStringAdd
1002      | isList tipe   -> badListAdd localizer category "left" tipe expected
1003      | otherwise     -> badMath localizer category "Addition" "left" "+" tipe expected []
1004
1005    "*"
1006      | isList tipe  -> badListMul localizer category "left" tipe expected
1007      | otherwise    -> badMath localizer category "Multiplication" "left" "*" tipe expected []
1008
1009    "-"  -> badMath localizer category "Subtraction" "left" "-" tipe expected []
1010    "^"  -> badMath localizer category "Exponentiation" "left" "^" tipe expected []
1011    "/"  -> badFDiv localizer "left" tipe expected
1012    "//" -> badIDiv localizer "left" tipe expected
1013    "&&" -> badBool localizer "&&" "left" tipe expected
1014    "||" -> badBool localizer "||" "left" tipe expected
1015    "<"  -> badCompLeft localizer category "<" "left" tipe expected
1016    ">"  -> badCompLeft localizer category ">" "left" tipe expected
1017    "<=" -> badCompLeft localizer category "<=" "left" tipe expected
1018    ">=" -> badCompLeft localizer category ">=" "left" tipe expected
1019
1020    "++" -> badAppendLeft localizer category tipe expected
1021
1022    "<|" ->
1023      ( "The left side of (<|) needs to be a function so I can pipe arguments to it!"
1024      , loneType localizer tipe expected
1025          (D.reflow (addCategory "I am seeing" category))
1026          [ D.reflow $ "This needs to be some kind of function though!"
1027          ]
1028      )
1029
1030    _ ->
1031      ( D.reflow $
1032          "The left argument of (" <> Name.toChars op <> ") is causing problems:"
1033      , typeComparison localizer tipe expected
1034          (addCategory "The left argument is" category)
1035          ("But (" <> Name.toChars op <> ") needs the left argument to be:")
1036          []
1037      )
1038
1039
1040
1041-- OP RIGHT
1042
1043
1044data RightDocs
1045  = EmphBoth (D.Doc, D.Doc)
1046  | EmphRight (D.Doc, D.Doc)
1047
1048
1049opRightToDocs :: L.Localizer -> Category -> Name.Name -> T.Type -> T.Type -> RightDocs
1050opRightToDocs localizer category op tipe expected =
1051  case op of
1052    "+"
1053      | isFloat expected && isInt tipe -> badCast op FloatInt
1054      | isInt expected && isFloat tipe -> badCast op IntFloat
1055      | isString tipe -> EmphRight $ badStringAdd
1056      | isList tipe   -> EmphRight $ badListAdd localizer category "right" tipe expected
1057      | otherwise     -> EmphRight $ badMath localizer category "Addition" "right" "+" tipe expected []
1058
1059    "*"
1060      | isFloat expected && isInt tipe -> badCast op FloatInt
1061      | isInt expected && isFloat tipe -> badCast op IntFloat
1062      | isList tipe -> EmphRight $ badListMul localizer category "right" tipe expected
1063      | otherwise   -> EmphRight $ badMath localizer category "Multiplication" "right" "*" tipe expected []
1064
1065    "-"
1066      | isFloat expected && isInt tipe -> badCast op FloatInt
1067      | isInt expected && isFloat tipe -> badCast op IntFloat
1068      | otherwise ->
1069          EmphRight $ badMath localizer category "Subtraction" "right" "-" tipe expected []
1070
1071    "^"
1072      | isFloat expected && isInt tipe -> badCast op FloatInt
1073      | isInt expected && isFloat tipe -> badCast op IntFloat
1074      | otherwise ->
1075          EmphRight $ badMath localizer category "Exponentiation" "right" "^" tipe expected []
1076
1077    "/"  -> EmphRight $ badFDiv localizer "right" tipe expected
1078    "//" -> EmphRight $ badIDiv localizer "right" tipe expected
1079    "&&" -> EmphRight $ badBool localizer "&&" "right" tipe expected
1080    "||" -> EmphRight $ badBool localizer "||" "right" tipe expected
1081    "<"  -> badCompRight localizer "<" tipe expected
1082    ">"  -> badCompRight localizer ">" tipe expected
1083    "<=" -> badCompRight localizer "<=" tipe expected
1084    ">=" -> badCompRight localizer ">=" tipe expected
1085    "==" -> badEquality localizer "==" tipe expected
1086    "/=" -> badEquality localizer "/=" tipe expected
1087
1088    "::" -> badConsRight localizer category tipe expected
1089    "++" -> badAppendRight localizer category tipe expected
1090
1091    "<|" ->
1092      EmphRight
1093        ( D.reflow $ "I cannot send this through the (<|) pipe:"
1094        , typeComparison localizer tipe expected
1095            "The argument is:"
1096            "But (<|) is piping it to a function that expects:"
1097            []
1098        )
1099
1100    "|>" ->
1101      case (tipe, expected) of
1102        (T.Lambda expectedArgType _ _, T.Lambda argType _ _) ->
1103          EmphRight
1104            ( D.reflow $ "This function cannot handle the argument sent through the (|>) pipe:"
1105            , typeComparison localizer argType expectedArgType
1106                "The argument is:"
1107                "But (|>) is piping it to a function that expects:"
1108                []
1109            )
1110
1111        _ ->
1112          EmphRight
1113            ( D.reflow $ "The right side of (|>) needs to be a function so I can pipe arguments to it!"
1114            , loneType localizer tipe expected
1115                (D.reflow (addCategory "But instead of a function, I am seeing" category))
1116                []
1117            )
1118
1119    _ ->
1120      badOpRightFallback localizer category op tipe expected
1121
1122
1123badOpRightFallback :: L.Localizer -> Category -> Name.Name -> T.Type -> T.Type -> RightDocs
1124badOpRightFallback localizer category op tipe expected =
1125  EmphRight
1126    ( D.reflow $
1127        "The right argument of (" <> Name.toChars op <> ") is causing problems."
1128    , typeComparison localizer tipe expected
1129        (addCategory "The right argument is" category)
1130        ("But (" <> Name.toChars op <> ") needs the right argument to be:")
1131        [ D.toSimpleHint $
1132            "With operators like (" ++ Name.toChars op ++ ") I always check the left\
1133            \ side first. If it seems fine, I assume it is correct and check the right\
1134            \ side. So the problem may be in how the left and right arguments interact!"
1135        ]
1136    )
1137
1138
1139isInt :: T.Type -> Bool
1140isInt tipe =
1141  case tipe of
1142    T.Type home name [] ->
1143      T.isInt home name
1144
1145    _ ->
1146      False
1147
1148
1149isFloat :: T.Type -> Bool
1150isFloat tipe =
1151  case tipe of
1152    T.Type home name [] ->
1153      T.isFloat home name
1154
1155    _ ->
1156      False
1157
1158
1159isString :: T.Type -> Bool
1160isString tipe =
1161  case tipe of
1162    T.Type home name [] ->
1163      T.isString home name
1164
1165    _ ->
1166      False
1167
1168
1169isList :: T.Type -> Bool
1170isList tipe =
1171  case tipe of
1172    T.Type home name [_] ->
1173      T.isList home name
1174
1175    _ ->
1176      False
1177
1178
1179
1180-- BAD CONS
1181
1182
1183badConsRight :: L.Localizer -> Category -> T.Type -> T.Type -> RightDocs
1184badConsRight localizer category tipe expected =
1185  case tipe of
1186    T.Type home1 name1 [actualElement] | T.isList home1 name1 ->
1187      case expected of
1188        T.Type home2 name2 [expectedElement] | T.isList home2 name2 ->
1189          EmphBoth
1190            ( D.reflow "I am having trouble with this (::) operator:"
1191            , typeComparison localizer expectedElement actualElement
1192                "The left side of (::) is:"
1193                "But you are trying to put that into a list filled with:"
1194                ( case expectedElement of
1195                    T.Type home name [_] | T.isList home name ->
1196                      [ D.toSimpleHint
1197                          "Are you trying to append two lists? The (++) operator\
1198                          \ appends lists, whereas the (::) operator is only for\
1199                          \ adding ONE element to a list."
1200                      ]
1201
1202                    _ ->
1203                      [ D.reflow
1204                          "Lists need ALL elements to be the same type though."
1205                      ]
1206                )
1207            )
1208
1209        _ ->
1210          badOpRightFallback localizer category "::" tipe expected
1211
1212    _ ->
1213      EmphRight
1214        ( D.reflow "The (::) operator can only add elements onto lists."
1215        , loneType localizer tipe expected
1216            (D.reflow (addCategory "The right side is" category))
1217            [D.fillSep ["But","(::)","needs","a",D.dullyellow "List","on","the","right."]
1218            ]
1219        )
1220
1221
1222
1223-- BAD APPEND
1224
1225
1226data AppendType
1227  = ANumber D.Doc D.Doc
1228  | AString
1229  | AList
1230  | AOther
1231
1232
1233toAppendType :: T.Type -> AppendType
1234toAppendType tipe =
1235  case tipe of
1236    T.Type home name _
1237      | T.isInt    home name -> ANumber "Int" "String.fromInt"
1238      | T.isFloat  home name -> ANumber "Float" "String.fromFloat"
1239      | T.isString home name -> AString
1240      | T.isList   home name -> AList
1241
1242    T.FlexSuper T.Number _ -> ANumber "number" "String.fromInt"
1243
1244    _ -> AOther
1245
1246
1247badAppendLeft :: L.Localizer -> Category -> T.Type -> T.Type -> (D.Doc, D.Doc)
1248badAppendLeft localizer category tipe expected =
1249  case toAppendType tipe of
1250    ANumber thing stringFromThing ->
1251      ( D.fillSep
1252          ["The","(++)","operator","can","append","List","and","String"
1253          ,"values,","but","not",D.dullyellow thing,"values","like","this:"
1254          ]
1255      , D.fillSep
1256          ["Try","using",D.green stringFromThing,"to","turn","it","into","a","string?"
1257          ,"Or","put","it","in","[]","to","make","it","a","list?"
1258          ,"Or","switch","to","the","(::)","operator?"
1259          ]
1260      )
1261
1262    _ ->
1263      ( D.reflow $
1264          "The (++) operator cannot append this type of value:"
1265      , loneType localizer tipe expected
1266          (D.reflow (addCategory "I am seeing" category))
1267          [ D.fillSep
1268              ["But","the","(++)","operator","is","only","for","appending"
1269              ,D.dullyellow "List","and",D.dullyellow "String","values."
1270              ,"Maybe","put","this","value","in","[]","to","make","it","a","list?"
1271              ]
1272          ]
1273      )
1274
1275
1276badAppendRight :: L.Localizer -> Category -> T.Type -> T.Type -> RightDocs
1277badAppendRight localizer category tipe expected =
1278  case (toAppendType expected, toAppendType tipe) of
1279    (AString, ANumber thing stringFromThing) ->
1280      EmphRight
1281        ( D.fillSep
1282            ["I","thought","I","was","appending",D.dullyellow "String","values","here,"
1283            ,"not",D.dullyellow thing,"values","like","this:"
1284            ]
1285        , D.fillSep
1286            ["Try","using",D.green stringFromThing,"to","turn","it","into","a","string?"]
1287        )
1288
1289    (AList, ANumber thing _) ->
1290      EmphRight
1291        ( D.fillSep
1292            ["I","thought","I","was","appending",D.dullyellow "List","values","here,"
1293            ,"not",D.dullyellow thing,"values","like","this:"
1294            ]
1295        , D.reflow "Try putting it in [] to make it a list?"
1296        )
1297
1298    (AString, AList) ->
1299      EmphBoth
1300        ( D.reflow $
1301            "The (++) operator needs the same type of value on both sides:"
1302        , D.fillSep
1303            ["I","see","a",D.dullyellow "String","on","the","left","and","a"
1304            ,D.dullyellow "List","on","the","right.","Which","should","it","be?"
1305            ,"Does","the","string","need","[]","around","it","to","become","a","list?"
1306            ]
1307        )
1308
1309    (AList, AString) ->
1310      EmphBoth
1311        ( D.reflow $
1312            "The (++) operator needs the same type of value on both sides:"
1313        , D.fillSep
1314            ["I","see","a",D.dullyellow "List","on","the","left","and","a"
1315            ,D.dullyellow "String","on","the","right.","Which","should","it","be?"
1316            ,"Does","the","string","need","[]","around","it","to","become","a","list?"
1317            ]
1318        )
1319
1320    (_,_) ->
1321      EmphBoth
1322        ( D.reflow $
1323            "The (++) operator cannot append these two values:"
1324        , typeComparison localizer expected tipe
1325            "I already figured out that the left side of (++) is:"
1326            (addCategory "But this clashes with the right side, which is" category)
1327            []
1328        )
1329
1330
1331
1332-- BAD MATH
1333
1334
1335data ThisThenThat = FloatInt | IntFloat
1336
1337
1338badCast :: Name.Name -> ThisThenThat -> RightDocs
1339badCast op thisThenThat =
1340  EmphBoth
1341    ( D.reflow $
1342        "I need both sides of (" <> Name.toChars op <> ") to be the exact same type. Both Int or both Float."
1343    , let
1344        anInt = ["an", D.dullyellow "Int"]
1345        aFloat = ["a", D.dullyellow "Float"]
1346        toFloat = D.green "toFloat"
1347        round = D.green "round"
1348      in
1349      case thisThenThat of
1350        FloatInt ->
1351          badCastHelp aFloat anInt round toFloat
1352
1353        IntFloat ->
1354          badCastHelp anInt aFloat toFloat round
1355    )
1356
1357
1358badCastHelp :: [D.Doc] -> [D.Doc] -> D.Doc -> D.Doc -> D.Doc
1359badCastHelp anInt aFloat toFloat round =
1360  D.stack
1361    [ D.fillSep $
1362        ["But","I","see"]
1363        ++ anInt
1364        ++ ["on","the","left","and"]
1365        ++ aFloat
1366        ++ ["on","the","right."]
1367    , D.fillSep
1368        ["Use",toFloat,"on","the","left","(or",round,"on"
1369        ,"the","right)","to","make","both","sides","match!"
1370        ]
1371    , D.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats."
1372    ]
1373
1374
1375badStringAdd :: (D.Doc, D.Doc)
1376badStringAdd =
1377  (
1378    D.fillSep ["I","cannot","do","addition","with",D.dullyellow "String","values","like","this","one:"]
1379  ,
1380    D.stack
1381      [ D.fillSep
1382          ["The","(+)","operator","only","works","with",D.dullyellow "Int","and",D.dullyellow "Float","values."
1383          ]
1384      , D.toFancyHint
1385          ["Switch","to","the",D.green "(++)","operator","to","append","strings!"
1386          ]
1387      ]
1388  )
1389
1390
1391badListAdd :: L.Localizer -> Category -> String -> T.Type -> T.Type -> (D.Doc, D.Doc)
1392badListAdd localizer category direction tipe expected =
1393  (
1394    "I cannot do addition with lists:"
1395  ,
1396    loneType localizer tipe expected
1397      (D.reflow (addCategory ("The " <> direction <> " side of (+) is") category))
1398      [ D.fillSep
1399          ["But","(+)","only","works","with",D.dullyellow "Int","and",D.dullyellow "Float","values."
1400          ]
1401      , D.toFancyHint
1402          ["Switch","to","the",D.green "(++)","operator","to","append","lists!"
1403          ]
1404      ]
1405  )
1406
1407
1408badListMul :: L.Localizer -> Category -> String -> T.Type -> T.Type -> (D.Doc, D.Doc)
1409badListMul localizer category direction tipe expected =
1410  badMath localizer category "Multiplication" direction "*" tipe expected
1411    [
1412      D.toFancyHint
1413        [ "Maybe", "you", "want"
1414        , D.green "List.repeat"
1415        , "to", "build","a","list","of","repeated","values?"
1416        ]
1417    ]
1418
1419
1420badMath :: L.Localizer -> Category -> String -> String -> String -> T.Type -> T.Type -> [D.Doc] -> (D.Doc, D.Doc)
1421badMath localizer category operation direction op tipe expected otherHints =
1422  (
1423    D.reflow $
1424      operation ++ " does not work with this value:"
1425  ,
1426    loneType localizer tipe expected
1427      (D.reflow (addCategory ("The " <> direction <> " side of (" <> op <> ") is") category))
1428      ( [ D.fillSep
1429            ["But","(" <> D.fromChars op <> ")","only","works","with"
1430            ,D.dullyellow "Int","and",D.dullyellow "Float","values."
1431            ]
1432        ]
1433        ++ otherHints
1434      )
1435  )
1436
1437
1438badFDiv :: L.Localizer -> D.Doc -> T.Type -> T.Type -> (D.Doc, D.Doc)
1439badFDiv localizer direction tipe expected =
1440  (
1441    D.reflow $
1442      "The (/) operator is specifically for floating-point division:"
1443  ,
1444    if isInt tipe then
1445      D.stack
1446        [ D.fillSep
1447            ["The",direction,"side","of","(/)","must","be","a"
1448            ,D.dullyellow "Float" <> ","
1449            ,"but","I","am","seeing","an",D.dullyellow "Int" <> "."
1450            ,"I","recommend:"
1451            ]
1452        , D.vcat
1453            [ D.green "toFloat" <> " for explicit conversions     " <> D.black "(toFloat 5 / 2) == 2.5"
1454            , D.green "(//)   " <> " for integer division         " <> D.black "(5 // 2)        == 2"
1455            ]
1456        , D.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats."
1457        ]
1458
1459    else
1460      loneType localizer tipe expected
1461        (D.fillSep
1462          ["The",direction,"side","of","(/)","must","be","a"
1463          ,D.dullyellow "Float" <> ",","but","instead","I","am","seeing:"
1464          ]
1465        )
1466        []
1467  )
1468
1469
1470badIDiv :: L.Localizer -> D.Doc -> T.Type -> T.Type -> (D.Doc, D.Doc)
1471badIDiv localizer direction tipe expected =
1472  (
1473    D.reflow $
1474      "The (//) operator is specifically for integer division:"
1475  ,
1476    if isFloat tipe then
1477      D.stack
1478        [ D.fillSep
1479            ["The",direction,"side","of","(//)","must","be","an"
1480            ,D.dullyellow "Int" <> ","
1481            ,"but","I","am","seeing","a",D.dullyellow "Float" <> "."
1482            ,"I","recommend","doing","the","conversion","explicitly"
1483            ,"with","one","of","these","functions:"
1484            ]
1485        , D.vcat
1486            [ D.green "round" <> " 3.5     == 4"
1487            , D.green "floor" <> " 3.5     == 3"
1488            , D.green "ceiling" <> " 3.5   == 4"
1489            , D.green "truncate" <> " 3.5  == 3"
1490            ]
1491        , D.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats."
1492        ]
1493    else
1494      loneType localizer tipe expected
1495        ( D.fillSep
1496            ["The",direction,"side","of","(//)","must","be","an"
1497            ,D.dullyellow "Int" <> ",","but","instead","I","am","seeing:"
1498            ]
1499        )
1500        []
1501  )
1502
1503
1504
1505-- BAD BOOLS
1506
1507
1508badBool :: L.Localizer -> D.Doc -> D.Doc -> T.Type -> T.Type -> (D.Doc, D.Doc)
1509badBool localizer op direction tipe expected =
1510  (
1511    D.reflow $
1512      "I am struggling with this boolean operation:"
1513  ,
1514    loneType localizer tipe expected
1515      ( D.fillSep
1516          ["Both","sides","of","(" <> op <> ")","must","be"
1517          ,D.dullyellow "Bool","values,","but","the",direction,"side","is:"
1518          ]
1519      )
1520      []
1521  )
1522
1523
1524
1525-- BAD COMPARISON
1526
1527
1528badCompLeft :: L.Localizer -> Category -> String -> String -> T.Type -> T.Type -> (D.Doc, D.Doc)
1529badCompLeft localizer category op direction tipe expected =
1530  (
1531    D.reflow $
1532      "I cannot do a comparison with this value:"
1533  ,
1534    loneType localizer tipe expected
1535      (D.reflow (addCategory ("The " <> direction <> " side of (" <> op <> ") is") category))
1536      [ D.fillSep
1537          ["But","(" <> D.fromChars op <> ")","only","works","on"
1538          ,D.dullyellow "Int" <> ","
1539          ,D.dullyellow "Float" <> ","
1540          ,D.dullyellow "Char" <> ","
1541          ,"and"
1542          ,D.dullyellow "String"
1543          ,"values.","It","can","work","on","lists","and","tuples"
1544          ,"of","comparable","values","as","well,","but","it","is"
1545          ,"usually","better","to","find","a","different","path."
1546          ]
1547      ]
1548  )
1549
1550
1551badCompRight :: L.Localizer -> String -> T.Type -> T.Type -> RightDocs
1552badCompRight localizer op tipe expected =
1553  EmphBoth
1554    (
1555      D.reflow $
1556        "I need both sides of (" <> op <> ") to be the same type:"
1557    ,
1558      typeComparison localizer expected tipe
1559        ("The left side of (" <> op <> ") is:")
1560        "But the right side is:"
1561        [ D.reflow $
1562            "I cannot compare different types though! Which side of (" <> op <> ") is the problem?"
1563        ]
1564    )
1565
1566
1567
1568-- BAD EQUALITY
1569
1570
1571badEquality :: L.Localizer -> String -> T.Type -> T.Type -> RightDocs
1572badEquality localizer op tipe expected =
1573  EmphBoth
1574    (
1575      D.reflow $
1576        "I need both sides of (" <> op <> ") to be the same type:"
1577    ,
1578      typeComparison localizer expected tipe
1579        ("The left side of (" <> op <> ") is:")
1580        "But the right side is:"
1581        [ if isFloat tipe || isFloat expected then
1582            D.toSimpleNote $
1583              "Equality on floats is not 100% reliable due to the design of IEEE 754. I\
1584              \ recommend a check like (abs (x - y) < 0.0001) instead."
1585          else
1586            D.reflow  "Different types can never be equal though! Which side is messed up?"
1587        ]
1588    )
1589
1590
1591
1592-- INFINITE TYPES
1593
1594
1595toInfiniteReport :: Code.Source -> L.Localizer -> A.Region -> Name.Name -> T.Type -> Report.Report
1596toInfiniteReport source localizer region name overallType =
1597  Report.Report "INFINITE TYPE" region [] $
1598    Code.toSnippet source region Nothing
1599      (
1600        D.reflow $
1601          "I am inferring a weird self-referential type for " <> Name.toChars name <> ":"
1602      ,
1603        D.stack
1604          [ D.reflow $
1605              "Here is my best effort at writing down the type. You will see ∞ for\
1606              \ parts of the type that repeat something already printed out infinitely."
1607          , D.indent 4 (D.dullyellow (T.toDoc localizer RT.None overallType))
1608          , D.reflowLink
1609              "Staring at this type is usually not so helpful, so I recommend reading the hints at"
1610              "infinite-type"
1611              "to get unstuck!"
1612          ]
1613      )
1614