on 3ad0db6 help sign in
elk/src/Reporting/Error/Syntax.hs
554 lines 18 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Syntax
4 ( Error(..)
5 , ParseError(..)
6 , Problem(..), Theory(..)
7 , toReport
8 )
9 where
11import qualified Data.Set as Set
12import Data.Text (Text)
14import qualified Reporting.Render.Type as RenderType
15import qualified Reporting.Region as Region
16import qualified Reporting.Report as Report
17import qualified Reporting.Helpers as Help
18import Reporting.Helpers ((<>), dullyellow, hsep, i2t, reflowParagraph, text)
22-- ALL SYNTAX ERRORS
25data Error
26 = Parse Problem
27 | BadFunctionName Int
28 | BadPattern Text
30 | CommentOnNothing
32 | SettingsOnNormalModule
33 | SettingsOnPortModule
34 | DuplicateSettingOnEffectModule Text
35 | BadSettingOnEffectModule Text
36 | NoSettingsOnEffectModule
37 | MissingManagerOnEffectModule Text
38 | UnexpectedPort Text
40 | TypeWithoutDefinition Text
42 | DuplicateArgument Text Text
43 | DuplicateFieldName Region.Region Text
44 | DuplicateValueDeclaration Text
45 | DuplicateTypeDeclaration Text
46 | DuplicateDefinition Text
47 | UnboundTypeVarsInUnion Text [Text] [Text]
48 | UnboundTypeVarsInAlias Text [Text] [Text]
49 | UnusedTypeVarsInAlias Text [Text] [Text]
50 | MessyTypeVarsInAlias Text [Text] [Text] [Text]
54-- PARSE ERRORS
57data ParseError
58 = ParseError !Int !Int !Problem
61data Problem
62 = Tab
63 | EndOfFile_Comment
64 | EndOfFile_String
65 | EndOfFile_MultiString
66 | NewLineInString
67 | BadEscape
68 | BadChar
69 | BadNumberDot
70 | BadNumberEnd
71 | BadNumberExp
72 | BadNumberHex
73 | BadNumberZero
74 | BadChunkInQualifiedCapVar
75 | HasType
76 | Equals
77 | Arrow
78 | Pipe
79 | Dot
80 | Theories [Theory]
83data Theory
84 = Keyword Text
85 | Symbol Text
86 | Variable
87 | InfixOp
88 | Digit
89 | BadIndent
90 | FreshLine
91 | Expecting Text
92 deriving (Eq, Ord)
96-- TO REPORT
99toReport :: RenderType.Localizer -> Error -> Report.Report
100toReport _localizer err =
101 case err of
102 Parse problem ->
103 problemToReport problem
105 BadFunctionName arity ->
106 Report.report
107 "BAD FUNCTION DEFINITION"
108 Nothing
109 "Complex patterns cannot be used as function names."
110 ( reflowParagraph $
111 "This function definition has " <> i2t arity <> " arguments, but instead\
112 \ of a normal name like `add` or `reverse` it has a pattern. There is no\
113 \ way to \"deconstruct\" a function with pattern matching, so this needs to\
114 \ be changed to a normal name."
115 )
117 BadPattern name ->
118 Report.report
119 "BAD PATTERN"
120 Nothing
121 ("The free variable `" <> name <> "` appears more than once in this pattern.")
122 ( Help.stack
123 [ text "Rename the variables so there are no duplicates."
124 , reflowParagraph $
125 "In Elm, pattern matching works by binding these free variables to subsections\
126 \ of the matching value. It does not make sense to have the same name for two\
127 \ different subsections though! When you say `" <> name <> "` in some code, there\
128 \ is no way for me to know which subsection you mean!"
129 ]
130 )
132 CommentOnNothing ->
133 Report.report
134 "STRAY COMMENT"
135 Nothing
136 ("This documentation comment is not followed by anything.")
137 ( reflowParagraph $
138 "All documentation comments need to be right above the declaration they\
139 \ describe. Maybe some code got deleted or commented out by accident? Or\
140 \ maybe this comment is here by accident?"
141 )
143 SettingsOnNormalModule ->
144 Report.report
145 "BAD MODULE DECLARATION"
146 Nothing
147 "A normal module can expose values, but not settings like this."
148 ( reflowParagraph $
149 "If you want a normal module, just remove this stuff. If you want to create\
150 \ an `effect module` you just forgot to use the `effect` keyword. In that\
151 \ case, just change `module` to `effect module` and you should be headed in\
152 \ the right direction!"
153 )
155 SettingsOnPortModule ->
156 Report.report
157 "BAD MODULE DECLARATION"
158 Nothing
159 "A port module can expose values, but not have settings like this."
160 ( reflowParagraph $
161 "If you want a port module, just remove this stuff. If you want to create\
162 \ a custom effect module instead (which is rare) just change `port module`\
163 \ to `effect module` and you should be headed in the right direction!"
164 )
166 DuplicateSettingOnEffectModule name ->
167 Report.report
168 "EFFECT MODULE PROBLEM"
169 Nothing
170 ("You have defined " <> Help.functionName name <> " multiple times.")
171 ( reflowParagraph $
172 "There can only be one " <> Help.functionName name
173 <> " though! Remove until there is one left."
174 )
176 BadSettingOnEffectModule name ->
177 Report.report
178 "EFFECT MODULE PROBLEM"
179 Nothing
180 ("Setting " <> Help.functionName name <> " is not recognized.")
181 ( reflowParagraph $
182 "Remove this entry and you should be all set!"
183 )
185 NoSettingsOnEffectModule ->
186 Report.report
187 "EFFECT MODULE PROBLEM"
188 Nothing
189 ("You are defining an effect module, but it has no settings.")
190 ( reflowParagraph $
191 "If you just wanted a normal module, change the keywords `effect module`\
192 \ to `module` and you should be all set. If you want a proper effect module,\
193 \ you need to specify your commands and/or subscriptions. Read more about this\
194 \ here: <http://guide.elm-lang.org/effect_managers/>"
195 )
197 MissingManagerOnEffectModule name ->
198 Report.report
199 "EFFECT MODULE PROBLEM"
200 Nothing
201 ("You are defining an effect module, but there is no " <> Help.functionName name <> " defined.")
202 ( reflowParagraph $
203 "There is a small set of top-level functions and values that must be defined\
204 \ in any complete effect module. The best thing is probably to just read more\
205 \ about effect modules here:\
206 \ <http://guide.elm-lang.org/effect_managers/>"
207 )
209 UnexpectedPort name ->
210 Report.report
211 "BAD PORT"
212 Nothing
213 ("You are declaring port " <> Help.functionName name <> " in a normal module.")
214 ( reflowParagraph $
215 "All ports must be defined in a `port module`. You should probably have just\
216 \ one of these for your project. This way all of your foreign interactions\
217 \ stay relatively organized."
218 )
220 TypeWithoutDefinition valueName ->
221 Report.report
222 "MISSING DEFINITION"
223 Nothing
224 ("There is a type annotation for `" <> valueName <> "` but there"
225 <> " is no corresponding definition!"
226 )
227 ( text $
228 "Directly below the type annotation, put a definition like:\n\n"
229 <> " " <> valueName <> " = 42"
230 )
232 DuplicateArgument funcName argName ->
233 Report.report
234 "DUPLICATE ARGUMENT"
235 Nothing
236 ( "The name `" <> argName
237 <> "` is used more than once in the arguments of "
238 <> Help.functionName funcName <> "."
239 )
240 ( reflowParagraph $
241 "Rename things until `" <> argName <> "` is used only once.\
242 \ Otherwise how can we tell which one you want when you\
243 \ say `" <> argName <> "` it in the body of your function?"
244 )
246 DuplicateFieldName region name ->
247 Report.report
248 "DUPLICATE FIELD"
249 (Just region)
250 ("This record has more than one field named `" <> name <> "`.")
251 (text "There can only be one. Do some renaming to make sure the names are distinct!")
253 DuplicateValueDeclaration name ->
254 Report.report
255 "DUPLICATE DEFINITION"
256 Nothing
257 ( "Naming multiple top-level values " <> Help.functionName name <> " makes things\n"
258 <> "ambiguous. When you say " <> Help.functionName name <> " which one do you want?"
259 )
260 ( reflowParagraph $
261 "Find all the top-level values named " <> Help.functionName name
262 <> " and do some renaming. Make sure the names are distinct!"
263 )
265 DuplicateTypeDeclaration name ->
266 Report.report
267 "DUPLICATE DEFINITION"
268 Nothing
269 ( "There are multiple types named `" <> name <> "` in this module."
270 )
271 ( reflowParagraph $
272 "Search through this module, find all the types named `"
273 <> name <> "`, and give each of them a unique name."
274 )
276 DuplicateDefinition name ->
277 Report.report
278 "DUPLICATE DEFINITION"
279 Nothing
280 ("There are multiple values named `" <> name <> "` in this let-expression."
281 )
282 ( reflowParagraph $
283 "Search through this let-expression, find all the values named `"
284 <> name <> "`, and give each of them a unique name."
285 )
287 UnboundTypeVarsInUnion typeName givenVars unbound ->
288 unboundTypeVars "type" typeName givenVars unbound
290 UnboundTypeVarsInAlias typeName givenVars unbound ->
291 unboundTypeVars "type alias" typeName givenVars unbound
293 UnusedTypeVarsInAlias typeName givenVars unused ->
294 Report.report
295 "UNUSED TYPE VARIABLES"
296 Nothing
297 ( "Type alias `" <> typeName <> "` cannot have unused type variables: "
298 <> Help.commaSep unused
299 )
300 ( Help.stack
301 [ text "You probably need to change the declaration like this:"
302 , dullyellow $ hsep $
303 map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ ["=", "..."])
304 ]
305 )
307 MessyTypeVarsInAlias typeName givenVars unused unbound ->
308 Report.report
309 "TYPE VARIABLE PROBLEMS"
310 Nothing
311 ( "Type alias `" <> typeName <> "` has some problems with type variables."
312 )
313 ( Help.stack
314 [ reflowParagraph $
315 "The declaration says it uses certain type variables ("
316 <> Help.commaSep unused <> ") but they do not appear in the aliased type. "
317 <> "Furthermore, the aliased type says it uses type variables ("
318 <> Help.commaSep unbound
319 <> ") that do not appear in the declaration."
320 , text "You probably need to change the declaration like this:"
321 , dullyellow $ hsep $
322 map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ unbound ++ ["=", "..."])
323 ]
324 )
327unboundTypeVars :: Text -> Text -> [Text] -> [Text] -> Report.Report
328unboundTypeVars declKind typeName givenVars unboundVars =
329 Report.report
330 "UNBOUND TYPE VARIABLES"
331 Nothing
332 ( Help.capitalize declKind <> " `" <> typeName <> "` must declare its use of type variable"
333 <> Help.commaSep unboundVars
334 )
335 ( Help.stack
336 [ text "You probably need to change the declaration like this:"
337 , hsep $
338 map text (declKind : typeName : givenVars)
339 ++ map (dullyellow . text) unboundVars
340 ++ map text ["=", "..."]
341 , reflowParagraph $
342 "Here's why. Imagine one `" <> typeName <> "` where `" <> head unboundVars <>
343 "` is an Int and another where it is a Bool. When we explicitly list the type\
344 \ variables, the type checker can see that they are actually different types."
345 ]
346 )
350-- PARSE ERROR TO REPORT
353parseReport :: Text -> Help.Doc -> Report.Report
354parseReport pre post =
355 Report.report "PARSE ERROR" Nothing pre post
358problemToReport :: Problem -> Report.Report
359problemToReport problem =
360 case problem of
361 Tab ->
362 parseReport
363 "I ran into a tab, but tabs are not allowed in Elm files."
364 (reflowParagraph "Replace the tab with spages.")
366 EndOfFile_Comment ->
367 parseReport
368 "I got to the end of the file while parsing a multi-line comment."
369 ( Help.stack
370 [ reflowParagraph $
371 "Multi-line comments look like {- comment -}, and it looks like\
372 \ you are missing the closing marker."
373 , reflowParagraph $
374 "Nested multi-line comments like {- this {- and this -} -} are allowed.\
375 \ That means the opening and closing markers must be balanced, just\
376 \ like parentheses in normal code. Maybe that is the problem?"
377 ]
378 )
380 EndOfFile_String ->
381 parseReport
382 "I got to the end of the file while parsing a string."
383 (reflowParagraph $
384 "Strings look like \"this\" with double quotes on each end.\
385 \ So the closing double quote seems to be missing."
386 )
388 EndOfFile_MultiString ->
389 parseReport
390 "I got to the end of the file while parsing a multi-line string."
391 (reflowParagraph $
392 "Multi-line strings begin and end with three double quotes in a\
393 \ row, like \"\"\"this\"\"\". So the closing marker seems\
394 \ to be missing."
395 )
397 NewLineInString ->
398 parseReport
399 "This string is missing the closing quote."
400 ( Help.stack
401 [ reflowParagraph $
402 "Elm strings must start and end with a double quote, like \"this\"."
403 , reflowParagraph $
404 "If you want a string that can contain newlines, use three double quotes in a\
405 \ row, like \"\"\"this\"\"\"."
406 ]
407 )
409 BadEscape ->
410 parseReport
411 "Ran into a bad escape code."
412 ( reflowParagraph
413 "Elm allows typical escape characters like \\n and \\t, but you can\
414 \ also use \\x0040 to refer to unicode characters by their hex code.\
415 \ It seems like something has gone wrong with one of these codes."
416 )
418 BadChar ->
419 parseReport
420 "Ran into a bad use of single quotes."
421 ( Help.stack
422 [ reflowParagraph $
423 "If you want to create a string, just switch to double quotes:"
424 , Help.indent 4 $
425 dullyellow (text "'this'")
426 <> text " => "
427 <> dullyellow (text "\"this\"")
428 , reflowParagraph $
429 "Unlike JavaScript, Elm distinguishes between strings like \"hello\"\
430 \ and individual characters like 'A' and '3'. It is not all strings!\
431 \ If you really do want a character though, something went wrong\
432 \ and I did not find the closing single quote."
433 ]
434 )
436 BadNumberDot ->
437 parseReport
438 "Numbers cannot end with a decimal points."
439 ( reflowParagraph $
440 "Leave it off or add some digits after it, like or 3 or 3.0"
441 )
443 BadNumberEnd ->
444 parseReport
445 "Numbers cannot have letters or underscores in them."
446 ( reflowParagraph $
447 "Maybe a space is missing between a number and a variable?"
448 )
450 BadNumberExp ->
451 parseReport
452 "If you put the letter E in a number, it should followed by more digits."
453 ( reflowParagraph $
454 "If you want to say 1000, you can also say 1e3.\
455 \ You cannot just end it with an E though!"
456 )
458 BadNumberHex ->
459 parseReport
460 "I see the start of a hex number, but not the end."
461 ( reflowParagraph $
462 "A hex number looks like 0x123ABC, where the 0x is followed by hexidecimal digits (i.e. 0123456789abcdefABCDEF)"
463 )
465 BadNumberZero ->
466 parseReport
467 "Normal numbers cannot start with a zero. Take the zeros off the front."
468 ( reflowParagraph $
469 "Only numbers like 0x0040 or 0.25 can start with a zero."
470 )
472 BadChunkInQualifiedCapVar ->
473 error "TODO"
475 HasType ->
476 parseReport
477 "Single colons should only show up in types."
478 ( reflowParagraph $
479 "Maybe you want :: instead? Or maybe you are defining\
480 \ a type annotation but there are extra spaces before it?"
481 )
483 Equals ->
484 parseReport
485 "The = operator is reserved for defining variables."
486 ( reflowParagraph $
487 "Maybe you want == instead? Or maybe you are defining a\
488 \ variable but there are extra spaces before it?"
489 )
491 Arrow ->
492 parseReport
493 "Arrows are reserved for cases and anonymous functions."
494 ( reflowParagraph $
495 "Maybe you want > or >= instead?"
496 )
498 Pipe ->
499 parseReport
500 "Vertical bars are reserved for use in type declarations."
501 ( reflowParagraph $
502 "Maybe you want || instead?"
503 )
505 Dot ->
506 parseReport
507 "Dots are for record access and decimal points."
508 ( reflowParagraph $
509 "They cannot float around on their own though, so\
510 \ maybe there is some extra whitespace?"
511 )
513 Theories allTheories ->
514 case Set.toList (Set.fromList allTheories) of
515 [] ->
516 parseReport
517 "Something went wrong when parsing this code."
518 ( reflowParagraph $
519 "I do not have any suggestions though, and I suspect there is some\
520 \ sort of bug. Can you get it down to an <http://sscce.org> and\
521 \ share it at <https://github.com/elm-lang/error-message-catalog/issues>?\
522 \ That way we can figure out how to give better advice!"
523 )
525 theories ->
526 error "TODO" (map theoryToText theories)
529theoryToText :: Theory -> Text
530theoryToText theory =
531 case theory of
532 Keyword _ ->
533 ""
535 Symbol _ ->
536 ""
538 Variable ->
539 ""
541 InfixOp ->
542 ""
544 Digit ->
545 "a digit, like 0 or 6"
547 BadIndent ->
548 ""
550 FreshLine ->
551 ""
553 Expecting _ ->
554 ""