on master help sign in
Begin revamp of type error messages
5366b91Evan Czaplicki
src/Reporting/Error/Type.hs+893−1060
@@ -2,35 +2,25 @@
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Type
3module Reporting.Error.Type
4 ( Error(..)
4 ( Error(..)
5− , Mismatch(..)
5+ , Origin(..)
6− , Hint(..)
7− , Reason(..)
8− , SpecificThing(..)
9− , PatternContext(..)
10− , PatternCategory(..)
11 , toReport
6 , toReport
12− , flipReason
13 )
7 )
14 where
8 where
15
9
16−import Control.Arrow (second)
10+
17−import qualified Data.Map as Map
11+import Prelude hiding (round)
18−import qualified Data.Text as Text
19−import Data.Text (Text)
20import Data.Monoid ((<>))
12import Data.Monoid ((<>))
21
13
22−import qualified AST.Module.Name as ModuleName
14+import qualified AST.Canonical as Can
23−import qualified AST.Type as Type
15+import qualified AST.Utils.Type as Type
24−import qualified AST.Variable as Var
16+import qualified Data.Index as Index
25import qualified Elm.Name as N
17import qualified Elm.Name as N
18+import qualified Reporting.Helpers as H
26import qualified Reporting.Region as R
19import qualified Reporting.Region as R
27−import qualified Reporting.Render.Type as RenderType
20+import qualified Reporting.Render.Code as Code
21+import qualified Reporting.Render.Type as Type
28import qualified Reporting.Report as Report
22import qualified Reporting.Report as Report
29−import qualified Reporting.Helpers as Help
23+import qualified Type.Constraint as C
30−import Reporting.Helpers
31− ( Doc, capitalize, dullyellow, functionName, i2t
32− , indent, ordinalize, reflowParagraph, stack, text
33− )
34
24
35
25
36
26
@@ -38,1138 +28,981 @@ import Reporting.Helpers
38
28
39
29
40data Error
30data Error
41− = Mismatch Mismatch
31+ = Mismatch Origin (C.Expected Can.Type)
42− | BadMain Type.Canonical
32+ | BadPattern R.Region C.PCategory Can.Type (C.PExpected Can.Type)
43− | BadFlags Type.Canonical (Maybe Text)
44− | InfiniteType (Either Hint Text) Type.Canonical
33+ | InfiniteType Can.Type
45−
34+
46−
35+
47−data Mismatch =
48− MismatchInfo
49− { _hint :: Hint
50− , _leftType :: Type.Canonical
51− , _rightType :: Type.Canonical
52− , _decoders :: Bool
53− , _reason :: Maybe Reason
54− }
55−
56−
57−data Reason
36+data Origin
58− = BadFields [(Text, Maybe Reason)]
37+ = BadExpr R.Region C.Category Can.Type
59− | MessyFields [Text] [Text] [Text]
38+ | BadLocal R.Region N.Name Can.Type
60− | IntFloat
39+ | BadForeign R.Region N.Name Can.Type
61− | MissingArgs Int
62− | RigidClash Text Text
63− | NotPartOfSuper Type.Super
64− | RigidVarTooGeneric Text SpecificThing
65− | RigidSuperTooGeneric Type.Super Text SpecificThing
66−
67−
68−data SpecificThing
69− = SpecificSuper Type.Super
70− | SpecificType N.Name
71− | SpecificFunction
72− | SpecificRecord
73− | SpecificUnit
74− | SpecificTuple
75−
76−
77−data Hint
78− = CaseBranch Int R.Region
79− | Case
80− | IfCondition
81− | IfBranches
82− | MultiIfBranch Int R.Region
83− | If
84− | List
85− | ListElement Int R.Region
86− | BinopLeft ModuleName.Canonical N.Name R.Region
87− | BinopRight ModuleName.Canonical N.Name R.Region
88− | Binop ModuleName.Canonical N.Name
89− | Function (Maybe Var.Canonical)
90− | UnexpectedArg (Maybe Var.Canonical) Int Int R.Region
91− | FunctionArity (Maybe Var.Canonical) Int Int R.Region
92− | ReturnType Text Int Int R.Region
93− | Instance Text
94− | Literal Text
95− | Pattern PatternContext PatternCategory
96− | PatternCons
97− | Shader
98− | Lambda
99− | Accessor Text
100− | Access (Maybe Text) Text
101− | Record
102− | Unit
103− | Tuple
104− -- effect manager problems
105− | Manager Text
106− | State Text
107− | SelfMsg
108−
109−
110−data PatternContext
111− = PatternArg N.Name Int
112− | PatternTail
113− | PatternList Int
114− | PatternUnknown
115−
116−
117−data PatternCategory
118− = PRecord
119− | PUnit
120− | PTuple
121− | PList
122− | PCtor N.Name
123
40
124
41
125
42
126-- TO REPORT
43-- TO REPORT
127
44
128
45
129−toReport :: RenderType.Localizer -> Error -> Report.Report
46+toReport :: Code.Source -> Type.Localizer -> Error -> Report.Report
130−toReport localizer err =
47+toReport source localizer err =
131 case err of
48 case err of
132− Mismatch info ->
49+ Mismatch origin expected ->
133− mismatchToReport localizer info
50+ case origin of
134−
51+ BadForeign region name tipe ->
135− InfiniteType context overallType ->
52+ toMismatchReport source localizer region (Var name) tipe expected
136− infiniteTypeToReport localizer context overallType
137−
138− BadMain tipe ->
139− Report.report
140− "BAD MAIN TYPE"
141− Nothing
142− "The `main` value has an unsupported type."
143− ( stack
144− [ reflowParagraph $
145− "I need Html, Svg, or a Program so I have something to render on\
146− \ screen, but you gave me:"
147− , indent 4 (RenderType.toDoc localizer tipe)
148− ]
149− )
150
53
151− BadFlags tipe maybeMessage ->
54+ BadLocal region name tipe ->
152− let
55+ toMismatchReport source localizer region (Var name) tipe expected
153− context =
154− maybe "" (" the following " <> ) maybeMessage
155− in
156− Report.report
157− "BAD FLAGS"
158− Nothing
159− ("Your `main` is demanding an unsupported type as a flag."
160− )
161− ( stack
162− [ text ("The specific unsupported type is" <> context <> ":")
163− , indent 4 (RenderType.toDoc localizer tipe)
164− , text "The types of values that can flow through in and out of Elm include:"
165− , indent 4 $ reflowParagraph $
166− "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays,\
167− \ Tuples, Json.Values, and concrete records."
168− ]
169− )
170
56
57+ BadExpr region category tipe ->
58+ toMismatchReport source localizer region (Category category) tipe expected
171
59
60+ BadPattern region category tipe expected ->
61+ error "TODO" region category tipe expected
172
62
173−-- TYPE MISMATCHES
63+ InfiniteType overallType ->
64+ error "TODO" source localizer overallType
174
65
175
66
176−mismatchToReport :: RenderType.Localizer -> Mismatch -> Report.Report
177−mismatchToReport localizer (MismatchInfo hint leftType rightType decoders maybeReason) =
178− let
179− report =
180− Report.report "TYPE MISMATCH"
181
67
182− typicalHints =
68+-- HELPERS
183− maybe id (:) (reasonToDoc <$> maybeReason) $
184− if decoders then [ jsonDecoderHint ] else []
185
69
186− cmpHint leftWords rightWords extraHints =
187− comparisonHint localizer leftType rightType leftWords rightWords
188− (typicalHints <> map Help.toSimpleHint extraHints)
189− in
190− case hint of
191− CaseBranch branchNumber region ->
192− report
193− (Just region)
194− ( "The " <> ordinalPair branchNumber
195− <> " branches of this `case` produce different types of values."
196− )
197− ( cmpHint
198− ("The " <> ordinalize (branchNumber - 1) <> " branch has this type:")
199− ("But the " <> ordinalize branchNumber <> " is:")
200− [ "All branches in a `case` must have the same type. So no matter\
201− \ which one we take, we always get back the same type of value."
202− ]
203− )
204
70
205− Case ->
71+data Thing
206− report
72+ = Var N.Name
207− Nothing
73+ | Category C.Category
208− ( "All the branches of this case-expression are consistent, but the overall\n"
209− <> "type does not match how it is used elsewhere."
210− )
211− ( cmpHint
212− "The `case` evaluates to something of type:"
213− "Which is fine, but the surrounding context wants it to be:"
214− []
215− )
216
74
217− IfCondition ->
218− report
219− Nothing
220− "This condition does not evaluate to a boolean value, True or False."
221− ( cmpHint
222− "You have given me a condition with this type:"
223− "But I need it to be:"
224− [ "Elm does not have \"truthiness\" such that ints and strings and lists\
225− \ are automatically converted to booleans. Do that conversion explicitly."
226− ]
227− )
228
75
229− IfBranches ->
76+typeComparison :: Type.Localizer -> Can.Type -> Can.Type -> ( String, String, [H.Doc] ) -> H.Doc
230− report
77+typeComparison localizer actual expected ( iAmSeeing, insteadOf, soHereAreSomeThoughts ) =
231− Nothing
78+ H.stack $
232− "The branches of this `if` produce different types of values."
79+ [ H.reflow iAmSeeing
233− ( cmpHint
80+ , H.indent 4 (Type.toDoc localizer actual)
234− "The `then` branch has type:"
81+ , H.reflow insteadOf
235− "But the `else` branch is:"
82+ , H.indent 4 (Type.toDoc localizer expected)
236− [ "These need to match so that no matter which branch we take, we\
83+ ]
237− \ always get back the same type of value."
84+ ++ soHereAreSomeThoughts
238− ]
85+
239− )
86+
240−
87+toDescription :: Thing -> String -> String
241− MultiIfBranch branchNumber region ->
88+toDescription thing thatThingIs =
242− report
89+ case thing of
243− (Just region)
90+ Var name ->
244− ( "The " <> ordinalPair branchNumber
91+ "This `" <> N.toString name <> "` value has type:"
245− <> " branches of this `if` produce different types of values."
92+
246− )
93+ Category category ->
247− ( cmpHint
94+ case category of
248− ("The " <> ordinalize (branchNumber - 1) <> " branch has this type:")
95+ C.List -> thatThingIs <> " a list of type:"
249− ("But the "<> ordinalize branchNumber <> " is:")
96+ C.Number -> thatThingIs <> " a number of type:"
250− [ "All the branches of an `if` need to match so that no matter which\
97+ C.Float -> thatThingIs <> " a float of type:"
251− \ one we take, we get back the same type of value overall."
98+ C.String -> thatThingIs <> " a string of type:"
99+ C.Char -> thatThingIs <> " a character of type:"
100+ C.If -> "This `if` expression produces a value of type:"
101+ C.Case -> "This `case` expression produces a value of type:"
102+ C.Lambda -> thatThingIs <> " an anonymous function of type:"
103+ C.Accessor field -> "This ." <> N.toString field <> " field access function has type of type:"
104+ C.Access field -> "The value at ." <> N.toString field <> " has type of type:"
105+ C.Record -> thatThingIs <> " a record of type:"
106+ C.Tuple -> thatThingIs <> " a tuple of type:"
107+ C.Unit -> thatThingIs <> " a unit value:"
108+ C.Shader -> thatThingIs <> " a GLSL shader of type:"
109+ C.Effects -> thatThingIs <> " a thing for CORE LIBRARIES ONLY."
110+ C.CallResult maybeName ->
111+ case maybeName of
112+ C.NoName -> thatThingIs <> ":"
113+ C.FuncName name -> "This `" <> N.toString name <> "` call produces:"
114+ C.CtorName name -> "This `" <> N.toString name <> "` call produces:"
115+ C.OpName _ -> thatThingIs <> ":"
116+
117+
118+
119+-- TO MISMATCH REPORT
120+
121+
122+toMismatchReport :: Code.Source -> Type.Localizer -> R.Region -> Thing -> Can.Type -> C.Expected Can.Type -> Report.Report
123+toMismatchReport source localizer exprRegion thing tipe expected =
124+ Report.Report "TYPE MISMATCH" exprRegion [] $
125+ case expected of
126+ C.NoExpectation expectedType ->
127+ Report.toCodeSnippet source exprRegion Nothing
128+ (
129+ "This expression is being used in an unexpected way:"
130+ ,
131+ typeComparison localizer tipe expectedType
132+ (
133+ toDescription thing "It is"
134+ ,
135+ "But you are trying to use it as:"
136+ ,
137+ [
138+ -- problem
252 ]
139 ]
253− )
140+ )
254−
255− If ->
256− report
257− Nothing
258− "All the branches of this `if` are consistent, but the overall\
259− \ type does not match how it is used elsewhere."
260− ( cmpHint
261− "The `if` evaluates to something of type:"
262− "Which is fine, but the surrounding context wants it to be:"
263− []
264− )
141+ )
265
142
266− ListElement elementNumber region ->
143+ C.FromAnnotation name arity subContext expectedType ->
267− report
144+ Report.toCodeSnippet source exprRegion Nothing
268− (Just region)
145+ (
269− ("The " <> ordinalPair elementNumber <> " entries in this list are different types of values.")
146+ H.reflow $
270− ( cmpHint
147+ case subContext of
271− ("The " <> ordinalize (elementNumber - 1) <> " entry has this type:")
148+ C.TypedIfBranch index ->
272− ("But the "<> ordinalize elementNumber <> " is:")
149+ "Something is off with the " <> H.ordinalize (Index.toHuman index) <> " branch of this `if` experssion:"
273− [ "Every entry in a list needs to be the same type of value.\
150+
274− \ This way you never run into unexpected values partway through.\
151+ C.TypedCaseBranch index ->
275− \ To mix different types in a single list, create a \"union type\" as\
152+ "Something is off with the " <> H.ordinalize (Index.toHuman index) <> " branch of this `case` expression:"
276− \ described in: <http://guide.elm-lang.org/types/union_types.html>"
153+
154+ C.TypedBody ->
155+ "Something is off with the body of the `" <> N.toString name <> "` definition:"
156+ ,
157+ typeComparison localizer tipe expectedType
158+ (
159+ toDescription thing $
160+ case subContext of
161+ C.TypedIfBranch index ->
162+ "The " <> H.ordinalize (Index.toHuman index) <> " branch is"
163+
164+ C.TypedCaseBranch index ->
165+ "The " <> H.ordinalize (Index.toHuman index) <> " branch is"
166+
167+ C.TypedBody ->
168+ "The body is"
169+ ,
170+ "But the type annotation on `" <> N.toString name <> "` says it should be:"
171+ ,
172+ [ error "TODO check for arity issues. Suggest that an arg is unlisted." arity
173+ --, problem
277 ]
174 ]
278− )
279−
280− List ->
281− report
282− Nothing
283− ( "All the elements in this list are the same type, but the overall\
284− \ type does not match how it is used elsewhere."
285− )
175+ )
286− ( cmpHint
287− "The list has type:"
288− "Which is fine, but the surrounding context wants it to be:"
289− []
290− )
291−
292− BinopLeft home op region ->
293− report
294− (Just region)
295− ("The left argument of (" <> op <> ") is causing a type mismatch.")
296− ( cmpHint
297− ("(" <> op <> ") is expecting the left argument to be a:")
298− "But the left argument is:"
299− (binopHint home op leftType rightType)
300− )
301−
302− BinopRight home op region ->
303− report
304− (Just region)
305− ("The right side of (" <> op <> ") is causing a type mismatch.")
306− ( cmpHint
307− ("(" <> op <> ") is expecting the right side to be a:")
308− "But the right side is:"
309− ( binopHint home op leftType rightType
310− ++
311− [ "With operators like (" <> op <> ") I always check the left\
312− \ side first. If it seems fine, I assume it is correct and check the right\
313− \ side. So the problem may be in how the left and right arguments interact."
314− ]
315− )
316− )
317−
318− Binop _ op ->
319− report
320− Nothing
321− ( "The two arguments to (" <> op <>
322− ") are fine, but the overall type of this expression\
323− \ does not match how it is used elsewhere."
324− )
325− ( cmpHint
326− "The result of this binary operation is:"
327− "Which is fine, but the surrounding context wants it to be:"
328− []
329− )
330−
331− Function maybeName ->
332− report
333− Nothing
334− ( "The return type of " <> maybeFuncName maybeName <> " is being used in unexpected ways."
335− )
336− ( cmpHint
337− "The function results in this type of value:"
338− "Which is fine, but the surrounding context wants it to be:"
339− []
340− )
341−
342− UnexpectedArg maybeName 1 1 region ->
343− report
344− (Just region)
345− ("The argument to " <> maybeFuncName maybeName <> " is causing a mismatch.")
346− ( cmpHint
347− (capitalize (maybeFuncName maybeName) <> " is expecting the argument to be:")
348− "But it is:"
349− []
350− )
176+ )
351
177
352− UnexpectedArg maybeName index _totalArgs region ->
178+ C.FromContext region context expectedType ->
353− report
179+ case context of
354− (Just region)
180+ C.ListEntry index ->
355− ( "The " <> ordinalize index <> " argument to " <> maybeFuncName maybeName
356− <> " is causing a mismatch."
357− )
358− ( cmpHint
359− ( capitalize (maybeFuncName maybeName) <> " is expecting the "
360− <> ordinalize index <> " argument to be:"
361− )
362− "But it is:"
363− ( if index == 1 then
181+ if Index.toHuman index == 2 then
364− []
182+ error "TODO 1st vs 2nd list entry"
365− else
183+ else
366− [ "I always figure out the type of arguments from left to right. If an argument\
184+ Report.toCodeSnippet source region (Just exprRegion)
367− \ is acceptable when I check it, I assume it is \"correct\" in subsequent checks.\
185+ (
368− \ So the problem may actually be in how previous arguments interact with the "
186+ H.reflow $
369− <> ordinalize index <> "."
187+ "The " <> H.ordinalize (Index.toHuman index) <> " element of this list does not match all the previous elements:"
370− ]
188+ ,
189+ typeComparison localizer tipe expectedType
190+ (
191+ toDescription thing $
192+ "The " <> H.ordinalize (Index.toHuman index) <> " element is"
193+ ,
194+ "But all the previous elements in the list are:"
195+ ,
196+ [ H.toSimpleHint $
197+ "Everything in the list needs to be the same type of value.\
198+ \ This way you never run into unexpected values partway through.\
199+ \ To mix different types in a single list, create a \"union type\" as\
200+ \ described in: <http://guide.elm-lang.org/types/union_types.html>"
201+ --, problem
202+ ]
203+ )
371 )
204 )
372− )
373
205
374− FunctionArity maybeName 0 actual region ->
206+ C.Negate ->
375− let
207+ Report.toCodeSnippet source region (Just exprRegion)
376− args =
208+ (
377− if actual == 1 then "an argument" else i2t actual <> " arguments"
209+ "I do not know how to negate this type of value:"
378−
210+ ,
379− preHint =
211+ H.stack
380− case maybeName of
212+ [ H.reflow $ toDescription thing "It is"
381− Nothing ->
213+ , Type.toDoc localizer tipe
382− "You are giving " <> args <> " to something that is not a function!"
214+ , H.fillSep
383−
215+ ["But","I","only","now","how","to","negate"
384− Just name ->
216+ ,H.dullyellow "Int","and",H.dullyellow "Float","values."
385− varName name <> " is not a function, but you are giving it " <> args <> "!"
217+ ]
386− in
218+ --, problem
387− report
219+ ]
388− (Just region)
220+ )
389− preHint
221+
390− (text "Maybe you forgot some parentheses? Or a comma?")
222+ C.OpLeft op ->
391−
223+ Report.toCodeSnippet source region (Just exprRegion) $
392− FunctionArity maybeName expected actual region ->
224+ opLeftToDocs localizer thing op tipe expectedType
393− report
225+
394− (Just region)
226+ C.OpRight op ->
395− ( capitalize (maybeFuncName maybeName) <> " is expecting "
227+ Report.toCodeSnippet source region (Just exprRegion) $
396− <> Help.args expected <> ", but was given " <> i2t actual <> "."
228+ opRightToDocs localizer thing op tipe expectedType
397− )
229+
398− (text "Maybe you forgot some parentheses? Or a comma?")
230+ C.IfCondition ->
399−
231+ Report.toCodeSnippet source region (Just exprRegion)
400− ReturnType name typeArity argArity region ->
232+ (
401− if typeArity == 0 || argArity == 0 then
233+ "This `if` condition does not evaluate to a boolean value, True or False."
402− report
234+ ,
403− (Just region)
235+ H.stack
404− ("The definition of " <> functionName name <> " does not match its type annotation.")
236+ [ H.reflow $ toDescription thing "It is"
405− ( cmpHint
237+ , Type.toDoc localizer tipe
406− ( "The type annotation for " <> functionName name <> " says it is a:"
238+ , H.fillSep ["But","I","need","this","`if`","condition","to","be","a",H.dullyellow "Bool","value."]
239+ , H.toSimpleHint $
240+ "Elm does not have “truthiness” such that ints and strings and lists\
241+ \ are automatically converted to booleans. Do that conversion explicitly!"
242+ ]
243+ )
244+
245+ C.IfBranch index ->
246+ if Index.toHuman index == 2 then
247+ error "TODO 1st vs 2nd if branches"
248+ -- error "TODO if with no chaining"
249+ else
250+ Report.toCodeSnippet source region (Just exprRegion)
251+ (
252+ H.reflow $
253+ "The " <> H.ordinalize (Index.toHuman index) <> " branch of this `if` does not match all the previous branches:"
254+ ,
255+ typeComparison localizer tipe expectedType
256+ (
257+ toDescription thing $
258+ "The " <> H.ordinalize (Index.toHuman index) <> " branch is"
259+ ,
260+ "But all the previous branches result in:"
261+ ,
262+ [ H.link "Hint"
263+ "All branches in an `if` must produce the same type of values. This way, no\
264+ \ matter which branch we take, the result is always a consistent shape. Read"
265+ "union-types"
266+ "to learn how to “mix” types."
267+ --, problem
268+ ]
269+ )
407 )
270 )
408− "But the definition (shown above) is a:"
409− (arityHint typeArity argArity)
410− )
411
271
272+ C.CaseBranch index ->
273+ if Index.toHuman index == 2 then
274+ error "TODO 1st vs 2nd case branches"
412− else
275+ else
413− report
276+ Report.toCodeSnippet source region (Just exprRegion)
414− (Just region)
277+ (
415− ("The definition of " <> functionName name <> " does not match its type annotation.")
278+ H.reflow $
416− ( cmpHint
279+ "The " <> H.ordinalize (Index.toHuman index) <> " branch of this `case` does not match all the previous branches:"
417− ( "The type annotation for " <> functionName name <> " says it always returns:"
280+ ,
281+ typeComparison localizer tipe expectedType
282+ (
283+ toDescription thing $
284+ "The " <> H.ordinalize (Index.toHuman index) <> " branch is"
285+ ,
286+ "But all the previous branches result in:"
287+ ,
288+ [ H.link "Hint"
289+ "All branches in a `case` must produce the same type of values. This way, no\
290+ \ matter which branch we take, the result is always a consistent shape. Read"
291+ "union-types"
292+ "to learn how to “mix” types."
293+ --, problem
294+ ]
295+ )
418 )
296 )
419− "But the returned value (shown above) is a:"
420− (arityHint typeArity argArity)
421− )
422−
423− Instance var ->
424− let
425− name =
426− functionName var
427− in
428− report
429− Nothing
430− (name <> " is being used in an unexpected way.")
431− ( cmpHint
432− ("Based on its definition, " <> name <> " has this type:")
433− "But you are trying to use it as:"
434− []
435− )
436−
437− Literal name ->
438− report
439− Nothing
440− ( "This " <> name <> " value is being used as if it is some other type of value."
441− )
442− ( cmpHint
443− ("The " <> name <> " definitely has this type:")
444− ("But it is being used as:")
445− []
446− )
447
297
448− Pattern context category ->
298+ C.CallArg maybeFuncName index ->
449− let (overview, lineOne, lineTwo) = toPatternMessage context category in
299+ let
450− report Nothing overview (cmpHint lineOne lineTwo [])
300+ thisFunction =
451−
301+ case maybeFuncName of
452− PatternCons ->
302+ C.NoName ->
453− report Nothing "This list pattern is mixing types:" $
303+ "this function"
454− cmpHint
304+
455− "The head of the list matches this type of value:"
305+ C.FuncName name ->
456− "But the tail of the list matches:"
306+ "`" <> N.toString name <> "`"
457− []
307+
458−
308+ C.CtorName name ->
459− Shader ->
309+ "`" <> N.toString name <> "`"
460− report
310+
461− Nothing
311+ C.OpName op ->
462− "There is some problem with this GLSL shader."
312+ "(" <> N.toString op <> ")"
463− ( cmpHint
313+
464− "The shader block has this type:"
314+ in
465− "Which is fine, but the surrounding context wants it to be:"
315+ Report.toCodeSnippet source region (Just exprRegion)
316+ (
317+ H.reflow $
318+ "The " <> H.ordinalize (Index.toHuman index) <> " argument to " <> thisFunction <> " is not what I expect:"
319+ ,
320+ typeComparison localizer tipe expectedType
321+ (
322+ toDescription thing "This argument is"
323+ ,
324+ "But " <> thisFunction <> " needs the " <> H.ordinalize (Index.toHuman index)
325+ <> " argument to be:"
326+ ,
327+ if Index.toHuman index == 1 then
328+ []
329+ else
330+ [ H.toSimpleHint $
331+ "I always figure out the argument types from left to right. If an argument\
332+ \ is acceptable, I assume it is “correct” and move on. So the problem may\
333+ \ actually be in one of the previous arguments!"
334+ ]
335+ )
336+ )
337+
338+ C.TooManyArgs maybeName numGivenArgs ->
339+ case length (Type.delambda tipe) - 1 of
340+ 0 ->
341+ Report.toCodeSnippet source region (Just exprRegion)
342+ (
343+ H.reflow $
344+ case maybeName of
345+ C.NoName ->
346+ "You are giving " <> H.args numGivenArgs <> " to something that is not a function!"
347+
348+ C.FuncName name ->
349+ "`" <> N.toString name <> "` is not a function, but you are giving it " <> H.args numGivenArgs <> "!"
350+
351+ C.CtorName name ->
352+ "`" <> N.toString name <> "` is not a function, but you are giving it " <> H.args numGivenArgs <> "!"
353+
354+ C.OpName op ->
355+ "(" <> N.toString op <> ") is not a function, but you are giving it " <> H.args numGivenArgs <> "!"
356+ ,
357+ typeComparison localizer tipe expectedType
358+ (
359+ case maybeName of
360+ C.NoName ->
361+ "It is a value of type:"
362+
363+ C.FuncName name ->
364+ "`" <> N.toString name <> "` is a value of type:"
365+
366+ C.CtorName name ->
367+ "`" <> N.toString name <> "` is a constructor of type:"
368+
369+ C.OpName op ->
370+ "(" <> N.toString op <> ") is a value of type:"
371+ ,
372+ "But you are giving it " <> H.args numGivenArgs <> " as if its type was:"
373+ ,
374+ [ "Maybe you forgot some parentheses? Or a comma?"
375+ ]
376+ )
377+ )
378+
379+ numExpectedArgs ->
380+ let
381+ thisFunction =
382+ case maybeName of
383+ C.NoName ->
384+ "This function"
385+
386+ C.FuncName name ->
387+ "The `" <> N.toString name <> "` function"
388+
389+ C.CtorName name ->
390+ "The `" <> N.toString name <> "` constructor"
391+
392+ C.OpName op ->
393+ "The (" <> N.toString op <> ") function"
394+ in
395+ Report.toCodeSnippet source region (Just exprRegion)
396+ (
397+ H.reflow $
398+ thisFunction <> " is getting too many arguments:"
399+ ,
400+ typeComparison localizer tipe expectedType
401+ (
402+ thisFunction <> " is expecting to get " <> H.args numExpectedArgs <> " like this:"
403+ ,
404+ "But you are giving it " <> H.args numGivenArgs <> " as if its type was:"
405+ ,
406+ [ "Maybe you forgot some parentheses? Or a comma?"
407+ , H.toSimpleNote $
408+ "I am not good at figuring out which arguments are extra myself. The\
409+ \ problem may even be that the FUNCTION is defined funny, and the arguments are\
410+ \ actually fine. Anyway, I hope I gave enough info to help you figure it out!"
466− []
411+ ]
467− )
412+ )
468−
413+ )
469− Lambda ->
414+
470− report
415+ C.RecordAccess field ->
471− Nothing
416+ Report.toCodeSnippet source region (Just exprRegion) $
472− "This function is being used in an unexpected way."
417+ case Type.deepDealias tipe of
473− ( cmpHint
418+ Can.TRecord _ _ ->
474− "The function has type:"
419+ (
475− "But you are trying to use it as:"
420+ H.reflow $
421+ "This record does not have a `" <> N.toString field <> "` field:"
422+ ,
423+ H.stack $
424+ [ H.reflow $
425+ "It is a record with this type:"
426+ , Type.toDoc localizer tipe
427+ , H.reflow $
428+ "So maybe the ." <> N.toString field <> " field access is misspelled?"
429+ ]
430+ ++
431+ case error "TODO closeNames" of
432+ [] ->
433+ []
434+
476− []
435+ [name] ->
436+ [ H.toFancyHint $
437+ ["It","may","also","be","a","typo","in","the","record","itself."
438+ ,"Perhaps","the",H.dullyellow name,"field","is","misspelled?"
439+ ]
440+ ]
441+
442+ names ->
443+ [ H.toFancyHint $
444+ ["It","may","also","be","a","typo","in","the","record","itself."
445+ ,"Perhaps","the"
446+ ]
447+ ++ H.commaSep "or" H.dullyellow names
448+ ++ ["field","is","misspelled?"]
449+ ]
450+ )
451+
452+ Can.TLambda _ _ ->
453+ (
454+ H.reflow $
455+ "I cannot access field `" <> N.toString field <> "` of this " <> whatever <> ":"
456+ ,
457+ H.stack
458+ [ H.reflow $
459+ "The " <> whatever <> " is not a record! It is actually a function:"
460+ , Type.toDoc localizer tipe
461+ , H.toSimpleHint "Maybe it is missing some arguments?"
462+ ]
463+ )
464+
465+ _ ->
466+ (
467+ H.reflow $
468+ "I cannot access field `" <> N.toString field <> "` of this " <> whatever <> ":"
469+ ,
470+ H.stack
471+ [ H.reflow $
472+ "The " <> whatever <> " is not a record though! It is a:"
473+ , Type.toDoc localizer tipe
474+ ]
475+ )
476+
477+ C.RecordUpdate ->
478+ case Type.deepDealias tipe of
479+ Can.TRecord _ _ ->
480+ Report.toCodeSnippet source region Nothing
481+ (
482+ H.reflow $
483+ "I think there is a typo in one of these field names:"
484+ ,
485+ error "TODO record update field name typo"
486+ )
487+
488+ _ ->
489+ Report.toCodeSnippet source region (Just exprRegion)
490+ (
491+ "The record update syntax does not work with this value:"
492+ ,
493+ H.stack
494+ [ H.reflow $
495+ "I can only update records, but this appears to be a " <> whatever <> ":"
496+ , Type.toDoc localizer tipe
497+ ]
498+ )
499+
500+ C.Destructure ->
501+ error "TODO Destructure"
502+
503+
504+
505+-- OP LEFT
506+
507+
508+opLeftToDocs :: Type.Localizer -> Thing -> N.Name -> Can.Type -> Can.Type -> (H.Doc, H.Doc)
509+opLeftToDocs localizer thing op tipe expectedType =
510+ case op of
511+ "+"
512+ | isString tipe -> badStringAdd
513+ | isList tipe -> badListAdd localizer "left" tipe
514+ | otherwise -> badMath localizer "Addition" "left" "+" tipe []
515+
516+ "*"
517+ | isList tipe -> badListMul localizer "left" tipe
518+ | otherwise -> badMath localizer "Multiplication" "left" "*" tipe []
519+
520+ "-" -> badMath localizer "Subtraction" "left" "-" tipe []
521+ "^" -> badMath localizer "Exponentiation" "left" "^" tipe []
522+ "/" -> badFDiv localizer "left" tipe
523+ "//" -> badIDiv localizer "left" tipe
524+ "&&" -> badBool localizer "&&" "left" tipe
525+ "||" -> badBool localizer "||" "left" tipe
526+ "<" -> badCompArg localizer thing "<" "left" tipe
527+ ">" -> badCompArg localizer thing ">" "left" tipe
528+ "<=" -> badCompArg localizer thing "<=" "left" tipe
529+ ">=" -> badCompArg localizer thing ">=" "left" tipe
530+
531+ "<|" ->
532+ ( "The left side of (<|) needs to be a function so I can pipe arguments to it!"
533+ ,
534+ H.stack
535+ [ H.reflow $
536+ "Instead of a function, I am seeing " <> whatever <> ":"
537+ , Type.toDoc localizer tipe
538+ , error "TODO"
539+ {-
540+ case category of
541+ C.Call ->
542+ "Maybe you gave " <> funcName <> " too many arguments already?"
543+
544+ _ ->
545+ []
546+ -}
547+ ]
477− )
548+ )
478
549
479− Accessor field ->
550+ _ ->
480− report Nothing
551+ (
481− ("The ." <> field <> " accessor is being used in an unexpected way.")
552+ H.reflow $
482− ( cmpHint
553+ "The left argument of (" <> N.toString op <> ") is causing problems:"
483− "It has type:"
554+ ,
484− "But you are trying to use it as:"
555+ typeComparison localizer tipe expectedType
556+ (
557+ toDescription thing "The left argument is"
558+ ,
559+ "But (" <> N.toString op <> ") needs the left argument to be:"
560+ ,
485 []
561 []
486− )
487−
488− Access (Just body) field ->
489− let
490− header = "`" <> body <> "` does not have a field named `" <> field <> "`."
491− in
492− report Nothing header $ stack $
493− [ reflowParagraph $ "The type of `" <> body <> "` is:"
494− , indent 4 $ dullyellow $ RenderType.toDoc localizer leftType
495− , reflowParagraph $ "Which does not contain a field named `" <> field <> "`."
496− ]
497− ++ typicalHints
498−
499− Access Nothing field ->
500− let
501− header = "Cannot access a field named `" <> field <> "`."
502− in
503− report Nothing header $ stack $
504− [ reflowParagraph $ "You are trying to get `" <> field <> "` from a value with this type:"
505− , indent 4 $ dullyellow $ RenderType.toDoc localizer leftType
506− , reflowParagraph $ "It is not in there!"
507− ]
508− ++ typicalHints
509−
510− Record ->
511− report
512− Nothing
513− "This record is being used in an unexpected way."
514− ( cmpHint
515− "The record has type:"
516− "But you are trying to use it as:"
517− []
518 )
562 )
563+ )
519
564
520− Unit ->
521− report
522− Nothing
523− "This “unit” value is being used in an unexpected way."
524− ( cmpHint
525− "It has type:"
526− "But you are trying to use it as:"
527− []
528− )
529
565
530− Tuple ->
531− report
532− Nothing
533− "This tuple is being used in an unexpected way."
534− ( cmpHint
535− "The tuple has type:"
536− "But you are trying to use it as:"
537− []
538− )
539
566
540− Manager name ->
567+-- OP RIGHT
541− report
568+
542− Nothing
569+
543− ("The `" <> name <> "` in your effect manager has a weird type.")
570+opRightToDocs :: Type.Localizer -> Thing -> N.Name -> Can.Type -> Can.Type -> (H.Doc, H.Doc)
544− ( cmpHint
571+opRightToDocs localizer thing op tipe expectedType =
545− ("Your `" <> name <> "` function has this type:")
572+ case op of
546− "But it needs to have a type like this:"
573+ "+"
547− [ "You can read more about setting up effect managers properly here:\
574+ | isInt tipe -> badCast op FloatInt
548− \ <http://guide.elm-lang.org/effect_managers/>"
575+ | isFloat tipe -> badCast op IntFloat
576+ | isString tipe -> badStringAdd
577+ | isList tipe -> badListAdd localizer "right" tipe
578+ | otherwise -> badMath localizer "Addition" "right" "+" tipe []
579+
580+ "*"
581+ | isInt tipe -> badCast op FloatInt
582+ | isFloat tipe -> badCast op IntFloat
583+ | isList tipe -> badListMul localizer "right" tipe
584+ | otherwise -> badMath localizer "Multiplication" "right" "*" tipe []
585+
586+ "-"
587+ | isInt tipe -> badCast op FloatInt
588+ | isFloat tipe -> badCast op IntFloat
589+ | otherwise -> badMath localizer "Subtraction" "right" "-" tipe []
590+
591+ "^"
592+ | isInt tipe -> badCast op FloatInt
593+ | isFloat tipe -> badCast op IntFloat
594+ | otherwise -> badMath localizer "Exponentiation" "right" "^" tipe []
595+
596+ "/" -> badFDiv localizer "right" tipe
597+ "//" -> badIDiv localizer "right" tipe
598+ "&&" -> badBool localizer "&&" "right" tipe
599+ "||" -> badBool localizer "||" "right" tipe
600+ "<" -> badCompRight localizer thing "<" tipe expectedType
601+ ">" -> badCompRight localizer thing ">" tipe expectedType
602+ "<=" -> badCompRight localizer thing "<=" tipe expectedType
603+ ">=" -> badCompRight localizer thing ">=" tipe expectedType
604+ "==" -> badEquality localizer "==" tipe expectedType
605+ "/=" -> badEquality localizer "/=" tipe expectedType
606+
607+ "<|" ->
608+ (
609+ H.reflow $
610+ "I cannot pipe this " <> whatever <> " in as the final argument:"
611+ ,
612+ typeComparison localizer tipe expectedType
613+ (
614+ toDescription thing "You are providing"
615+ ,
616+ "But (<|) is piping it a function that expects:"
617+ ,
618+ [ "Maybe an argument is missing?"
619+-- , problem
549 ]
620 ]
621+ )
550− )
622+ )
551
623
552− State name ->
624+ "|>" ->
553− report
625+ case (tipe, expectedType) of
626+ (Can.TLambda expectedArgType _, Can.TLambda argType _) ->
627+ (
628+ "I cannot pipe the argument through to this function:"
629+ ,
630+ typeComparison localizer argType expectedArgType
631+ (
632+ "You are providing an argument of type:"
633+ ,
634+ "But (|>) is piping it a function that expects its next argument to be:"
635+ {-
636+ case maybeName of
554− Nothing
637+ Nothing ->
555− ( "Your effect manager creates a certain type of state with `init`, but your `"
638+ "But (|>) is piping it a function that expects the next argument to be:"
556− <> name <> "` function expects a different kind of state."
639+
557− )
640+ Just (C.FuncName name) ->
558− ( cmpHint
641+ "But (|>) is piping it to `" <> N.toString name <> "` which expects the next argument to be:"
559− "The state created by `init` has this type:"
642+
560− ("But `" <> name <> "` expects state of this type:")
643+ Just (C.OpName op) ->
561− [ "Make the two state types match and you should be all set! More info here:\
644+ "But (|>) is piping it to (" <> N.toString op <> ") which expects the next argument to be:"
562− \ <http://guide.elm-lang.org/effect_managers/>"
645+ -}
646+ ,
647+ [ "Maybe an argument is missing?"
648+-- , problem
649+ ]
650+ )
651+ )
652+
653+ _ ->
654+ (
655+ "The right side of (|>) needs to be a function so I can pipe arguments to it!"
656+ ,
657+ H.stack
658+ [ H.reflow $
659+ "Instead of a function, I am seeing " <> whatever <> ":"
660+ , Type.toDoc localizer tipe
661+ , error "TODO"
662+ {-
663+ case category of
664+ Call ->
665+ "Maybe you gave " <> funcName <> " too many arguments already?"
666+
667+ _ ->
668+ []
669+ -}
563− ]
670+ ]
564− )
671+ )
565
672
566− SelfMsg ->
673+ _ ->
567− report
674+ (
568− Nothing
675+ H.reflow $
569− "Effect managers can send messages to themselves, but `onEffects` and `onSelfMsg` are defined for different types of self messages."
676+ "The right argument of (" <> N.toString op <> ") is causing problems."
570− ( cmpHint
677+ ,
571− "The `onEffects` function can send this type of message:"
678+ typeComparison localizer tipe expectedType
572− "But the `onSelfMsg` function receives this type:"
679+ (
573− [ "Make the two message types match and you should be all set! More info here:\
680+ toDescription thing "The right argument is"
574− \ <http://guide.elm-lang.org/effect_managers/>"
681+ ,
682+ "But (" <> N.toString op <> ") needs the right argument to be:"
683+ ,
684+ error "TODO"
685+ {-
686+ [ problem
687+ , binopHint home op leftType rightType
575 ]
688 ]
689+ -}
576− )
690+ )
577−
691+ )
578
692
579
693
580−-- PATTERN REPORT
581
694
695+isInt :: Can.Type -> Bool
696+isInt tipe =
697+ error "TODO isInt" tipe
582
698
583−toPatternMessage :: PatternContext -> PatternCategory -> (Text, Text, Text)
584−toPatternMessage context category =
585− case context of
586− PatternArg name index ->
587− toPatternHelp category $
588− "But as the " <> ordinalize index
589− <> " value of a `" <> name <> "` pattern, it must be:"
590
699
591− PatternTail ->
700+isFloat :: Can.Type -> Bool
592− toPatternHelp category $
701+isFloat tipe =
593− "But as the pattern after a (::), it must be a list like: "
702+ error "TODO isFloat" tipe
594
703
595− PatternList index ->
596− ( "The patterns in this list match different types of values:"
597− , "The " <> ordinalize index <> " pattern matches type:"
598− , "But all the previous patterns in the list match:"
599− )
600
704
601− PatternUnknown ->
705+isString :: Can.Type -> Bool
602− toPatternHelp category $
706+isString tipe =
603− "But the values flowing through here need to be:"
707+ error "TODO isString" tipe
604
708
605
709
606−toPatternHelp :: PatternCategory -> Text -> (Text, Text, Text)
710+isList :: Can.Type -> Bool
607−toPatternHelp category lineTwo =
711+isList tipe =
608− case category of
712+ error "TODO isList" tipe
609− PRecord ->
610− ( "This record pattern will cause problems:"
611− , "It matches record values with this type:"
612− , lineTwo
613− )
614
713
615− PUnit ->
616− ( "This “unit” pattern will cause problems:"
617− , "It matches unit values with this type:"
618− , lineTwo
619− )
620
714
621− PTuple ->
715+whatever :: String
622− ( "This tuple pattern will cause problems:"
716+whatever =
623− , "It matches tuples with this type:"
717+ error "TODO whatever"
624− , lineTwo
625− )
626
718
627− PList ->
628− ( "This list pattern will cause problems:"
629− , "It matches lists with this type:"
630− , lineTwo
631− )
632
719
633− PCtor name ->
634− ( "This `" <> name <> "` value will cause problems:"
635− , "It matches `" <> name <> "` values with this type:"
636− , lineTwo
637− )
638
720
721+-- BAD MATH
639
722
640
723
641−-- COMPARISON HINT
724+data ThisThenThat = FloatInt | IntFloat
642−
643−
644−comparisonHint
645− :: RenderType.Localizer
646− -> Type.Canonical
647− -> Type.Canonical
648− -> Text
649− -> Text
650− -> [Doc]
651− -> Doc
652−comparisonHint localizer leftType rightType leftWords rightWords finalHints =
653− let
654− (leftDoc, rightDoc) =
655− RenderType.diffToDocs localizer leftType rightType
656− in
657− stack $
658− [ reflowParagraph leftWords
659− , indent 4 leftDoc
660− , reflowParagraph rightWords
661− , indent 4 rightDoc
662− ]
663− ++
664− finalHints
665
725
666
726
727+badCast :: N.Name -> ThisThenThat -> (H.Doc, H.Doc)
728+badCast op thisThenThat =
729+ (
730+ H.text $
731+ "I need both sides of (" <> N.toString op <> ") to be the exact same type. Both Int or both Float."
732+ ,
733+ let
734+ anInt = ["an", H.dullyellow "Int"]
735+ aFloat = ["a", H.dullyellow "Float"]
736+ toFloat = H.green "toFloat"
737+ round = H.green "round"
738+ in
739+ case thisThenThat of
740+ FloatInt ->
741+ badCastHelp aFloat anInt round toFloat
667
742
668−-- JSON DECODER HINT
743+ IntFloat ->
669−
744+ badCastHelp anInt aFloat toFloat round
670−
745+ )
671−jsonDecoderHint :: Doc
672−jsonDecoderHint =
673− Help.toSimpleHint $
674− "New to JSON decoders? They are tough at first, so DEFINITELY learn more here: "
675− <> Help.hintLink "json-decoders"
676−
677−
678
746
679−-- BINOP HINTS
680
747
748+badCastHelp :: [H.Doc] -> [H.Doc] -> H.Doc -> H.Doc -> H.Doc
749+badCastHelp anInt aFloat toFloat round =
750+ H.stack
751+ [ H.fillSep $
752+ ["But","I","see"]
753+ ++ anInt
754+ ++ ["on","the","left","and"]
755+ ++ aFloat
756+ ++ ["on","the","right."]
757+ , H.fillSep
758+ ["Use",toFloat,"on","the","left","(or",round,"on"
759+ ,"the","right)","to","make","both","sides","match!"
760+ ]
761+ , H.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats."
762+ ]
681
763
682−binopHint :: ModuleName.Canonical -> N.Name -> Type.Canonical -> Type.Canonical -> [Text]
683−binopHint home op leftType rightType =
684− let
685− leftString = show (RenderType.toDoc Map.empty leftType)
686− rightString = show (RenderType.toDoc Map.empty rightType)
687− in
688− if home == ModuleName.basics && op == "+" && elem "String" [leftString, rightString] then
689− [ "To append strings in Elm, you need to use the (++) operator, not (+).\
690− \ <http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#++>"
691− ]
692
764
693− else if home == ModuleName.basics && op == "/" && elem "Int" [leftString, rightString] then
765+badStringAdd :: (H.Doc, H.Doc)
694− [ "The (/) operator is specifically for floating point division, and (//) is\
766+badStringAdd =
695− \ for integer division. You may need to do some conversions between ints and\
767+ (
696− \ floats to get both arguments matching the division operator you want."
768+ H.fillSep ["I","cannot","do","addition","with",H.dullyellow "String","values","like","this","one:"]
769+ ,
770+ H.stack
771+ [ H.fillSep
772+ ["The","(+)","operator","only","works","with",H.dullyellow "Int","and",H.dullyellow "Float","values."
773+ ]
774+ , H.toFancyHint
775+ ["Switch","to","the",H.green "(++)","operator","to","append","strings!"
776+ ]
697 ]
777 ]
778+ )
698
779
699− else
700− []
701−
702−
703−
704−-- ARITY HINTS
705
780
781+badListAdd :: Type.Localizer -> String -> Can.Type -> (H.Doc, H.Doc)
782+badListAdd localizer direction tipe =
783+ (
784+ "I cannot do addition with lists:"
785+ ,
786+ H.stack
787+ [ H.reflow $
788+ "The " <> direction <> " side of (+) is " <> whatever <> ":"
789+ , Type.toDoc localizer tipe
790+ , H.fillSep
791+ ["But","(+)","only","works","with",H.dullyellow "Int","and",H.dullyellow "Float","values."
792+ ]
793+ , H.toFancyHint
794+ ["Switch","to","the",H.green "(++)","operator","to","append","lists!"
795+ ]
796+ ]
797+ )
706
798
707−arityHint :: Int -> Int -> [Text]
708−arityHint typeArity argArity =
709− if typeArity == argArity then
710− []
711
799
712− else
800+badListMul :: Type.Localizer -> String -> Can.Type -> (H.Doc, H.Doc)
713− [ "The type annotation says there " <> sayArgs typeArity <>
801+badListMul localizer direction tipe =
714− ", but there " <> sayArgs argArity <>
802+ badMath localizer "Multiplication" direction "*" tipe
715− " named in the definition. It is best practice for each argument\
803+ [
716− \ in the type to correspond to a named argument in the definition,\
804+ H.toFancyHint
717− \ so try that first!"
805+ [ "Maybe", "you", "want"
806+ , H.green "List.repeat"
807+ , "to", "build","a","list","of","repeated","values?"
808+ ]
718 ]
809 ]
719
810
720
811
721−sayArgs :: Int -> Text
812+badMath :: Type.Localizer -> String -> String -> String -> Can.Type -> [H.Doc] -> (H.Doc, H.Doc)
722−sayArgs n =
813+badMath localizer operation direction op tipe otherHints =
723− case n of
814+ (
724− 0 ->
815+ H.reflow $
725− "are NO arguments"
816+ operation ++ " does not work with this value:"
726−
817+ ,
727− 1 ->
818+ H.stack $
728− "is 1 argument"
819+ [ H.reflow $
729−
820+ "The " <> direction <> " side of (" <> op <> ") is a " <> whatever <> ":"
730− _ ->
821+ , Type.toDoc localizer tipe
731− "are " <> i2t n <> " arguments"
822+ , H.fillSep
732−
823+ ["But","(" <> H.text op <> ")","only","works","with"
733−
824+ ,H.dullyellow "Int","and",H.dullyellow "Float","values."
734−
825+ ]
735−-- MISMATCH HELPERS
826+ , error "TODO check for missing arguments"
736−
827+ ]
737−
828+ ++ otherHints
738−ordinalPair :: Int -> Text
829+ )
739−ordinalPair number =
740− ordinalize (number - 1) <> " and " <> ordinalize number
741−
742−
743−varName :: Var.Canonical -> Text
744−varName (Var.Canonical _ opName) =
745− functionName opName
746−
747−
748−maybeFuncName :: Maybe Var.Canonical -> Text
749−maybeFuncName maybeVar =
750− case maybeVar of
751− Nothing ->
752− "this function"
753−
754− Just var ->
755− "function " <> varName var
756−
757−
758−
759−-- MISMTACH REASONS
760−
761−
762−flipReason :: Reason -> Reason
763−flipReason reason =
764− case reason of
765− BadFields fields ->
766− BadFields (map (second (fmap flipReason)) fields)
767−
768− MessyFields both left right ->
769− MessyFields both right left
770−
771− IntFloat ->
772− IntFloat
773−
774− MissingArgs num ->
775− MissingArgs num
776−
777− RigidClash a b ->
778− RigidClash b a
779−
780− NotPartOfSuper super ->
781− NotPartOfSuper super
782−
783− RigidVarTooGeneric name specific ->
784− RigidVarTooGeneric name specific
785−
786− RigidSuperTooGeneric super name specific ->
787− RigidSuperTooGeneric super name specific
788−
789−
790−reasonToDoc :: Reason -> Doc
791−reasonToDoc reason =
792− case collectFields reason of
793− Nothing ->
794− let
795− (firstLine, details) =
796− reasonToDocHelp reason
797− in
798− Help.vcat $ Help.toSimpleHint firstLine : map (indent 4) details
799−
800− Just (fields, maybeDeepReason) ->
801− let
802− recordHint elaboration =
803− [ "Problem", "in", "the", dullyellow (text (Text.intercalate "." fields)) <> "field." ]
804− ++
805− map text (Text.words elaboration)
806− in
807− case reasonToDocHelp <$> maybeDeepReason of
808− Nothing ->
809− Help.toFancyHint (recordHint badFieldElaboration)
810−
811− Just (firstLine, details) ->
812− Help.vcat $ Help.toFancyHint (recordHint firstLine) : map (indent 4) details
813−
814−
815−
816−-- COLLECT FIELDS
817−
818−
819−collectFields :: Reason -> Maybe ([Text], Maybe Reason)
820−collectFields reason =
821− case reason of
822− BadFields [(field, maybeReason)] ->
823− Just (collectFieldsHelp [field] maybeReason)
824−
825− _ ->
826− Nothing
827−
828−
829−collectFieldsHelp :: [Text] -> Maybe Reason -> ([Text], Maybe Reason)
830−collectFieldsHelp revFields maybeReason =
831− case maybeReason of
832− Just (BadFields [(field, deeperReason)]) ->
833− collectFieldsHelp (field : revFields) deeperReason
834−
835− _ ->
836− ( reverse revFields, maybeReason )
837−
838−
839−reasonToDocHelp :: Reason -> (Text, [Doc])
840−reasonToDocHelp reason =
841− let
842− simpleDoc msg =
843− ( msg, [] )
844− in
845− case reason of
846− BadFields fields ->
847− simpleDoc $
848− "I am seeing issues with the "
849− <> Help.commaSep (map fst fields) <> " fields. "
850− <> badFieldElaboration
851−
852− MessyFields both leftOnly rightOnly ->
853− messyFieldsHelp both leftOnly rightOnly
854−
855− IntFloat ->
856− simpleDoc $
857− "Elm does not automatically convert between Ints and Floats. Use\
858− \ `toFloat` and `round` to do specific conversions.\
859− \ <http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#toFloat>"
860−
861− MissingArgs num ->
862− simpleDoc $
863− "It looks like a function needs " <> Help.moreArgs num <> "."
864−
865− RigidClash name1 name2 ->
866− simpleDoc $
867− "Your type annotation uses `" <> name1 <> "` and `" <> name2 <>
868− "` as DIFFERENT type variables. By using separate names, you are\
869− \ claiming the values can be different, but the code suggests that\
870− \ they must be the SAME based on how they are used. Maybe these two\
871− \ type variables in your type annotation should just be one? Maybe\
872− \ your code uses them in a weird way? More help at: "
873− <> Help.hintLink "type-annotations"
874−
875− NotPartOfSuper Type.Number ->
876− simpleDoc "Only ints and floats are numbers."
877−
878− NotPartOfSuper Type.Comparable ->
879− simpleDoc "Only ints, floats, chars, strings, lists, and tuples are comparable."
880−
881− NotPartOfSuper Type.Appendable ->
882− simpleDoc "Only strings and lists are appendable."
883−
884− NotPartOfSuper Type.CompAppend ->
885− simpleDoc "Only strings and lists are both comparable and appendable."
886−
887− RigidVarTooGeneric name specific ->
888− simpleDoc $ rigidTooGenericHelp "ANY type of value" name specific
889−
890− RigidSuperTooGeneric Type.Number name specific ->
891− simpleDoc $ rigidTooGenericHelp "BOTH Ints and Floats" name specific
892−
893− RigidSuperTooGeneric Type.Comparable name specific ->
894− simpleDoc $ rigidTooGenericHelp "ANY comparable value" name specific
895−
896− RigidSuperTooGeneric Type.Appendable name specific ->
897− simpleDoc $ rigidTooGenericHelp "BOTH Strings and Lists" name specific
898−
899− RigidSuperTooGeneric Type.CompAppend name specific ->
900− simpleDoc $ rigidTooGenericHelp "Strings and ANY comparable List" name specific
901−
902−
903−rigidTooGenericHelp :: Text -> Text -> SpecificThing -> Text
904−rigidTooGenericHelp manyTypesOfValues name specific =
905− "The type variable `" <> name <> "` in your type annotation suggests that"
906− <> manyTypesOfValues <> " can flow through, but the code suggests that it must be "
907− <> specificThingToText specific <> " based on how it is used. Maybe make your\
908− \ type annotation more specific? Maybe the code has a problem? More help at: "
909− <> Help.hintLink "type-annotations"
910−
911−
912−specificThingToText :: SpecificThing -> Text
913−specificThingToText specific =
914− case specific of
915− SpecificSuper Type.Number ->
916− "a number"
917−
918− SpecificSuper Type.Comparable ->
919− "comparable"
920−
921− SpecificSuper Type.Appendable ->
922− "appendable"
923−
924− SpecificSuper Type.CompAppend ->
925− "comparable AND appendable"
926
830
927− SpecificType name ->
928− if Text.isInfixOf (Text.take 1 name) "AEIOU" then
929− "an " <> name
930
831
832+badFDiv :: Type.Localizer -> H.Doc -> Can.Type -> (H.Doc, H.Doc)
833+badFDiv localizer direction tipe =
834+ (
835+ H.reflow $
836+ "The (/) operator is specifically for floating-point division:"
837+ ,
838+ if isInt tipe then
839+ H.stack
840+ [ H.fillSep
841+ ["The",direction,"side","of","(/)","must","be","a"
842+ ,H.dullyellow "Float" <> ","
843+ ,"but","I","am","seeing","an",H.dullyellow "Int" <> "."
844+ ,"I","recommend:"
845+ ]
846+ , H.vcat
847+ [ H.green "toFloat" <> " for explicit conversions " <> H.black "(toFloat 5 / 2) == 2.5"
848+ , H.green "(//) " <> " for integer division " <> H.black "(5 // 2) == 2"
849+ ]
850+ , H.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats."
851+ ]
852+
931− else
853+ else
932− "a " <> name
854+ H.stack
855+ [ H.fillSep
856+ ["The",direction,"side","of","(/)","must","be","a"
857+ ,H.dullyellow "Float" <> ","
858+ ,"but","instead","I","am","seeing:"
859+ ]
860+ , Type.toDoc localizer tipe
861+ ]
862+ )
933
863
934− SpecificRecord ->
935− "a record"
936
864
937− SpecificFunction ->
865+badIDiv :: Type.Localizer -> H.Doc -> Can.Type -> (H.Doc, H.Doc)
938− "a function"
866+badIDiv localizer direction tipe =
867+ (
868+ H.reflow $
869+ "The (//) operator is specifically for integer division:"
870+ ,
871+ if isFloat tipe then
872+ H.stack
873+ [ H.fillSep
874+ ["The",direction,"side","of","(//)","must","be","an"
875+ ,H.dullyellow "Int" <> ","
876+ ,"but","I","am","seeing","a",H.dullyellow "Float" <> "."
877+ ,"I","recommend","doing","the","conversion","explicitly"
878+ ,"with","one","of","these","functions:"
879+ ]
880+ , H.vcat
881+ [ H.green "round" <> " 3.5 == 4"
882+ , H.green "floor" <> " 3.5 == 3"
883+ , H.green "ceiling" <> " 3.5 == 4"
884+ , H.green "truncate" <> " 3.5 == 3"
885+ ]
886+ , H.link "Note" "Read" "implicit-casts" "to learn why Elm does not implicitly convert Ints to Floats."
887+ ]
888+ else
889+ H.stack
890+ [ H.fillSep
891+ ["The",direction,"side","of","(//)","must","be","an"
892+ ,H.dullyellow "Int" <> ","
893+ ,"but","instead","I","am","seeing:"
894+ ]
895+ , Type.toDoc localizer tipe
896+ ]
897+ )
939
898
940− SpecificUnit ->
941− "a unit value"
942
899
943− SpecificTuple ->
944− "a tuple"
945
900
901+-- BAD BOOLS
946
902
947−badFieldElaboration :: Text
948−badFieldElaboration =
949− "I always figure out field types in alphabetical order. If a field\
950− \ seems fine, I assume it is “correct” in subsequent checks.\
951− \ So the problem may actually be a weird interaction with previous fields."
952
903
904+badBool :: Type.Localizer -> H.Doc -> H.Doc -> Can.Type -> (H.Doc, H.Doc)
905+badBool localizer op direction tipe =
906+ (
907+ H.reflow $
908+ "I am struggling with this boolean operation:"
909+ ,
910+ H.stack
911+ [ H.fillSep
912+ ["Both","sides","of","(" <> op <> ")","must","be"
913+ ,H.dullyellow "Bool","values,","but","the",direction,"side","is:"
914+ ]
915+ , Type.toDoc localizer tipe
916+ , H.toSimpleHint
917+ "Elm does not have “truthiness” such that ints and strings and lists are\
918+ \ automatically converted to booleans. Do that conversion explicitly!"
919+ ]
920+ )
953
921
954−messyFieldsHelp :: [Text] -> [Text] -> [Text] -> (Text, [Doc])
955−messyFieldsHelp both leftOnly rightOnly =
956− error "TODO messyFieldsHelp" both leftOnly rightOnly
957
922
958−{-
959− case (leftOnly, rightOnly) of
960− ([], [missingField]) ->
961− oneMissingField both missingField
962
923
963− ([missingField], []) ->
924+-- BAD COMPARISON
964− oneMissingField both missingField
965
925
966− ([], missingFields) ->
967− manyMissingFields both missingFields
968
926
969− (missingFields, []) ->
927+badCompRight :: Type.Localizer -> Thing -> String -> Can.Type -> Can.Type -> (H.Doc, H.Doc)
970− manyMissingFields both missingFields
928+badCompRight localizer thing op tipe expectedType =
929+ case tipe of
930+ Can.TVar name | N.startsWith "comparable" name ->
931+ badCompArg localizer thing op "right" tipe
971
932
972 _ ->
933 _ ->
973− let
934+ badCompMix localizer op tipe expectedType
974− typoPairs =
935+
975− case Help.findTypoPairs leftOnly rightOnly of
936+
976− [] ->
937+badCompArg :: Type.Localizer -> Thing -> String -> String -> Can.Type -> (H.Doc, H.Doc)
977− Help.findTypoPairs (both ++ leftOnly) (both ++ rightOnly)
938+badCompArg localizer thing op direction tipe =
978−
939+ (
979− pairs ->
940+ H.reflow $
980− pairs
941+ "I cannot do a comparison with this value:"
981− in
942+ ,
982− if null typoPairs then
943+ H.stack
983− ( "The record fields do not match up. One has "
944+ [ H.reflow $ toDescription thing $
984− <> Help.commaSep leftOnly <> ". The other has "
945+ "The " <> direction <> " side of (" <> op <> ") is"
985− <> Help.commaSep rightOnly <> "."
946+ , Type.toDoc localizer tipe
986− , []
947+ , H.fillSep
987− )
948+ ["But","(" <> H.text op <> ")","only","works","on"
988−
949+ ,H.dullyellow "Int" <> ","
989− else
950+ ,H.dullyellow "Float" <> ","
990− ( "The record fields do not match up. Maybe you made one of these typos?"
951+ ,H.dullyellow "Char" <> ","
991− , typoDocs "<->" typoPairs
952+ ,"and"
992− )
953+ ,H.dullyellow "String"
993−
954+ ,"values.","If","you","want","to","get","crazy,","it","will","work","on"
994−
955+ ,"lists","and","tuples","of","comparable","values","as","well."
995−oneMissingField :: [Text] -> Text -> ( Text, [Doc] )
956+ ]
996−oneMissingField knownFields missingField =
957+ ]
997− case Help.findPotentialTypos knownFields missingField of
958+ )
998− [] ->
999− ( "Looks like a record is missing the `" <> missingField <> "` field."
1000− , []
1001− )
1002−
1003− [typo] ->
1004− ( "Looks like a record is missing the `" <> missingField
1005− <> "` field. Maybe it is a typo?"
1006− , typoDocs "->" [(missingField, typo)]
1007− )
1008−
1009− typos ->
1010− ( "Looks like a record is missing the `" <> missingField
1011− <> "` field. It is close to names like "
1012− <> Help.commaSep typos <> " so maybe it is a typo?"
1013− , []
1014− )
1015−
1016
959
1017−manyMissingFields :: [Text] -> [Text] -> (Text, [Doc])
1018−manyMissingFields knownFields missingFields =
1019− case Help.findTypoPairs missingFields knownFields of
1020− [] ->
1021− ( "Looks like a record is missing these fields: "
1022− <> Help.commaSep missingFields
1023− , []
1024− )
1025
960
1026− typoPairs ->
961+badCompMix :: Type.Localizer -> String -> Can.Type -> Can.Type -> (H.Doc, H.Doc)
1027− ( "Looks like a record is missing these fields: "
962+badCompMix localizer op tipe expectedType =
1028− <> Help.commaSep missingFields
963+ (
1029− <> ". Potential typos include:"
964+ H.reflow $
1030− , typoDocs "->" typoPairs
965+ "I need both sides of (" <> op <> ") to be the same type:"
966+ ,
967+ typeComparison localizer expectedType tipe
968+ (
969+ "The left side of is:"
970+ ,
971+ "But the right side is:"
972+ ,
973+ [ H.reflow $
974+ "I cannot compare different types though! Which side of (" <> op <> ") is the problem?"
975+ ]
1031 )
976 )
977+ )
1032
978
1033
979
1034−typoDocs :: Text -> [(Text, Text)] -> [Doc]
1035−typoDocs arrow typoPairs =
1036− let
1037− maxLen =
1038− maximum (map (Text.length . fst) typoPairs)
1039− in
1040− text "" : map (padTypo arrow maxLen) typoPairs
1041−
1042−
1043−padTypo :: Text -> Int -> (Text, Text) -> Doc
1044−padTypo arrow maxLen (missingField, knownField) =
1045− text (Text.replicate (maxLen - Text.length missingField) " ")
1046− <> dullyellow (text missingField)
1047− <+> text arrow
1048− <+> dullyellow (text knownField)
1049−-}
1050−
1051
980
1052−-- INFINITE TYPES
981+-- BAD EQUALITY
1053
982
1054
983
1055−infiniteTypeToReport :: RenderType.Localizer -> Either Hint Text -> Type.Canonical -> Report.Report
984+badEquality :: Type.Localizer -> String -> Can.Type -> Can.Type -> (H.Doc, H.Doc)
1056−infiniteTypeToReport localizer context overallType =
985+badEquality localizer op tipe expectedType =
1057− let
986+ (
1058− (maybeRegion, description) =
987+ H.reflow $
1059− case context of
988+ "I need both sides of (" <> op <> ") to be the same type:"
1060− Right name ->
989+ ,
1061− ( Nothing, functionName name )
990+ typeComparison localizer expectedType tipe
1062− Left hint ->
991+ (
1063− infiniteHint hint
992+ "The left side of is:"
1064− in
993+ ,
1065− Report.report
994+ "But the right side is:"
1066− "INFINITE TYPE"
995+ ,
1067− maybeRegion
996+ if isFloat tipe || isFloat expectedType then
1068− ( "I am inferring a weird self-referential type for " <> description <> ":"
997+ [ H.toSimpleNote $
1069− )
998+ "Equality on floats is not 100% reliable due to the design of IEEE 754. I\
1070− ( stack
999+ \ recommend a check like (abs (x - y) < 0.0001) instead."
1071− [ reflowParagraph $
1072− "Here is my best effort at writing down the type. You will see ∞ for\
1073− \ parts of the type that repeat something already printed out infinitely."
1074− , indent 4 (RenderType.toDoc localizer overallType)
1075− , reflowParagraph $
1076− "Usually staring at the type is not so helpful in these cases, so definitely\
1077− \ read the debugging hints for ideas on how to figure this out: "
1078− <> Help.hintLink "infinite-type"
1079 ]
1000 ]
1001+ else
1002+ [ H.reflow $
1003+ "Different types can never be equal though! Which side is messed up?"
1004+ ]
1005+-- else
1006+-- error "TODO detect problems here instead?"
1080 )
1007 )
1081−
1008+ )
1082−
1083−infiniteHint :: Hint -> (Maybe R.Region, Text)
1084−infiniteHint hint =
1085− case hint of
1086− CaseBranch n region ->
1087− ( Just region, "the " <> ordinalize n <> " `case` branch" )
1088−
1089− Case ->
1090− ( Nothing, "this `case` expression" )
1091−
1092− IfCondition ->
1093− ( Nothing, "the `if` condition" )
1094−
1095− IfBranches ->
1096− ( Nothing, "the `if` branches" )
1097−
1098− MultiIfBranch n region ->
1099− ( Just region, "the " <> ordinalize n <> " `if` branch" )
1100−
1101− If ->
1102− ( Nothing, "this `if` expression" )
1103−
1104− List ->
1105− ( Nothing, "this list" )
1106−
1107− ListElement n region ->
1108− ( Just region, "the " <> ordinalize n <> " list entry" )
1109−
1110− BinopLeft _ _ region ->
1111− ( Just region, "the left argument" )
1112−
1113− BinopRight _ _ region ->
1114− ( Just region, "the right argument" )
1115−
1116− Binop _ _ ->
1117− ( Nothing, "this expression" )
1118−
1119− Function maybeName ->
1120− ( Nothing, maybeFuncName maybeName )
1121−
1122− UnexpectedArg maybeName 1 1 region ->
1123− ( Just region, "the argument to " <> maybeFuncName maybeName )
1124−
1125− UnexpectedArg maybeName index _total region ->
1126− ( Just region, "the " <> ordinalize index <> " argument to " <> maybeFuncName maybeName )
1127−
1128− FunctionArity maybeName _ _ region ->
1129− ( Just region, maybeFuncName maybeName )
1130−
1131− ReturnType name _ _ region ->
1132− ( Just region, functionName name )
1133−
1134− Instance name ->
1135− ( Nothing, functionName name )
1136−
1137− Literal name ->
1138− ( Nothing, name )
1139−
1140− Pattern _ _ ->
1141− ( Nothing, "this pattern" )
1142−
1143− PatternCons ->
1144− ( Nothing, "this pattern" )
1145−
1146− Shader ->
1147− ( Nothing, "this shader" )
1148−
1149− Lambda ->
1150− ( Nothing, "this function" )
1151−
1152− Accessor _ ->
1153− ( Nothing, "this record accessor" )
1154−
1155− Access _ _ ->
1156− ( Nothing, "this field access" )
1157−
1158− Record ->
1159− ( Nothing, "this record" )
1160−
1161− Unit ->
1162− ( Nothing, "this “unit” value" )
1163−
1164− Tuple ->
1165− ( Nothing, "this tuple" )
1166−
1167− -- effect manager problems
1168− Manager name ->
1169− ( Nothing, functionName name )
1170−
1171− State name ->
1172− ( Nothing, functionName name )
1173−
1174− SelfMsg ->
1175− ( Nothing, "this code" )