on master help sign in
Create hints for syntax errors
5b3458dEvan Czaplicki
src/Reporting/Error.hs+1−1
@@ -25,7 +25,7 @@ toString :: A.Located Error -> String
25toString (A.A region err) =
25toString (A.A region err) =
26 case err of
26 case err of
27 Syntax syntaxError ->
27 Syntax syntaxError ->
28− Syntax.toString region syntaxError
28+ error "TODO" syntaxError
29
29
30 Canonicalize canonicalizeError ->
30 Canonicalize canonicalizeError ->
31 Canonicalize.toString region canonicalizeError
31 Canonicalize.toString region canonicalizeError
src/Reporting/Error/Syntax.hs+99−6
@@ -2,9 +2,15 @@
2module Reporting.Error.Syntax where
2module Reporting.Error.Syntax where
3
3
4import qualified Data.Aeson as Json
4import qualified Data.Aeson as Json
5+import qualified Data.List as List
5import qualified Text.Parsec.Error as Parsec
6import qualified Text.Parsec.Error as Parsec
7+import qualified Text.PrettyPrint as P
8+import Text.PrettyPrint ((<+>))
6
9
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
8
14
9
15
10data Error
16data Error
@@ -15,15 +21,103 @@ data Error
15 | DuplicateValueDeclaration String
21 | DuplicateValueDeclaration String
16 | DuplicateTypeDeclaration String
22 | DuplicateTypeDeclaration String
17 | DuplicateDefinition String
23 | DuplicateDefinition String
18− | UnboundTypeVar String String
24+ | UnboundTypeVarsInAlias String [String] String [String] Type.Raw
25+ | UnboundTypeVarsInUnion String [String] String [String] [(String, [Type.Raw])]
19
26
20
27
21-- TO STRING
28-- TO STRING
22
29
23−toString :: R.Region -> Error -> String
30+toHint :: Error -> String
24−toString region err =
31+toHint err =
25 case err of
32 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))
27
121
28
122
29-- TO JSON
123-- TO JSON
@@ -32,4 +126,3 @@ toJson :: Error -> Json.Value
32toJson err =
126toJson err =
33 case err of
127 case err of
34 _ -> error "Syntax.toJson"
128 _ -> error "Syntax.toJson"
35−
src/Validate.hs+23−10
@@ -340,16 +340,30 @@ extractValues (A.A region decl) =
340-- UNBOUND TYPE VARIABLES
340-- UNBOUND TYPE VARIABLES
341
341
342checkDecl :: D.ValidDecl -> Result.Result wrn Error.Error ()
342checkDecl :: D.ValidDecl -> Result.Result wrn Error.Error ()
343−checkDecl (A.A _ decl) =
343+checkDecl (A.A region decl) =
344 case decl of
344 case decl of
345 D.Definition _ ->
345 D.Definition _ ->
346 return ()
346 return ()
347
347
348 D.Datatype name boundVars ctors ->
348 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)
350
358
351 D.TypeAlias name boundVars tipe ->
359 D.TypeAlias name boundVars tipe ->
360+ case findUnbound boundVars (freeVars tipe) of
361+ [] ->
362+ return ()
363+
364+ v:vs ->
365+ Result.throw region
352− checkTypeVars name boundVars tipe
366+ (Error.UnboundTypeVarsInAlias name boundVars v vs tipe)
353
367
354 D.Port _ ->
368 D.Port _ ->
355 return ()
369 return ()
@@ -358,16 +372,15 @@ checkDecl (A.A _ decl) =
358 return ()
372 return ()
359
373
360
374
361−checkTypeVars :: String -> [String] -> Type.Raw -> Result.Result wrn Error.Error ()
375+findUnbound :: [String] -> [A.Located String] -> [String]
362−checkTypeVars name boundVars tipe =
376+findUnbound boundVars frees =
363 let bounds = Set.fromList boundVars
377 let bounds = Set.fromList boundVars
364
378
365− check (A.A region free) =
379+ isUnbound x =
366− if Set.member free bounds
380+ Set.notMember x bounds
367− then return ()
368− else Result.throw region (Error.UnboundTypeVar name free)
369 in
381 in
370− F.traverse_ check (freeVars tipe)
382+ filter isUnbound (map A.drop frees)
383+
371
384
372
385
373freeVars :: Type.Raw -> [A.Located String]
386freeVars :: Type.Raw -> [A.Located String]