@@ -2,18 +2,18 @@
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Canonicalize where
3module Reporting.Error.Canonicalize where
5−--import Data.Aeson ((.=))
6−import qualified Data.Aeson as Json
7−--import qualified Data.Aeson.Types as Json
8import Data.Function (on)
5import Data.Function (on)
9import qualified Data.List as List
6import qualified Data.List as List
10import qualified Text.EditDistance as Dist
7import qualified Text.EditDistance as Dist
8+import qualified Text.PrettyPrint as P
12import qualified AST.Module as Module
10import qualified AST.Module as Module
13import qualified AST.Type as Type
11import qualified AST.Type as Type
14import qualified AST.Variable as Var
12import qualified AST.Variable as Var
15import Elm.Utils ((|>))
13import Elm.Utils ((|>))
16−import qualified Reporting.Region as R
14+import qualified Reporting.PrettyPrint as P
15+import qualified Reporting.Region as Region
16+import qualified Reporting.Report as Report
@@ -31,19 +31,20 @@ data VarError = VarError
31 { varKind :: String
31 { varKind :: String
32 , varName :: String
32 , varName :: String
33 , varProblem :: VarProblem
33 , varProblem :: VarProblem
34+ , varSuggestions :: [String]
37data VarProblem
38data VarProblem
38− = Ambiguous [String]
39+ = Ambiguous
39− | UnknownQualifier [Module.Name]
40+ | UnknownQualifier String
40− | QualifiedUnknown [String]
41+ | QualifiedUnknown String
41− | ExposedUnknown { closeExposed :: [String], closeQualified :: [String] }
42+ | ExposedUnknown
44−var :: String -> String -> VarProblem -> Error
45+variable :: String -> String -> VarProblem -> [String] -> Error
45−var kind name problem =
46+variable kind name problem suggestions =
46− Var (VarError kind name problem)
47+ Var (VarError kind name problem suggestions)
@@ -79,7 +80,7 @@ valueNotFound name value possibilities =
79data AliasError
80data AliasError
80 = ArgMismatch Var.Canonical Int Int
81 = ArgMismatch Var.Canonical Int Int
81 | SelfRecursive String [String] Type.Raw
82 | SelfRecursive String [String] Type.Raw
82− | MutuallyRecursive [(R.Region, String, [String], Type.Raw)]
83+ | MutuallyRecursive [(Region.Region, String, [String], Type.Raw)]
85alias :: Var.Canonical -> Int -> Int -> Error
86alias :: Var.Canonical -> Int -> Int -> Error
@@ -94,7 +95,7 @@ data PortError = PortError
94 , portIsInbound :: Bool
95 , portIsInbound :: Bool
95 , portRootType :: Type.Canonical
96 , portRootType :: Type.Canonical
96 , portLocalType :: Type.Canonical
97 , portLocalType :: Type.Canonical
97− , portMessage :: String
98+ , portMessage :: Maybe String
@@ -103,10 +104,10 @@ port
104 -> Type.Canonical
105 -> Type.Canonical
105 -> Type.Canonical
106 -> Type.Canonical
106− -> String
107+ -> Maybe String
108−port name isInbound rootType localType message =
109+port name isInbound rootType localType maybeMessage =
109− Port (PortError name isInbound rootType localType message)
110+ Port (PortError name isInbound rootType localType maybeMessage)
112-- NEARBY NAMES
113-- NEARBY NAMES
@@ -128,138 +129,126 @@ distance x y =
128 Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
129 Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
131−-- TO STRING
132+-- TO REPORT
133−toString :: R.Region -> Error -> String
134+toReport :: Error -> Report.Report
134−toString region err =
135+toReport err =
135 case err of
136 case err of
136− _ -> error "Canonicalize.toString" region
141−toJson :: Error -> Json.Value
144− _ -> error "Canonicalize.toJson"
148− key .= (value :: String)
151−errorToJson :: LError -> Json.Value
152−errorToJson (A.A region err) =
153− Json.object (generals ++ specifics)
156− [ "category" .=. "canonicalization"
157− , "region" .= region
162− Var (VarError kind name varProblem) ->
137+ Var (VarError kind name problem suggestions) ->
163− [ "kind" .= kind
138+ let var = kind ++ " '" ++ name ++ "'"
164− , "name" .= name
139+ in
165− ]
140+ case problem of
166− ++ varProblemToJson varProblem
141+ Ambiguous ->
143+ ("This usage of " ++ var ++ " is ambiguous.")
144+ (maybeYouWant suggestions)
146+ UnknownQualifier qualifier ->
148+ ("Cannot find " ++ var ++ ", qualifier '" ++ qualifier ++ "' is not in scope.")
149+ (maybeYouWant suggestions)
151+ QualifiedUnknown qualifier ->
153+ ("Cannot find " ++ var ++ ", module '" ++ qualifier ++ "' does not expose that value.")
154+ (maybeYouWant suggestions)
158+ ("Cannot find " ++ var)
159+ (maybeYouWant suggestions)
161+ Pattern patternError ->
162+ case patternError of
163+ PatternArgMismatch var expected actual ->
164+ argMismatchReport "Pattern" var expected actual
168− Alias aliasError ->
166+ Alias aliasError ->
169− case aliasError of
167+ case aliasError of
170− ArgMismatch name expected actual ->
168+ ArgMismatch var expected actual ->
171− [ "tag" .=. "alias-args"
169+ argMismatchReport "Type" var expected actual
172− , "name" .= Var.toString name
170+
173− , "expected-args" .= expected
171+ SelfRecursive name tvars tipe ->
174− , "actual-args" .= actual
172+ Report.simple "This type alias is recursive, forming an infinite type!" $
175− ]
173+ "Type aliases are just names for more fundamental types, so I go through\n"
176−
174+ ++ "and expand them all to know the real underlying type. The problem is that\n"
177− Recursive [(name, tvars, tipe)] ->
175+ ++ "when I expand a recursive type alias, it keeps getting bigger and bigger.\n"
178− [ "tag" .=. "recursive-alias"
176+ ++ "Dealiasing results in an infinitely large type! Try this instead:\n\n"
179− ]
177+ ++ " type " ++ name ++ concatMap (' ':) tvars ++ " ="
180−
178+ ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty True tipe)))
181− Recursive aliases ->
179+ ++ "\n\nThis creates a brand new type that cannot be expanded. It is similar types\n"
182− [ "tag" .=. "mutually-recursive-aliases"
180+ ++ "like List which are recursive, but do not get expanded into infinite types."
184−
182+ MutuallyRecursive aliases ->
183+ Report.simple "This type alias is part of a mutually recursive set of type aliases." $
184+ "The following type aliases all depend on each other in some way:\n"
185+ ++ concatMap showName aliases ++ "\n\n"
186+ ++ "The problem is that when you try to expand any of these type aliases, they\n"
187+ ++ "expand infinitely! To fix, first try to centralize them into one alias."
189+ showName (Region.Region start _, name, _, _) =
190+ "\n " ++ name ++ replicate (65 - length name) ' '
191+ ++ "line " ++ show (Region.line start)
185− Import moduleName importError ->
193+ Import name importError ->
194+ let moduleName = Module.nameToString name
186− case importError of
196+ case importError of
187− ModuleNotFound suggestions ->
197+ ModuleNotFound suggestions ->
188− [ "tag" .=. "unknown-import"
198+ Report.simple
189− , "name" .= moduleName
199+ ("Could not find a module named '" ++ moduleName ++ "'")
190− , "suggestions" .= suggestions
200+ (maybeYouWant (map Module.nameToString suggestions))
192− ValueNotFound name suggestions ->
202+ ValueNotFound value suggestions ->
193− []
203+ Report.simple
194−
204+ ("Module '" ++ moduleName ++ "' does not expose '" ++ value ++ "'")
205+ (maybeYouWant suggestions)
195− Export name suggestions ->
207+ Export name suggestions ->
196− []
208+ Report.simple
197−
209+ ("Could not export '" ++ name ++ "' which is not defined in this module.")
198− Port portError ->
210+ (maybeYouWant suggestions)
200−
212+ Port (PortError name isInbound _rootType localType maybeMessage) ->
202−varProblemToJson :: VarProblem -> [Json.Pair]
214+ boundPort =
203−varProblemToJson varProblem =
215+ if isInbound then "inbound port" else "outbound port"
204− let details tag suggestions =
216+
205− [ "tag" .=. tag
217+ preHint =
206− , "suggestions" .= suggestions
218+ "Trying to send " ++ maybe "an unsupported type" id maybeMessage
207− ]
219+ ++ " through " ++ boundPort ++ " '" ++ name ++ "'"
222+ "The specific unsupported type is:\n\n"
223+ ++ P.render (P.nest 4 (P.pretty False localType)) ++ "\n\n"
224+ ++ "The types of values that can flow through " ++ boundPort ++ "s include:\n"
225+ ++ "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples,\n"
226+ ++ "Json.Values, and concrete records."
228+ Report.simple preHint postHint
231+argMismatchReport :: String -> Var.Canonical -> Int -> Int -> Report.Report
232+argMismatchReport kind var expected actual =
235+ kind ++ " " ++ Var.toString var ++ " has too "
236+ ++ (if actual < expected then "few" else "many")
240+ "Expecting " ++ show expected ++ ", but got " ++ show actual ++ "."
209− case varProblem of
242+ Report.Report Nothing preHint postHint
210− Ambiguous possibilities ->
211− details "ambiguous" possibilities
213− UnknownQualifier possibleModules ->
216− (map Module.nameToString possibleModules)
218− QualifiedUnknown possibilities ->
219− details "qualified-unknown" possibilities
221− ExposedUnknown closeExposed closeQualified ->
222− details "exposed-unknown" (closeExposed ++ closeQualified)
227−mutuallyRecursiveMessage :: String
228−mutuallyRecursiveMessage =
229− "The following type aliases are mutually recursive, forming an \
230− \infinite type. When you expand them, they just keep getting bigger:"
233−typeAliasErrorSegue :: String
234−typeAliasErrorSegue =
238−typeAliasErrorExplanation :: String
239−typeAliasErrorExplanation =
240− "It looks very similar, but the 'type' keyword creates a brand new type, \
241− \not just an alias for an existing one. This lets us avoid infinitely \
242− \expanding it during type inference."
246− :: (String, [String], Type.Type var)
247− -> D.Declaration' pk def var expr
253− :: (String, [String], Type.Type var)
254− -> D.Declaration' pk def var expr
256− D.Datatype n ts [(n,[t])]
245+maybeYouWant :: [String] -> String
246+maybeYouWant suggestions =
259−indented :: [D.ValidDecl] -> Doc
251+ _:_ ->
260−indented decls =
252+ "Maybe you want one of the following?\n"
261− P.vcat (map prty decls) <> P.text "\n"
253+ ++ concatMap ("\n "++) suggestions
263− prty decl = P.text "\n " <> pretty decl