on 55ba05f help sign in
elk/src/Reporting/Error/Syntax.hs
241 lines 8.2 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2module Reporting.Error.Syntax where
4import qualified Data.List as List
5import qualified Data.Set as Set
6import qualified Text.Parsec.Error as Parsec
8import qualified AST.Helpers as Help
9import qualified AST.Type as Type
10import qualified Reporting.Render.Type as RenderType
11import qualified Reporting.Report as Report
14data Error
15 = Parse [Parsec.Message]
16 | BadFunctionName Int
17 | BadPattern String
18 | InfixDuplicate String
19 | TypeWithoutDefinition String
20 | PortWithoutAnnotation String
21 | UnexpectedPort
22 | DuplicateFieldName String
23 | DuplicateValueDeclaration String
24 | DuplicateTypeDeclaration String
25 | DuplicateDefinition String
26 | UnboundTypeVarsInAlias String [String] String [String] Type.Raw
27 | UnboundTypeVarsInUnion String [String] String [String] [(String, [Type.Raw])]
30-- TO REPORT
32toReport :: RenderType.Localizer -> Error -> Report.Report
33toReport localizer err =
34 case err of
35 Parse messages ->
36 parseErrorReport messages
38 BadFunctionName arity ->
39 Report.simple
40 "BAD FUNCTION DEFINITION"
41 "Complex patterns cannot be used as function names."
42 ( "This function definition has " ++ show arity ++ " arguments, but instead of a normal name like\n"
43 ++ "`add` or `reverse` it has a pattern. There is no way to \"deconstruct\" a\n"
44 ++ "function with pattern matching, so this needs to be changed to a normal name."
45 )
47 BadPattern name ->
48 Report.simple
49 "BAD PATTERN"
50 ("The free variable `" ++ name ++ "` appears more than once in this pattern.")
51 ( "Rename the variables so there are no duplicates.\n\n"
52 ++ "In Elm, pattern matching works by binding these free variables to subsections\n"
53 ++ "of the matching value. It does not make sense to have the same name for two\n"
54 ++ "different subsections though! When you say `" ++ name ++ "` in some code, there\n"
55 ++ "is no way for me to know which subsection you mean!"
56 )
58 InfixDuplicate opName ->
59 Report.simple
60 "INFIX OVERLAP"
61 ("The infix declarations for " ++ operator ++ " must be removed.")
62 ("The precedence and associativity can only be set in one place, and\n"
63 ++ "this information has already been set somewhere else."
64 )
65 where
66 operator =
67 if Help.isOp opName
68 then "(" ++ opName ++ ")"
69 else "`" ++ opName ++ "`"
71 TypeWithoutDefinition valueName ->
72 Report.simple
73 "MISSING DEFINITION"
74 ("There is a type annotation for `" ++ valueName ++ "` but there"
75 ++ " is no corresponding definition!"
76 )
77 ("Directly below the type annotation, put a definition like:\n\n"
78 ++ " " ++ valueName ++ " = 42"
79 )
81 PortWithoutAnnotation portName ->
82 Report.simple
83 "PORT ERROR"
84 ("Port `" ++ portName ++ "` does not have a type annotation!")
85 ("Directly above the port definition, I need something like this:\n\n"
86 ++ " port " ++ portName ++ " : Signal Int"
87 )
90 UnexpectedPort ->
91 Report.simple
92 "PORT ERROR"
93 "This module has ports, but ports can only appear in the main module."
94 ( "Ports in library code would create hidden dependencies where importing a\n"
95 ++ "module could bring in constraints not captured in the public API. Furthermore,\n"
96 ++ "if the module is imported twice, do we send values out the port twice?"
97 )
99 DuplicateFieldName name ->
100 Report.simple
101 "DUPLICATE FIELD"
102 ("This record has more than one field named `" ++ name ++ "`.")
103 "There can only be one. Do some renaming to make sure the names are distinct!"
105 DuplicateValueDeclaration name ->
106 Report.simple
107 "DUPLICATE DEFINITION"
108 ("Naming multiple top-level values `" ++ name ++ "` makes things\n"
109 ++ "ambiguous. When you say `" ++ name ++ "` which one do you want?"
110 )
111 ("Find all the top-level values named `" ++ name ++ "` and\n"
112 ++ "do some renaming. Make sure the names are distinct!"
113 )
115 DuplicateTypeDeclaration name ->
116 Report.simple
117 "DUPLICATE DEFINITION"
118 ("Naming multiple types `" ++ name ++ "` makes things ambiguous\n"
119 ++ "When you say `" ++ name ++ "` which one do you want?"
120 )
121 ("Find all the types named `" ++ name ++ "` and\n"
122 ++ "do some renaming. Make sure the names are distinct!"
123 )
125 DuplicateDefinition name ->
126 Report.simple
127 "DUPLICATE DEFINITION"
128 ("Naming multiple values `" ++ name ++ "` in a single let-expression makes\n"
129 ++ "things ambiguous. When you say `" ++ name ++ "` which one do you want?"
130 )
131 ("Find all the values named `" ++ name ++ "` in this let-expression and\n"
132 ++ "do some renaming. Make sure the names are distinct!"
133 )
135 UnboundTypeVarsInAlias typeName givenVars tvar tvars tipe ->
136 unboundTypeVars typeName tvar tvars $
137 RenderType.aliasDecl localizer typeName (givenVars ++ tvar : tvars) tipe
139 UnboundTypeVarsInUnion typeName givenVars tvar tvars ctors ->
140 unboundTypeVars typeName tvar tvars $
141 RenderType.typeDecl localizer typeName (givenVars ++ tvar : tvars) ctors
144unboundTypeVars :: String -> String -> [String] -> String -> Report.Report
145unboundTypeVars typeName tvar tvars revisedDeclaration =
146 Report.simple
147 "UNBOUND TYPE VARS"
148 ( "Not all type variables in `" ++ typeName ++ "` are listed, making sneaky\n"
149 ++ "type errors possible. Unbound type variables include: "
150 ++ List.intercalate ", " (tvar:tvars)
151 )
152 ( "You probably want this definition instead:\n"
153 ++ concatMap ("\n "++) (lines revisedDeclaration) ++ "\n\n"
154 ++ "Here's why. Imagine one `" ++ typeName ++ "` where `" ++ tvar ++ "` is an Int and\n"
155 ++ "another where it is a Bool. When we explicitly list the type variables, type\n"
156 ++ "checker can see that they are actually different types."
157 )
160-- TAGGING PARSE ERRORS
162newline :: String
163newline = "NEWLINE"
165freshLine :: String
166freshLine = "FRESH_LINE"
168whitespace :: String
169whitespace = "WHITESPACE"
171keyword :: String -> String
172keyword kwd =
173 "KEYWORD=" ++ kwd
175unkeyword :: String -> Maybe String
176unkeyword message =
177 if List.isPrefixOf "KEYWORD=" message
178 then Just (drop (length "KEYWORD=") message)
179 else Nothing
182-- REPORTING PARSE ERRORS
184parseErrorReport :: [Parsec.Message] -> Report.Report
185parseErrorReport messages =
186 let
187 addMsg message hint =
188 case message of
189 Parsec.SysUnExpect _msg ->
190 hint
192 Parsec.UnExpect _msg ->
193 hint
195 Parsec.Expect msg ->
196 let
197 msg' =
198 if msg `elem` [whitespace, newline, freshLine]
199 then "whitespace"
200 else msg
201 in
202 hint { _expected = Set.insert msg' (_expected hint) }
204 Parsec.Message msg ->
205 hint { _messages = msg : _messages hint }
207 (ParseHint msgs expects) =
208 foldr addMsg emptyHint messages
210 preHint =
211 case msgs of
212 [msg] ->
213 case unkeyword msg of
214 Just kwd ->
215 "It looks like the keyword `" ++ kwd ++ "` is being used as a variable.\n"
216 ++ "Try renaming it to something else."
217 Nothing ->
218 msg
220 _ -> "I ran into something unexpected when parsing your code!"
222 postHint =
223 if Set.null expects
224 then ""
225 else
226 "I am looking for one of the following things:\n"
227 ++ concatMap ("\n "++) (Set.toList expects)
228 in
229 Report.simple "SYNTAX PROBLEM" preHint postHint
232data ParseHint = ParseHint
233 { _messages :: [String]
234 , _expected :: Set.Set String
235 }
236 deriving (Show)
239emptyHint :: ParseHint
240emptyHint =
241 ParseHint [] Set.empty