on master help sign in
Create hints for canonicalization errors
commit230181a0740070b266fb16210115c16f76a2d148 authorEvan Czaplicki <info@elm-lang.org> dateTue 5 May 2015, 06:51 (12 years ago) parent15eb831
3 files changed+142−30
src/Canonicalize/Port.hs+5−5 src/Canonicalize/Variable.hs+22−14 src/Reporting/Error/Canonicalize.hs+115−11
src/Canonicalize/Port.hs+5−5
@@ -90,7 +90,7 @@ validForeignType name isInbound rootType tipe =
9090 T.Type v ->
9191 case any ($ v) [ Var.isJson, Var.isPrimitive, Var.isTuple ] of
9292 True -> Result.ok ()
93− False -> err "an unsupported type"
93+ False -> err Nothing
9494
9595 T.App t [] ->
9696 valid t
@@ -105,16 +105,16 @@ validForeignType name isInbound rootType tipe =
105105 Foldable.traverse_ valid ts
106106
107107 T.App _ _ ->
108− err "an unsupported type"
108+ err Nothing
109109
110110 T.Var _ ->
111− err "a free type variable"
111+ err (Just "a free type variable")
112112
113113 T.Lambda _ _ ->
114− err "functions"
114+ err (Just "a function")
115115
116116 T.Record _ (Just _) ->
117− err "extended records with free type variables"
117+ err (Just "an extended record")
118118
119119 T.Record fields Nothing ->
120120 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 =
128128 name == Env._home env
129129
130130 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))
132132 where
133133 vars = map (Var.toString . extract) possibleVars
134134
@@ -173,7 +173,7 @@ notFound region kind possibilities var =
173173 possibleNames =
174174 map toVarName possibilities
175175
176− problem =
176+ (problem, suggestions) =
177177 case name of
178178 Left _ ->
179179 exposedProblem name possibleNames
@@ -181,10 +181,10 @@ notFound region kind possibilities var =
181181 Right (modul, varName) ->
182182 qualifiedProblem modul varName (Either.rights possibleNames)
183183 in
184− Result.err (A.A region (Error.var kind var problem))
184+ Result.err (A.A region (Error.variable kind var problem suggestions))
185185
186186
187−exposedProblem :: VarName -> [VarName] -> Error.VarProblem
187+exposedProblem :: VarName -> [VarName] -> (Error.VarProblem, [String])
188188exposedProblem name possibleNames =
189189 let (exposed, qualified) =
190190 possibleNames
@@ -192,27 +192,35 @@ exposedProblem name possibleNames =
192192 |> Error.nearbyNames noQualifier name
193193 |> Either.partitionEithers
194194 in
195− Error.ExposedUnknown exposed (map qualifiedToString qualified)
195+ ( Error.ExposedUnknown
196+ , exposed ++ map qualifiedToString qualified
197+ )
196198
197199
198200qualifiedProblem
199201 :: Module.Name
200202 -> String
201203 -> [(Module.Name, String)]
202− -> Error.VarProblem
204+ -> (Error.VarProblem, [String])
203205qualifiedProblem moduleName name allQualified =
204206 let availableModules =
205207 Set.fromList (map fst allQualified)
208+
209+ moduleNameString =
210+ Module.nameToString moduleName
206211 in
207212 case Set.member moduleName availableModules of
208213 True ->
209− allQualified
210− |> filter ((==) moduleName . fst)
211− |> map snd
212− |> Error.nearbyNames id name
213− |> Error.QualifiedUnknown
214+ ( Error.QualifiedUnknown moduleNameString
215+ , allQualified
216+ |> filter ((==) moduleName . fst)
217+ |> map snd
218+ |> Error.nearbyNames id name
219+ )
214220
215221 False ->
216− Set.toList availableModules
217− |> Error.nearbyNames Module.nameToString moduleName
218− |> Error.UnknownQualifier
222+ ( Error.UnknownQualifier moduleNameString
223+ , Set.toList availableModules
224+ |> map Module.nameToString
225+ |> Error.nearbyNames id moduleNameString
226+ )
src/Reporting/Error/Canonicalize.hs+115−11
@@ -8,11 +8,13 @@ import qualified Data.Aeson as Json
88import Data.Function (on)
99import qualified Data.List as List
1010import qualified Text.EditDistance as Dist
11+import qualified Text.PrettyPrint as P
1112
1213import qualified AST.Module as Module
1314import qualified AST.Type as Type
1415import qualified AST.Variable as Var
1516import Elm.Utils ((|>))
17+import qualified Reporting.PrettyPrint as P
1618import qualified Reporting.Region as R
1719
1820
@@ -31,19 +33,20 @@ data VarError = VarError
3133 { varKind :: String
3234 , varName :: String
3335 , varProblem :: VarProblem
36+ , varSuggestions :: [String]
3437 }
3538
3639
3740data VarProblem
38− = Ambiguous [String]
39− | UnknownQualifier [Module.Name]
40− | QualifiedUnknown [String]
41− | ExposedUnknown { closeExposed :: [String], closeQualified :: [String] }
41+ = Ambiguous
42+ | UnknownQualifier String
43+ | QualifiedUnknown String
44+ | ExposedUnknown
4245
4346
44−var :: String -> String -> VarProblem -> Error
45−var kind name problem =
46− Var (VarError kind name problem)
47+variable :: String -> String -> VarProblem -> [String] -> Error
48+variable kind name problem suggestions =
49+ Var (VarError kind name problem suggestions)
4750
4851
4952-- PATTERN
@@ -94,7 +97,7 @@ data PortError = PortError
9497 , portIsInbound :: Bool
9598 , portRootType :: Type.Canonical
9699 , portLocalType :: Type.Canonical
97− , portMessage :: String
100+ , portMessage :: Maybe String
98101 }
99102
100103
@@ -103,10 +106,10 @@ port
103106 -> Bool
104107 -> Type.Canonical
105108 -> Type.Canonical
106− -> String
109+ -> Maybe String
107110 -> Error
108−port name isInbound rootType localType message =
109− Port (PortError name isInbound rootType localType message)
111+port name isInbound rootType localType maybeMessage =
112+ Port (PortError name isInbound rootType localType maybeMessage)
110113
111114
112115-- NEARBY NAMES
@@ -143,6 +146,107 @@ toJson err =
143146 case err of
144147 _ -> error "Canonicalize.toJson"
145148
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+
146250{--
147251(.=.) key value =
148252 key .= (value :: String)