on c5da7c0 help sign in
elk/src/Reporting/Error/Syntax.hs
718 lines 24 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Syntax
4 ( Error(..)
5 , ParseError(..)
6 , Problem(..), Theory(..), Context(..)
7 , BadOp(..), Next(..), NextDecl(..)
8 , toReport
9 )
10 where
12import qualified Data.Set as Set
13import Data.Text (Text)
15import qualified Reporting.Render.Type as RenderType
16import qualified Reporting.Region as Region
17import qualified Reporting.Report as Report
18import qualified Reporting.Helpers as Help
19import Reporting.Helpers ((<>), dullyellow, hsep, i2t, reflowParagraph, text)
23-- ALL SYNTAX ERRORS
26data Error
27 = Parse Problem
28 | BadFunctionName Int
29 | BadPattern Text
31 | CommentOnNothing
33 | SettingsOnNormalModule
34 | SettingsOnPortModule
35 | DuplicateSettingOnEffectModule Text
36 | BadSettingOnEffectModule Text
37 | NoSettingsOnEffectModule
38 | MissingManagerOnEffectModule Text
39 | UnexpectedPort Text
41 | TypeWithoutDefinition Text
43 | DuplicateArgument Text Text
44 | DuplicateFieldName Region.Region Text
45 | DuplicateValueDeclaration Text
46 | DuplicateTypeDeclaration Text
47 | DuplicateDefinition Text
48 | UnboundTypeVarsInUnion Text [Text] [Text]
49 | UnboundTypeVarsInAlias Text [Text] [Text]
50 | UnusedTypeVarsInAlias Text [Text] [Text]
51 | MessyTypeVarsInAlias Text [Text] [Text] [Text]
55-- PARSE ERRORS
58data ParseError = ParseError !Int !Int !Problem
61data Problem
62 = Tab
63 | EndOfFile_Comment
64 | EndOfFile_Shader
65 | EndOfFile_String
66 | EndOfFile_MultiString
67 | NewLineInString
68 | BadEscape
69 | BadChar
70 | BadNumberDot
71 | BadNumberEnd
72 | BadNumberExp
73 | BadNumberHex
74 | BadNumberZero
75 | BadShader Text
76 | BadOp BadOp [Context]
77 | Theories [Context] [Theory]
80data BadOp = HasType | Equals | Arrow | Pipe | Dot
83data Theory
84 = Expecting Next
85 | Keyword Text
86 | Symbol Text
87 | LowVar
88 | CapVar
89 | InfixOp
90 | Digit
91 | EndOfFile
92 | FreshLine NextDecl
93 | BadSpace
94 deriving (Eq, Ord, Show)
97data Next = Expr | ElseBranch | Arg | Pattern | Type | Listing | Exposing
98 deriving (Eq, Ord, Show)
101data NextDecl = ModuleDecl | DocCommentDecl | ImportDecl | OtherDecl
102 deriving (Eq, Ord, Show)
105data Context
106 = ExprIf
107 | ExprLet
108 | ExprFunc
109 | ExprCase
110 | ExprList
111 | ExprTuple
112 | ExprRecord
113 ----------------
114 | Definition Text
115 | Annotation Text
116 ----------------
117 | TypeTuple
118 | TypeRecord
119 ----------------
120 | PatternList
121 | PatternTuple
122 | PatternRecord
123 ----------------
124 | Module
125 | Import
126 | DocComment
127 | TypeUnion
128 | TypeAlias
129 | Infix
130 | Port
131 deriving (Eq, Ord, Show)
135-- TO REPORT
138toReport :: RenderType.Localizer -> Error -> Report.Report
139toReport _localizer err =
140 case err of
141 Parse problem ->
142 problemToReport problem
144 BadFunctionName arity ->
145 Report.report
146 "BAD FUNCTION DEFINITION"
147 Nothing
148 "Complex patterns cannot be used as function names."
149 ( reflowParagraph $
150 "This function definition has " <> i2t arity <> " arguments, but instead\
151 \ of a normal name like `add` or `reverse` it has a pattern. There is no\
152 \ way to \"deconstruct\" a function with pattern matching, so this needs to\
153 \ be changed to a normal name."
154 )
156 BadPattern name ->
157 Report.report
158 "BAD PATTERN"
159 Nothing
160 ("The free variable `" <> name <> "` appears more than once in this pattern.")
161 ( Help.stack
162 [ text "Rename the variables so there are no duplicates."
163 , reflowParagraph $
164 "In Elm, pattern matching works by binding these free variables to subsections\
165 \ of the matching value. It does not make sense to have the same name for two\
166 \ different subsections though! When you say `" <> name <> "` in some code, there\
167 \ is no way for me to know which subsection you mean!"
168 ]
169 )
171 CommentOnNothing ->
172 Report.report
173 "STRAY COMMENT"
174 Nothing
175 ("This documentation comment is not followed by anything.")
176 ( reflowParagraph $
177 "All documentation comments need to be right above the declaration they\
178 \ describe. Maybe some code got deleted or commented out by accident? Or\
179 \ maybe this comment is here by accident?"
180 )
182 SettingsOnNormalModule ->
183 Report.report
184 "BAD MODULE DECLARATION"
185 Nothing
186 "A normal module can expose values, but not settings like this."
187 ( reflowParagraph $
188 "If you want a normal module, just remove this stuff. If you want to create\
189 \ an `effect module` you forgot to use the `effect` keyword. In that\
190 \ case, change `module` to `effect module` and you should be headed in\
191 \ the right direction!"
192 )
194 SettingsOnPortModule ->
195 Report.report
196 "BAD MODULE DECLARATION"
197 Nothing
198 "A port module can expose values, but not have settings like this."
199 ( reflowParagraph $
200 "If you want a port module, just remove this stuff. If you want to create\
201 \ a custom effect module instead (which is rare) change `port module`\
202 \ to `effect module` and you should be headed in the right direction!"
203 )
205 DuplicateSettingOnEffectModule name ->
206 Report.report
207 "EFFECT MODULE PROBLEM"
208 Nothing
209 ("You have defined " <> Help.functionName name <> " multiple times.")
210 ( reflowParagraph $
211 "There can only be one " <> Help.functionName name
212 <> " though! Remove until there is one left."
213 )
215 BadSettingOnEffectModule name ->
216 Report.report
217 "EFFECT MODULE PROBLEM"
218 Nothing
219 ("Setting " <> Help.functionName name <> " is not recognized.")
220 ( reflowParagraph $
221 "Remove this entry and you should be all set!"
222 )
224 NoSettingsOnEffectModule ->
225 Report.report
226 "EFFECT MODULE PROBLEM"
227 Nothing
228 ("You are defining an effect module, but it has no settings.")
229 ( reflowParagraph $
230 "If you just wanted a normal module, change the keywords `effect module`\
231 \ to `module` and you should be all set. If you want a proper effect module,\
232 \ you need to specify your commands and/or subscriptions. Read more about this\
233 \ here: <http://guide.elm-lang.org/effect_managers/>"
234 )
236 MissingManagerOnEffectModule name ->
237 Report.report
238 "EFFECT MODULE PROBLEM"
239 Nothing
240 ("You are defining an effect module, but there is no " <> Help.functionName name <> " defined.")
241 ( reflowParagraph $
242 "There is a small set of top-level functions and values that must be defined\
243 \ in any complete effect module. The best thing is probably to just read more\
244 \ about effect modules here:\
245 \ <http://guide.elm-lang.org/effect_managers/>"
246 )
248 UnexpectedPort name ->
249 Report.report
250 "BAD PORT"
251 Nothing
252 ("You are declaring port " <> Help.functionName name <> " in a normal module.")
253 ( reflowParagraph $
254 "All ports must be defined in a `port module`. You should probably have only\
255 \ one of these for your project. This way all of your foreign interactions\
256 \ stay relatively organized."
257 )
259 TypeWithoutDefinition valueName ->
260 Report.report
261 "MISSING DEFINITION"
262 Nothing
263 ("There is a type annotation for `" <> valueName <> "` but there"
264 <> " is no corresponding definition!"
265 )
266 ( text $
267 "Directly below the type annotation, put a definition like:\n\n"
268 <> " " <> valueName <> " = 42"
269 )
271 DuplicateArgument funcName argName ->
272 Report.report
273 "DUPLICATE ARGUMENT"
274 Nothing
275 ( "The name `" <> argName
276 <> "` is used more than once in the arguments of "
277 <> Help.functionName funcName <> "."
278 )
279 ( reflowParagraph $
280 "Rename things until `" <> argName <> "` is used only once.\
281 \ Otherwise how can we tell which one you want when you\
282 \ say `" <> argName <> "` it in the body of your function?"
283 )
285 DuplicateFieldName region name ->
286 Report.report
287 "DUPLICATE FIELD"
288 (Just region)
289 ("This record has more than one field named `" <> name <> "`.")
290 (text "There can only be one. Do some renaming to make sure the names are distinct!")
292 DuplicateValueDeclaration name ->
293 Report.report
294 "DUPLICATE DEFINITION"
295 Nothing
296 ( "Naming multiple top-level values " <> Help.functionName name <> " makes things\n"
297 <> "ambiguous. When you say " <> Help.functionName name <> " which one do you want?"
298 )
299 ( reflowParagraph $
300 "Find all the top-level values named " <> Help.functionName name
301 <> " and do some renaming. Make sure the names are distinct!"
302 )
304 DuplicateTypeDeclaration name ->
305 Report.report
306 "DUPLICATE DEFINITION"
307 Nothing
308 ( "There are multiple types named `" <> name <> "` in this module."
309 )
310 ( reflowParagraph $
311 "Search through this module, find all the types named `"
312 <> name <> "`, and give each of them a unique name."
313 )
315 DuplicateDefinition name ->
316 Report.report
317 "DUPLICATE DEFINITION"
318 Nothing
319 ("There are multiple values named `" <> name <> "` in this let-expression."
320 )
321 ( reflowParagraph $
322 "Search through this let-expression, find all the values named `"
323 <> name <> "`, and give each of them a unique name."
324 )
326 UnboundTypeVarsInUnion typeName givenVars unbound ->
327 unboundTypeVars "type" typeName givenVars unbound
329 UnboundTypeVarsInAlias typeName givenVars unbound ->
330 unboundTypeVars "type alias" typeName givenVars unbound
332 UnusedTypeVarsInAlias typeName givenVars unused ->
333 Report.report
334 "UNUSED TYPE VARIABLES"
335 Nothing
336 ( "Type alias `" <> typeName <> "` cannot have unused type variables: "
337 <> Help.commaSep unused
338 )
339 ( Help.stack
340 [ text "You probably need to change the declaration like this:"
341 , dullyellow $ hsep $
342 map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ ["=", "..."])
343 ]
344 )
346 MessyTypeVarsInAlias typeName givenVars unused unbound ->
347 Report.report
348 "TYPE VARIABLE PROBLEMS"
349 Nothing
350 ( "Type alias `" <> typeName <> "` has some problems with type variables."
351 )
352 ( Help.stack
353 [ reflowParagraph $
354 "The declaration says it uses certain type variables ("
355 <> Help.commaSep unused <> ") but they do not appear in the aliased type. "
356 <> "Furthermore, the aliased type says it uses type variables ("
357 <> Help.commaSep unbound
358 <> ") that do not appear in the declaration."
359 , text "You probably need to change the declaration like this:"
360 , dullyellow $ hsep $
361 map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ unbound ++ ["=", "..."])
362 ]
363 )
366unboundTypeVars :: Text -> Text -> [Text] -> [Text] -> Report.Report
367unboundTypeVars declKind typeName givenVars unboundVars =
368 Report.report
369 "UNBOUND TYPE VARIABLES"
370 Nothing
371 ( Help.capitalize declKind <> " `" <> typeName <> "` must declare its use of type variable"
372 <> Help.commaSep unboundVars
373 )
374 ( Help.stack
375 [ text "You probably need to change the declaration like this:"
376 , hsep $
377 map text (declKind : typeName : givenVars)
378 ++ map (dullyellow . text) unboundVars
379 ++ map text ["=", "..."]
380 , reflowParagraph $
381 "Here's why. Imagine one `" <> typeName <> "` where `" <> head unboundVars <>
382 "` is an Int and another where it is a Bool. When we explicitly list the type\
383 \ variables, the type checker can see that they are actually different types."
384 ]
385 )
389-- PARSE ERROR TO REPORT
392parseReport :: Text -> Help.Doc -> Report.Report
393parseReport pre post =
394 Report.report "PARSE ERROR" Nothing pre post
397problemToReport :: Problem -> Report.Report
398problemToReport problem =
399 case problem of
400 Tab ->
401 parseReport
402 "I ran into a tab, but tabs are not allowed in Elm files."
403 (reflowParagraph "Replace the tab with spages.")
405 EndOfFile_Comment ->
406 parseReport
407 "I got to the end of the file while parsing a multi-line comment."
408 ( Help.stack
409 [ reflowParagraph $
410 "Multi-line comments look like {- comment -}, and it looks like\
411 \ you are missing the closing marker."
412 , reflowParagraph $
413 "Nested multi-line comments like {- this {- and this -} -} are allowed.\
414 \ That means the opening and closing markers must be balanced, just\
415 \ like parentheses in normal code. Maybe that is the problem?"
416 ]
417 )
419 EndOfFile_Shader ->
420 parseReport
421 "I got to the end of the file while parsing a GLSL block."
422 (reflowParagraph $
423 "A shader should be defined in a block like this: [glsl| ... |]"
424 )
426 EndOfFile_String ->
427 parseReport
428 "I got to the end of the file while parsing a string."
429 (reflowParagraph $
430 "Strings look like \"this\" with double quotes on each end.\
431 \ So the closing double quote seems to be missing."
432 )
434 EndOfFile_MultiString ->
435 parseReport
436 "I got to the end of the file while parsing a multi-line string."
437 (reflowParagraph $
438 "Multi-line strings begin and end with three double quotes in a\
439 \ row, like \"\"\"this\"\"\". So the closing marker seems\
440 \ to be missing."
441 )
443 NewLineInString ->
444 parseReport
445 "This string is missing the closing quote."
446 ( Help.stack
447 [ reflowParagraph $
448 "Elm strings must start and end with a double quote, like \"this\"."
449 , reflowParagraph $
450 "If you want a string that can contain newlines, use three double quotes in a\
451 \ row, like \"\"\"this\"\"\"."
452 ]
453 )
455 BadEscape ->
456 parseReport
457 "Ran into a bad escape code."
458 ( reflowParagraph
459 "Elm allows typical escape characters like \\n and \\t, but you can\
460 \ also use \\x0040 to refer to unicode characters by their hex code.\
461 \ It seems like something has gone wrong with one of these codes."
462 )
464 BadChar ->
465 parseReport
466 "Ran into a bad use of single quotes."
467 ( Help.stack
468 [ reflowParagraph $
469 "If you want to create a string, switch to double quotes:"
470 , Help.indent 4 $
471 dullyellow (text "'this'")
472 <> text " => "
473 <> dullyellow (text "\"this\"")
474 , reflowParagraph $
475 "Unlike JavaScript, Elm distinguishes between strings like \"hello\"\
476 \ and individual characters like 'A' and '3'. It is not all strings!\
477 \ If you really do want a character though, something went wrong\
478 \ and I did not find the closing single quote."
479 ]
480 )
482 BadNumberDot ->
483 parseReport
484 "Numbers cannot end with a decimal points."
485 ( reflowParagraph $
486 "Leave it off or add some digits after it, like or 3 or 3.0"
487 )
489 BadNumberEnd ->
490 parseReport
491 "Numbers cannot have letters or underscores in them."
492 ( reflowParagraph $
493 "Maybe a space is missing between a number and a variable?"
494 )
496 BadNumberExp ->
497 parseReport
498 "If you put the letter E in a number, it should followed by more digits."
499 ( reflowParagraph $
500 "If you want to say 1000, you can also say 1e3.\
501 \ You cannot just end it with an E though!"
502 )
504 BadNumberHex ->
505 parseReport
506 "I see the start of a hex number, but not the end."
507 ( reflowParagraph $
508 "A hex number looks like 0x123ABC, where the 0x is followed by hexidecimal digits (i.e. 0123456789abcdefABCDEF)"
509 )
511 BadNumberZero ->
512 parseReport
513 "Normal numbers cannot start with a zero. Take the zeros off the front."
514 ( reflowParagraph $
515 "Only numbers like 0x0040 or 0.25 can start with a zero."
516 )
518 BadShader msg ->
519 parseReport
520 "I ran into a problem while parsing this GLSL block."
521 (reflowParagraph msg)
523 BadOp op context ->
524 case op of
525 HasType ->
526 badOp context "the \"has type\" operator (:)"
527 "type annotations and record types"
528 "Maybe you want :: instead? Or maybe your indentation is a bit off?"
530 Equals ->
531 badOp context "an equals sign (=)"
532 "definitions"
533 "Maybe you want == instead? Or maybe your indentation is a bit off?"
535 Arrow ->
536 if isCaseRelated context then
537 parseReport
538 "I am parsing a case expression, but this arrow (->) is out of place."
539 ( reflowParagraph $
540 "Each branch in a case expression needs to start on the same\
541 \ column, so maybe this branch is indented too much?"
542 )
544 else
545 badOp context "an arrow (->)"
546 "cases expressions and anonymous functions"
547 "Maybe you want > or >= instead?"
549 Pipe ->
550 badOp context "a vertical bar (|)"
551 "type declarations"
552 "Maybe you want || instead?"
554 Dot ->
555 parseReport
556 "I was not expecting a dot (.) here."
557 ( reflowParagraph $
558 "Dots are for record access and decimal points, so\
559 \ they cannot float around on their own. Maybe\
560 \ there is some extra whitespace?"
561 )
563 Theories context allTheories ->
564 case Set.toList (Set.fromList allTheories) of
565 [] ->
566 parseReport
567 "Something went wrong when parsing this code."
568 ( reflowParagraph $
569 "I do not have any suggestions though, and I suspect there is some\
570 \ sort of bug. Can you get it down to an <http://sscce.org> and\
571 \ share it at <https://github.com/elm-lang/error-message-catalog/issues>?\
572 \ That way we can figure out how to give better advice!"
573 )
575 [theory] ->
576 parseReport
577 "I got confused while trying to parse your code."
578 ( reflowParagraph $
579 contextToText context
580 <> " right now, so I was expecting "
581 <> theoryToText theory
582 )
584 theories ->
585 parseReport
586 "I got confused while trying to parse your code."
587 ( Help.vcat $
588 [ reflowParagraph $
589 contextToText context <> ", so I was expecting:"
590 , text ""
591 ]
592 ++ map (bullet . theoryToText) theories
593 )
596bullet :: Text -> Help.Doc
597bullet point =
598 Help.indent 2 (reflowParagraph point)
601badOp :: [Context] -> Text -> Text -> Text -> Report.Report
602badOp context opName setting hint =
603 parseReport ("I was not expecting " <> opName <> " here.") $
604 reflowParagraph $
605 "It should only appear in " <> setting <> ", but "
606 <> contextToText context <> ". " <> hint
609contextToText :: [Context] -> Text
610contextToText contextStack =
611 case contextStack of
612 [] ->
613 "I am in between top-level declarations right now."
615 context : rest ->
616 let anchor = getAnchor rest in
617 "I think I am parsing " <>
618 case context of
619 ExprIf -> "an `if`" <> anchor
620 ExprLet -> "a `let`" <> anchor
621 ExprFunc -> "an anonymous function" <> anchor
622 ExprCase -> "a `case`" <> anchor
623 ExprList -> "a list" <> anchor
624 ExprTuple -> "a tuple" <> anchor
625 ExprRecord -> "a record" <> anchor
626 Definition name -> "the definition of `" <> name <> "`"
627 Annotation name -> "the type annotation for `" <> name <> "`"
628 TypeTuple -> "a tuple type" <> anchor
629 TypeRecord -> "a record type" <> anchor
630 PatternList -> "a pattern" <> anchor
631 PatternTuple -> "a pattern" <> anchor
632 PatternRecord -> "a pattern" <> anchor
633 Module -> "a module declaration"
634 Import -> "an import"
635 DocComment -> "a documentation comment"
636 TypeUnion -> "a union type"
637 TypeAlias -> "a type alias"
638 Infix -> "an infix declaration"
639 Port -> "a port declaration"
642getAnchor :: [Context] -> Text
643getAnchor context =
644 case context of
645 [] -> ""
646 Definition name : _ -> " in the definition of `" <> name <> "`"
647 Annotation name : _ -> " in the type annotation for `" <> name <> "`"
648 _ : rest -> getAnchor rest
651isCaseRelated :: [Context] -> Bool
652isCaseRelated context =
653 case context of
654 [] -> False
655 ExprCase : _ -> True
656 _ : rest -> isCaseRelated rest
659theoryToText :: Theory -> Text
660theoryToText theory =
661 case theory of
662 Keyword keyword ->
663 "the `" <> keyword <> "` keyword"
665 Symbol symbol ->
666 case symbol of
667 "=" -> "an equals sign (=)"
668 "->" -> "an arrow (->) followed by an expression"
669 ":" -> "the \"has type\" symbol (:) followed by a type"
670 "," -> "a comma"
671 "|" -> "a vertical bar (|)"
672 "::" -> "the cons operator (::)"
673 "." -> "a dot (.)"
674 "-" -> "a minus sign (-)"
675 "_" -> "an underscore"
676 "(" -> "a left paren, for grouping or starting tuples"
677 ")" -> "a closing paren"
678 "[" -> "a left square bracket, for starting lists"
679 "]" -> "a right square bracket, to end a list"
680 "{" -> "a left curly brace, for starting records"
681 "}" -> "a right curly brace, to end a record"
682 _ -> "the (" <> symbol <> ") symbol"
684 LowVar ->
685 "a lower-case variable, like `x` or `user`"
687 CapVar ->
688 "an upper-case variable, like `Maybe` or `Just`"
690 InfixOp ->
691 "an infix operator, like + or =="
693 Digit ->
694 "a digit from 0 to 9"
696 EndOfFile ->
697 "the end of the file"
699 BadSpace ->
700 "more indentation"
702 FreshLine nextDecl ->
703 "a fresh line with no indentation for " <>
704 case nextDecl of
705 ModuleDecl -> "an `import`"
706 DocCommentDecl -> "an `import`"
707 ImportDecl -> "an `import` or a declarations"
708 OtherDecl -> "a top-level declaration"
710 Expecting next ->
711 case next of
712 Expr -> "an expression, like x or 42"
713 ElseBranch -> "an `else` branch. An `if` must handle both possibilities!"
714 Arg -> "an argument, like `name` or `total`"
715 Pattern -> "a pattern, like `name` or (Just x)"
716 Type -> "a type, like Int or (List String)"
717 Listing -> "a list of exposed values and types, like (..) or (x,y,z)"
718 Exposing -> "something like `exposing (..)`"