on master help sign in
Create hints for syntax errors
commit5b3458d697cdd02589f4fc620537c4fc1b964e1b authorEvan Czaplicki <info@elm-lang.org> dateMon 4 May 2015, 14:25 (12 years ago) parentef76c15
3 files changed+123−17
src/Reporting/Error.hs+1−1 src/Reporting/Error/Syntax.hs+99−6 src/Validate.hs+23−10
src/Reporting/Error.hs+1−1
@@ -25,7 +25,7 @@ toString :: A.Located Error -> String
2525toString (A.A region err) =
2626 case err of
2727 Syntax syntaxError ->
28− Syntax.toString region syntaxError
28+ error "TODO" syntaxError
2929
3030 Canonicalize canonicalizeError ->
3131 Canonicalize.toString region canonicalizeError
src/Reporting/Error/Syntax.hs+99−6
@@ -2,9 +2,15 @@
22module Reporting.Error.Syntax where
33
44import qualified Data.Aeson as Json
5+import qualified Data.List as List
56import qualified Text.Parsec.Error as Parsec
7+import qualified Text.PrettyPrint as P
8+import Text.PrettyPrint ((<+>))
69
7−import qualified Reporting.Region as R
10+import qualified AST.Helpers as Help
11+import qualified AST.Type as Type
12+import Elm.Utils ((|>))
13+import qualified Reporting.PrettyPrint as P
814
915
1016data Error
@@ -15,15 +21,103 @@ data Error
1521 | DuplicateValueDeclaration String
1622 | DuplicateTypeDeclaration String
1723 | DuplicateDefinition String
18− | UnboundTypeVar String String
24+ | UnboundTypeVarsInAlias String [String] String [String] Type.Raw
25+ | UnboundTypeVarsInUnion String [String] String [String] [(String, [Type.Raw])]
1926
2027
2128-- TO STRING
2229
23−toString :: R.Region -> Error -> String
24−toString region err =
30+toHint :: Error -> String
31+toHint err =
2532 case err of
26− _ -> error "Syntax.toString" region
33+ Parse messages ->
34+ error "TODO" messages
35+
36+ InfixDuplicate opName ->
37+ "The infix declarations for " ++ operator ++ " must be removed.\n\n"
38+ ++ "The precedence and associativity can only be set in one place, and\n"
39+ ++ "this information has already been set somewhere else."
40+ where
41+ operator =
42+ if Help.isOp opName
43+ then "(" ++ opName ++ ")"
44+ else "`" ++ opName ++ "`"
45+
46+ TypeWithoutDefinition valueName ->
47+ "There is a type annotation for '" ++ valueName ++ "' but there"
48+ ++ "is no corresponding definition! I need something like this:\n\n"
49+ ++ " " ++ valueName ++ " = 42\n\n"
50+ ++ "And be sure to put the definition directly below the type annotation."
51+
52+ PortWithoutAnnotation portName ->
53+ "There is a port named '" ++ portName ++ "' but it does not have"
54+ ++ "a type annotation! I need something like this:\n\n"
55+ ++ indent ("port " ++ portName ++ " : Signal Int") ++ "\n\n"
56+ ++ "And be sure to put this annotation directly above the port definition."
57+
58+ DuplicateValueDeclaration name ->
59+ "Multiple top-level declarations define '" ++ name ++ "',\n"
60+ ++ "but the name overlap makes all uses of '" ++ name ++ "' ambiguous.\n"
61+ ++ "How can we know which you want?!\n\n"
62+ ++ "Find all the top-level variables named '" ++ name ++ "' and\n"
63+ ++ "do some renaming. Make sure the names are distinct!"
64+
65+ DuplicateTypeDeclaration tipeName ->
66+ "Multiple declarations define a type '" ++ tipeName ++ "',\n"
67+ ++ "but the name overlap makes all uses of '" ++ tipeName ++ "' ambiguous.\n"
68+ ++ "How can we know which you want?!\n\n"
69+ ++ "Find all the types named '" ++ tipeName ++ "' and\n"
70+ ++ "do some renaming. Make sure the names are distinct!"
71+
72+ DuplicateDefinition name ->
73+ "This let-expression defines multiple variables named '" ++ name ++ "',\n"
74+ ++ "but the name overlap makes all uses of '" ++ name ++ "' ambiguous.\n"
75+ ++ "How can we know which you want?!\n\n"
76+ ++ "Find the variables named '" ++ name ++ "' in this let-expression and\n"
77+ ++ "do some renaming. Make sure the names are distinct!"
78+
79+ UnboundTypeVarsInAlias typeName givenVars tvar tvars tipe ->
80+ unboundTypeVars typeName tvar tvars $ P.render $
81+ P.hang
82+ (P.text "type alias" <+> P.text typeName <+> P.hsep vars <+> P.equals)
83+ 4
84+ (P.pretty False tipe)
85+ where
86+ vars = map P.text (givenVars ++ tvar : tvars)
87+
88+
89+ UnboundTypeVarsInUnion typeName givenVars tvar tvars ctors ->
90+ unboundTypeVars typeName tvar tvars $ P.render $
91+ P.vcat
92+ [ P.text "type" <+> P.text typeName <+> P.hsep vars
93+ , map toDoc ctors
94+ |> zipWith (<+>) (P.text "=" : repeat (P.text "|"))
95+ |> P.vcat
96+ |> P.nest 4
97+ ]
98+ where
99+ vars = map P.text (givenVars ++ tvar : tvars)
100+ toDoc (ctor, args) =
101+ P.text ctor <+> P.hsep (map (P.pretty True) args)
102+
103+
104+unboundTypeVars :: String -> String -> [String] -> String -> String
105+unboundTypeVars typeName tvar tvars revisedDeclaration =
106+ "In the definition of type '" ++ typeName ++ "' you use the following\n"
107+ ++ "unbound type variable" ++ s ++ ": " ++ List.intercalate ", " (tvar:tvars) ++ "\n\n"
108+ ++ "All type variables must be listed to avoid sneaky type errors.\n"
109+ ++ "Imagine one '" ++ typeName ++ "' where '" ++ tvar ++ "' is an Int and\n"
110+ ++ "another where it is a Bool. They both look like a '" ++ typeName ++ "'\n"
111+ ++ "to the type checker, but they are actually different types!\n\n"
112+ ++ "Maybe you want a definition like this?\n\n"
113+ ++ indent revisedDeclaration ++ "\n\n"
114+ where
115+ s = if null tvars then "" else "s"
116+
117+
118+indent :: String -> String
119+indent string =
120+ List.intercalate "\n" (map (" " ++ ) (lines string))
27121
28122
29123-- TO JSON
@@ -32,4 +126,3 @@ toJson :: Error -> Json.Value
32126toJson err =
33127 case err of
34128 _ -> error "Syntax.toJson"
35−
src/Validate.hs+23−10
@@ -340,16 +340,30 @@ extractValues (A.A region decl) =
340340-- UNBOUND TYPE VARIABLES
341341
342342checkDecl :: D.ValidDecl -> Result.Result wrn Error.Error ()
343−checkDecl (A.A _ decl) =
343+checkDecl (A.A region decl) =
344344 case decl of
345345 D.Definition _ ->
346346 return ()
347347
348348 D.Datatype name boundVars ctors ->
349− F.traverse_ (checkTypeVars name boundVars) (concatMap snd ctors)
349+ let frees = concatMap freeVars (concatMap snd ctors)
350+ in
351+ case findUnbound boundVars frees of
352+ [] ->
353+ return ()
354+
355+ v:vs ->
356+ Result.throw region
357+ (Error.UnboundTypeVarsInUnion name boundVars v vs ctors)
350358
351359 D.TypeAlias name boundVars tipe ->
352− checkTypeVars name boundVars tipe
360+ case findUnbound boundVars (freeVars tipe) of
361+ [] ->
362+ return ()
363+
364+ v:vs ->
365+ Result.throw region
366+ (Error.UnboundTypeVarsInAlias name boundVars v vs tipe)
353367
354368 D.Port _ ->
355369 return ()
@@ -358,16 +372,15 @@ checkDecl (A.A _ decl) =
358372 return ()
359373
360374
361−checkTypeVars :: String -> [String] -> Type.Raw -> Result.Result wrn Error.Error ()
362−checkTypeVars name boundVars tipe =
375+findUnbound :: [String] -> [A.Located String] -> [String]
376+findUnbound boundVars frees =
363377 let bounds = Set.fromList boundVars
364378
365− check (A.A region free) =
366− if Set.member free bounds
367− then return ()
368− else Result.throw region (Error.UnboundTypeVar name free)
379+ isUnbound x =
380+ Set.notMember x bounds
369381 in
370− F.traverse_ check (freeVars tipe)
382+ filter isUnbound (map A.drop frees)
383+
371384
372385
373386freeVars :: Type.Raw -> [A.Located String]