on master help sign in
Create hints for canonicalization errors
230181aEvan Czaplicki
src/Canonicalize/Port.hs+5−5
@@ -90,7 +90,7 @@ validForeignType name isInbound rootType tipe =
90 T.Type v ->
90 T.Type v ->
91 case any ($ v) [ Var.isJson, Var.isPrimitive, Var.isTuple ] of
91 case any ($ v) [ Var.isJson, Var.isPrimitive, Var.isTuple ] of
92 True -> Result.ok ()
92 True -> Result.ok ()
93− False -> err "an unsupported type"
93+ False -> err Nothing
94
94
95 T.App t [] ->
95 T.App t [] ->
96 valid t
96 valid t
@@ -105,16 +105,16 @@ validForeignType name isInbound rootType tipe =
105 Foldable.traverse_ valid ts
105 Foldable.traverse_ valid ts
106
106
107 T.App _ _ ->
107 T.App _ _ ->
108− err "an unsupported type"
108+ err Nothing
109
109
110 T.Var _ ->
110 T.Var _ ->
111− err "a free type variable"
111+ err (Just "a free type variable")
112
112
113 T.Lambda _ _ ->
113 T.Lambda _ _ ->
114− err "functions"
114+ err (Just "a function")
115
115
116 T.Record _ (Just _) ->
116 T.Record _ (Just _) ->
117− err "extended records with free type variables"
117+ err (Just "an extended record")
118
118
119 T.Record fields Nothing ->
119 T.Record fields Nothing ->
120 Foldable.traverse_ (\(k,v) -> (,) k <$> valid v) fields
120 Foldable.traverse_ (\(k,v) -> (,) k <$> valid v) fields
src/Canonicalize/Variable.hs+22−14
@@ -128,7 +128,7 @@ preferLocals' region env extract kind possibilities var =
128 name == Env._home env
128 name == Env._home env
129
129
130 ambiguous possibleVars =
130 ambiguous possibleVars =
131− Result.err (A.A region (Error.var kind var (Error.Ambiguous vars)))
131+ Result.err (A.A region (Error.variable kind var Error.Ambiguous vars))
132 where
132 where
133 vars = map (Var.toString . extract) possibleVars
133 vars = map (Var.toString . extract) possibleVars
134
134
@@ -173,7 +173,7 @@ notFound region kind possibilities var =
173 possibleNames =
173 possibleNames =
174 map toVarName possibilities
174 map toVarName possibilities
175
175
176− problem =
176+ (problem, suggestions) =
177 case name of
177 case name of
178 Left _ ->
178 Left _ ->
179 exposedProblem name possibleNames
179 exposedProblem name possibleNames
@@ -181,10 +181,10 @@ notFound region kind possibilities var =
181 Right (modul, varName) ->
181 Right (modul, varName) ->
182 qualifiedProblem modul varName (Either.rights possibleNames)
182 qualifiedProblem modul varName (Either.rights possibleNames)
183 in
183 in
184− Result.err (A.A region (Error.var kind var problem))
184+ Result.err (A.A region (Error.variable kind var problem suggestions))
185
185
186
186
187−exposedProblem :: VarName -> [VarName] -> Error.VarProblem
187+exposedProblem :: VarName -> [VarName] -> (Error.VarProblem, [String])
188exposedProblem name possibleNames =
188exposedProblem name possibleNames =
189 let (exposed, qualified) =
189 let (exposed, qualified) =
190 possibleNames
190 possibleNames
@@ -192,27 +192,35 @@ exposedProblem name possibleNames =
192 |> Error.nearbyNames noQualifier name
192 |> Error.nearbyNames noQualifier name
193 |> Either.partitionEithers
193 |> Either.partitionEithers
194 in
194 in
195+ ( Error.ExposedUnknown
195− Error.ExposedUnknown exposed (map qualifiedToString qualified)
196+ , exposed ++ map qualifiedToString qualified
197+ )
196
198
197
199
198qualifiedProblem
200qualifiedProblem
199 :: Module.Name
201 :: Module.Name
200 -> String
202 -> String
201 -> [(Module.Name, String)]
203 -> [(Module.Name, String)]
202− -> Error.VarProblem
204+ -> (Error.VarProblem, [String])
203qualifiedProblem moduleName name allQualified =
205qualifiedProblem moduleName name allQualified =
204 let availableModules =
206 let availableModules =
205 Set.fromList (map fst allQualified)
207 Set.fromList (map fst allQualified)
208+
209+ moduleNameString =
210+ Module.nameToString moduleName
206 in
211 in
207 case Set.member moduleName availableModules of
212 case Set.member moduleName availableModules of
208 True ->
213 True ->
214+ ( Error.QualifiedUnknown moduleNameString
209− allQualified
215+ , allQualified
210− |> filter ((==) moduleName . fst)
216+ |> filter ((==) moduleName . fst)
211− |> map snd
217+ |> map snd
212− |> Error.nearbyNames id name
218+ |> Error.nearbyNames id name
213− |> Error.QualifiedUnknown
219+ )
214
220
215 False ->
221 False ->
222+ ( Error.UnknownQualifier moduleNameString
216− Set.toList availableModules
223+ , Set.toList availableModules
217− |> Error.nearbyNames Module.nameToString moduleName
224+ |> map Module.nameToString
218− |> Error.UnknownQualifier
225+ |> Error.nearbyNames id moduleNameString
226+ )
src/Reporting/Error/Canonicalize.hs+115−11
@@ -8,11 +8,13 @@ import qualified Data.Aeson as Json
8import Data.Function (on)
8import Data.Function (on)
9import qualified Data.List as List
9import qualified Data.List as List
10import qualified Text.EditDistance as Dist
10import qualified Text.EditDistance as Dist
11+import qualified Text.PrettyPrint as P
11
12
12import qualified AST.Module as Module
13import qualified AST.Module as Module
13import qualified AST.Type as Type
14import qualified AST.Type as Type
14import qualified AST.Variable as Var
15import qualified AST.Variable as Var
15import Elm.Utils ((|>))
16import Elm.Utils ((|>))
17+import qualified Reporting.PrettyPrint as P
16import qualified Reporting.Region as R
18import qualified Reporting.Region as R
17
19
18
20
@@ -31,19 +33,20 @@ data VarError = VarError
31 { varKind :: String
33 { varKind :: String
32 , varName :: String
34 , varName :: String
33 , varProblem :: VarProblem
35 , varProblem :: VarProblem
36+ , varSuggestions :: [String]
34 }
37 }
35
38
36
39
37data VarProblem
40data VarProblem
38− = Ambiguous [String]
41+ = Ambiguous
39− | UnknownQualifier [Module.Name]
42+ | UnknownQualifier String
40− | QualifiedUnknown [String]
43+ | QualifiedUnknown String
41− | ExposedUnknown { closeExposed :: [String], closeQualified :: [String] }
44+ | ExposedUnknown
42
45
43
46
44−var :: String -> String -> VarProblem -> Error
47+variable :: String -> String -> VarProblem -> [String] -> Error
45−var kind name problem =
48+variable kind name problem suggestions =
46− Var (VarError kind name problem)
49+ Var (VarError kind name problem suggestions)
47
50
48
51
49-- PATTERN
52-- PATTERN
@@ -94,7 +97,7 @@ data PortError = PortError
94 , portIsInbound :: Bool
97 , portIsInbound :: Bool
95 , portRootType :: Type.Canonical
98 , portRootType :: Type.Canonical
96 , portLocalType :: Type.Canonical
99 , portLocalType :: Type.Canonical
97− , portMessage :: String
100+ , portMessage :: Maybe String
98 }
101 }
99
102
100
103
@@ -103,10 +106,10 @@ port
103 -> Bool
106 -> Bool
104 -> Type.Canonical
107 -> Type.Canonical
105 -> Type.Canonical
108 -> Type.Canonical
106− -> String
109+ -> Maybe String
107 -> Error
110 -> Error
108−port name isInbound rootType localType message =
111+port name isInbound rootType localType maybeMessage =
109− Port (PortError name isInbound rootType localType message)
112+ Port (PortError name isInbound rootType localType maybeMessage)
110
113
111
114
112-- NEARBY NAMES
115-- NEARBY NAMES
@@ -143,6 +146,107 @@ toJson err =
143 case err of
146 case err of
144 _ -> error "Canonicalize.toJson"
147 _ -> error "Canonicalize.toJson"
145
148
149+
150+-- HINTS
151+
152+toHint :: Error -> String
153+toHint err =
154+ case err of
155+ Var (VarError kind name problem suggestions) ->
156+ let var = kind ++ "'" ++ name ++ "'"
157+ in
158+ case problem of
159+ Ambiguous ->
160+ "This usage of " ++ var ++ " is ambiguous"
161+ ++ maybeYouWant "\n\n" suggestions
162+
163+ UnknownQualifier qualifier ->
164+ "Cannot find " ++ var ++ "\n\n"
165+ ++ "The qualifier " ++ qualifier ++ " is not in scope."
166+ ++ maybeYouWant "\n" suggestions
167+
168+ QualifiedUnknown qualifier ->
169+ "Cannot find " ++ var ++ "\n\n"
170+ ++ "The module called " ++ qualifier ++ " does not expose that value."
171+ ++ maybeYouWant "\n" suggestions
172+
173+ ExposedUnknown ->
174+ "Cannot find " ++ var
175+ ++ maybeYouWant "\n\n" suggestions
176+
177+ Pattern patternError ->
178+ case patternError of
179+ PatternArgMismatch var expected actual ->
180+ argMismatchHint "pattern" var expected actual
181+
182+ Alias aliasError ->
183+ case aliasError of
184+ ArgMismatch var expected actual ->
185+ argMismatchHint "type" var expected actual
186+
187+ SelfRecursive name tvars tipe ->
188+ "This type alias is recursive, forming an infinite type!\n\n"
189+ ++ "Type aliases are just names for more fundamental types, so I go through\n"
190+ ++ "and expand them all to know the real underlying type. The problem is that\n"
191+ ++ "when I expand a recursive type alias, it keeps getting bigger and bigger.\n"
192+ ++ "Dealiasing results in an infinitely large type! Try this instead:\n\n"
193+ ++ " type " ++ name ++ concatMap (' ':) tvars ++ " ="
194+ ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty True tipe)))
195+ ++ "\n\nThis creates a brand new type that cannot be expanded. It is similar types\n"
196+ ++ "like List which are recursive, but do not get expanded into infinite types."
197+
198+ MutuallyRecursive aliases ->
199+ error "TODO" aliases
200+
201+ Import name importError ->
202+ case importError of
203+ ModuleNotFound suggestions ->
204+ "Could not find a module named '" ++ Module.nameToString name ++ "'"
205+ ++ maybeYouWant "\n\n" (map Module.nameToString suggestions)
206+
207+ ValueNotFound value suggestions ->
208+ "Module '" ++ Module.nameToString name ++ "' does not expose '" ++ value ++ "'"
209+ ++ maybeYouWant "\n\n" suggestions
210+
211+ Export name suggestions ->
212+ "Could not export '" ++ name ++ "'\n\n"
213+ ++ "It is not defined in this module."
214+ ++ maybeYouWant " " suggestions
215+
216+ Port (PortError name isInbound _rootType localType maybeMessage) ->
217+ "Cannot pass " ++ message ++ " through " ++ boundPort ++ " '" ++ name ++ "':\n\n"
218+ ++ P.render (P.nest 4 (P.pretty False localType)) ++ "\n\n"
219+ ++ "The kinds of values that can flow through " ++ boundPort ++ "s include:\n"
220+ ++ "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples,\n"
221+ ++ "Json.Values, and concrete records."
222+ where
223+ boundPort =
224+ if isInbound then "inbound port" else "outbound port"
225+
226+ message =
227+ maybe "the following type" id maybeMessage
228+
229+
230+argMismatchHint :: String -> Var.Canonical -> Int -> Int -> String
231+argMismatchHint kind var expected actual =
232+ "The " ++ kind ++ " " ++ Var.toString var ++ " has too "
233+ ++ (if actual < expected then "few" else "many")
234+ ++ "arguments.\n\n"
235+ ++ "Expecting " ++ show expected ++ ", but got " ++ show actual
236+
237+
238+maybeYouWant :: String -> [String] -> String
239+maybeYouWant prefix suggestions =
240+ case suggestions of
241+ [] ->
242+ ""
243+
244+ _:_ ->
245+ prefix
246+ ++ "Maybe you want one of the following?\n"
247+ ++ concatMap ("\n "++) suggestions
248+
249+
146{--
250{--
147(.=.) key value =
251(.=.) key value =
148 key .= (value :: String)
252 key .= (value :: String)