on 8a89c86 help sign in
elk/src/Reporting/Error/Syntax.hs
957 lines 31 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Syntax
4 ( Error(..)
5 , ParseError(..)
6 , Problem(..), Theory(..)
7 , EscapeProblem(..)
8 , ContextStack, Context(..)
9 , BadOp(..), Next(..)
10 , toReport
11 )
12 where
14import qualified Data.Set as Set
15import qualified Data.Text as Text
16import Data.Text (Text)
18import qualified Reporting.Render.Type as RenderType
19import qualified Reporting.Region as R
20import qualified Reporting.Report as Report
21import qualified Reporting.Helpers as Help
22import Reporting.Helpers ((<>), dullyellow, green, hsep, i2t, reflowParagraph, text)
26-- ALL SYNTAX ERRORS
29data Error
30 = Parse (Maybe R.Region) Problem
31 | BadFunctionName Int
32 | BadPattern Text
34 | CommentOnNothing
36 | SettingsOnNormalModule
37 | SettingsOnPortModule
38 | DuplicateSettingOnEffectModule Text
39 | BadSettingOnEffectModule Text
40 | NoSettingsOnEffectModule
41 | MissingManagerOnEffectModule Text
42 | UnexpectedPort Text
44 | TypeWithoutDefinition Text
46 | DuplicateArgument Text Text
47 | DuplicateFieldName R.Region Text
48 | DuplicateValueDeclaration Text
49 | DuplicateTypeDeclaration Text
50 | DuplicateDefinition Text
51 | UnboundTypeVarsInUnion Text [Text] [Text]
52 | UnboundTypeVarsInAlias Text [Text] [Text]
53 | UnusedTypeVarsInAlias Text [Text] [Text]
54 | MessyTypeVarsInAlias Text [Text] [Text] [Text]
58-- PARSE ERRORS
61data ParseError = ParseError !Int !Int !Problem
64data Problem
65 = Tab
66 | EndOfFile_Comment
67 | EndOfFile_Shader
68 | EndOfFile_String
69 | EndOfFile_MultiString
70 | EndOfFile_Char
71 | NewLineInString
72 | NewLineInChar
73 | BadEscape Int EscapeProblem
74 | BadChar Int
75 | BadNumberDot
76 | BadNumberEnd
77 | BadNumberExp
78 | BadNumberHex
79 | BadNumberZero
80 | BadShader Text
81 | BadUnderscore Char
82 | BadOp BadOp ContextStack
83 | Theories ContextStack [Theory]
86data EscapeProblem
87 = UnknownEscape
88 | UnicodeSyntax
89 | UnicodeRange
90 | UnicodeLength Int Text
93data BadOp = HasType | Equals | Arrow | Pipe | Dot
96data Theory
97 = Keyword Text
98 | Symbol Text
99 | Expecting Next
100 | LowVar
101 | CapVar
102 | InfixOp
103 | Digit
104 | BadSpace
105 deriving (Eq, Ord)
108data Next
109 = Decl
110 | Expr
111 | AfterOpExpr Text
112 | ElseBranch
113 | Arg
114 | Pattern
115 | Type
116 | Listing
117 | Exposing
118 deriving (Eq, Ord)
121type ContextStack = [(Context, R.Position)]
124data Context
125 = ExprIf
126 | ExprLet
127 | ExprFunc
128 | ExprCase
129 | ExprList
130 | ExprTuple
131 | ExprRecord
132 ----------------
133 | Definition Text
134 | Annotation Text
135 ----------------
136 | TypeTuple
137 | TypeRecord
138 ----------------
139 | PatternList
140 | PatternTuple
141 | PatternRecord
142 ----------------
143 | Module
144 | Import
145 | TypeUnion
146 | TypeAlias
147 | Infix
148 | Port
149 deriving (Eq, Ord, Show)
153-- TO REPORT
156toReport :: RenderType.Localizer -> Error -> Report.Report
157toReport _localizer err =
158 case err of
159 Parse subRegion problem ->
160 problemToReport subRegion problem
162 BadFunctionName arity ->
163 Report.report
164 "BAD FUNCTION DEFINITION"
165 Nothing
166 "Complex patterns cannot be used as function names."
167 ( reflowParagraph $
168 "This function definition has " <> i2t arity <> " arguments, but instead\
169 \ of a normal name like `add` or `reverse` it has a pattern. There is no\
170 \ way to \"deconstruct\" a function with pattern matching, so this needs to\
171 \ be changed to a normal name."
172 )
174 BadPattern name ->
175 Report.report
176 "BAD PATTERN"
177 Nothing
178 ("The free variable `" <> name <> "` appears more than once in this pattern.")
179 ( Help.stack
180 [ text "Rename the variables so there are no duplicates."
181 , reflowParagraph $
182 "In Elm, pattern matching works by binding these free variables to subsections\
183 \ of the matching value. It does not make sense to have the same name for two\
184 \ different subsections though! When you say `" <> name <> "` in some code, there\
185 \ is no way for me to know which subsection you mean!"
186 ]
187 )
189 CommentOnNothing ->
190 Report.report
191 "STRAY COMMENT"
192 Nothing
193 ("This documentation comment is not followed by anything.")
194 ( reflowParagraph $
195 "All documentation comments need to be right above the declaration they\
196 \ describe. Maybe some code got deleted or commented out by accident? Or\
197 \ maybe this comment is here by accident?"
198 )
200 SettingsOnNormalModule ->
201 Report.report
202 "BAD MODULE DECLARATION"
203 Nothing
204 "A normal module can expose values, but not settings like this."
205 ( reflowParagraph $
206 "If you want a normal module, just remove this stuff. If you want to create\
207 \ an `effect module` you forgot to use the `effect` keyword. In that\
208 \ case, change `module` to `effect module` and you should be headed in\
209 \ the right direction!"
210 )
212 SettingsOnPortModule ->
213 Report.report
214 "BAD MODULE DECLARATION"
215 Nothing
216 "A port module can expose values, but not have settings like this."
217 ( reflowParagraph $
218 "If you want a port module, just remove this stuff. If you want to create\
219 \ a custom effect module instead (which is rare) change `port module`\
220 \ to `effect module` and you should be headed in the right direction!"
221 )
223 DuplicateSettingOnEffectModule name ->
224 Report.report
225 "EFFECT MODULE PROBLEM"
226 Nothing
227 ("You have defined " <> Help.functionName name <> " multiple times.")
228 ( reflowParagraph $
229 "There can only be one " <> Help.functionName name
230 <> " though! Remove until there is one left."
231 )
233 BadSettingOnEffectModule name ->
234 Report.report
235 "EFFECT MODULE PROBLEM"
236 Nothing
237 ("Setting " <> Help.functionName name <> " is not recognized.")
238 ( reflowParagraph $
239 "Remove this entry and you should be all set!"
240 )
242 NoSettingsOnEffectModule ->
243 Report.report
244 "EFFECT MODULE PROBLEM"
245 Nothing
246 ("You are defining an effect module, but it has no settings.")
247 ( reflowParagraph $
248 "If you just wanted a normal module, change the keywords `effect module`\
249 \ to `module` and you should be all set. If you want a proper effect module,\
250 \ you need to specify your commands and/or subscriptions. Read more about this\
251 \ here: <http://guide.elm-lang.org/effect_managers/>"
252 )
254 MissingManagerOnEffectModule name ->
255 Report.report
256 "EFFECT MODULE PROBLEM"
257 Nothing
258 ("You are defining an effect module, but there is no " <> Help.functionName name <> " defined.")
259 ( reflowParagraph $
260 "There is a small set of top-level functions and values that must be defined\
261 \ in any complete effect module. The best thing is probably to just read more\
262 \ about effect modules here:\
263 \ <http://guide.elm-lang.org/effect_managers/>"
264 )
266 UnexpectedPort name ->
267 Report.report
268 "BAD PORT"
269 Nothing
270 ("You are declaring port " <> Help.functionName name <> " in a normal module.")
271 ( reflowParagraph $
272 "All ports must be defined in a `port module`. You should probably have only\
273 \ one of these for your project. This way all of your foreign interactions\
274 \ stay relatively organized."
275 )
277 TypeWithoutDefinition valueName ->
278 Report.report
279 "MISSING DEFINITION"
280 Nothing
281 ("There is a type annotation for `" <> valueName <> "` but there"
282 <> " is no corresponding definition!"
283 )
284 ( text $
285 "Directly below the type annotation, put a definition like:\n\n"
286 <> " " <> valueName <> " = 42"
287 )
289 DuplicateArgument funcName argName ->
290 Report.report
291 "DUPLICATE ARGUMENT"
292 Nothing
293 ( "The name `" <> argName
294 <> "` is used more than once in the arguments of "
295 <> Help.functionName funcName <> "."
296 )
297 ( reflowParagraph $
298 "Rename things until `" <> argName <> "` is used only once.\
299 \ Otherwise how can we tell which one you want when you\
300 \ say `" <> argName <> "` it in the body of your function?"
301 )
303 DuplicateFieldName region name ->
304 Report.report
305 "DUPLICATE FIELD"
306 (Just region)
307 ("This record has more than one field named `" <> name <> "`.")
308 (text "There can only be one. Do some renaming to make sure the names are distinct!")
310 DuplicateValueDeclaration name ->
311 Report.report
312 "DUPLICATE DEFINITION"
313 Nothing
314 ( "Naming multiple top-level values " <> Help.functionName name <> " makes things\n"
315 <> "ambiguous. When you say " <> Help.functionName name <> " which one do you want?"
316 )
317 ( reflowParagraph $
318 "Find all the top-level values named " <> Help.functionName name
319 <> " and do some renaming. Make sure the names are distinct!"
320 )
322 DuplicateTypeDeclaration name ->
323 Report.report
324 "DUPLICATE DEFINITION"
325 Nothing
326 ( "There are multiple types named `" <> name <> "` in this module."
327 )
328 ( reflowParagraph $
329 "Search through this module, find all the types named `"
330 <> name <> "`, and give each of them a unique name."
331 )
333 DuplicateDefinition name ->
334 Report.report
335 "DUPLICATE DEFINITION"
336 Nothing
337 ("There are multiple values named `" <> name <> "` in this let-expression."
338 )
339 ( reflowParagraph $
340 "Search through this let-expression, find all the values named `"
341 <> name <> "`, and give each of them a unique name."
342 )
344 UnboundTypeVarsInUnion typeName givenVars unbound ->
345 unboundTypeVars "type" typeName givenVars unbound
347 UnboundTypeVarsInAlias typeName givenVars unbound ->
348 unboundTypeVars "type alias" typeName givenVars unbound
350 UnusedTypeVarsInAlias typeName givenVars unused ->
351 Report.report
352 "UNUSED TYPE VARIABLES"
353 Nothing
354 ( "Type alias `" <> typeName <> "` cannot have unused type variables: "
355 <> Help.commaSep unused
356 )
357 ( Help.stack
358 [ text "You probably need to change the declaration like this:"
359 , dullyellow $ hsep $
360 map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ ["=", "..."])
361 ]
362 )
364 MessyTypeVarsInAlias typeName givenVars unused unbound ->
365 Report.report
366 "TYPE VARIABLE PROBLEMS"
367 Nothing
368 ( "Type alias `" <> typeName <> "` has some problems with type variables."
369 )
370 ( Help.stack
371 [ reflowParagraph $
372 "The declaration says it uses certain type variables ("
373 <> Help.commaSep unused <> ") but they do not appear in the aliased type. "
374 <> "Furthermore, the aliased type says it uses type variables ("
375 <> Help.commaSep unbound
376 <> ") that do not appear in the declaration."
377 , text "You probably need to change the declaration like this:"
378 , dullyellow $ hsep $
379 map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ unbound ++ ["=", "..."])
380 ]
381 )
384unboundTypeVars :: Text -> Text -> [Text] -> [Text] -> Report.Report
385unboundTypeVars declKind typeName givenVars unboundVars =
386 Report.report
387 "UNBOUND TYPE VARIABLES"
388 Nothing
389 ( Help.capitalize declKind <> " `" <> typeName <> "` must declare its use of type variable"
390 <> Help.commaSep unboundVars
391 )
392 ( Help.stack
393 [ text "You probably need to change the declaration like this:"
394 , hsep $
395 map text (declKind : typeName : givenVars)
396 ++ map (dullyellow . text) unboundVars
397 ++ map text ["=", "..."]
398 , reflowParagraph $
399 "Here's why. Imagine one `" <> typeName <> "` where `" <> head unboundVars <>
400 "` is an Int and another where it is a Bool. When we explicitly list the type\
401 \ variables, the type checker can see that they are actually different types."
402 ]
403 )
407-- PARSE ERROR TO REPORT
410makeParseReport :: Maybe R.Region -> Text -> Help.Doc -> Report.Report
411makeParseReport subRegion pre post =
412 Report.report "PARSE ERROR" subRegion pre post
415problemToReport :: Maybe R.Region -> Problem -> Report.Report
416problemToReport subRegion problem =
417 let parseReport = makeParseReport subRegion in
418 case problem of
419 Tab ->
420 parseReport
421 "I ran into a tab, but tabs are not allowed in Elm files."
422 (reflowParagraph "Replace the tab with spages.")
424 EndOfFile_Comment ->
425 parseReport
426 "I got to the end of the file while parsing a multi-line comment."
427 ( Help.stack
428 [ reflowParagraph $
429 "Multi-line comments look like {- comment -}, and it looks like\
430 \ you are missing the closing marker."
431 , reflowParagraph $
432 "Nested multi-line comments like {- this {- and this -} -} are allowed.\
433 \ That means the opening and closing markers must be balanced, just\
434 \ like parentheses in normal code. Maybe that is the problem?"
435 ]
436 )
438 EndOfFile_Shader ->
439 parseReport
440 "I got to the end of the file while parsing a GLSL block."
441 (reflowParagraph $
442 "A shader should be defined in a block like this: [glsl| ... |]"
443 )
445 EndOfFile_String ->
446 parseReport
447 "I got to the end of the file while parsing a string."
448 (reflowParagraph $
449 "Strings look like \"this\" with double quotes on each end.\
450 \ So the closing double quote seems to be missing."
451 )
453 EndOfFile_MultiString ->
454 parseReport
455 "I got to the end of the file while parsing a multi-line string."
456 (reflowParagraph $
457 "Multi-line strings begin and end with three double quotes in a\
458 \ row, like \"\"\"this\"\"\". So the closing marker seems\
459 \ to be missing."
460 )
462 EndOfFile_Char ->
463 parseReport
464 "I got to the end of the file while parsing a character."
465 (reflowParagraph $
466 "Characters look like 'x' with single quotes on each end.\
467 \ So the closing single quote seems to be missing."
468 )
470 NewLineInString ->
471 parseReport
472 "This string is missing the closing quote."
473 ( Help.stack
474 [ reflowParagraph $
475 "Elm strings must start and end with a double quote, like \"this\"."
476 , reflowParagraph $
477 "If you want a string that can contain newlines, use three double quotes in a\
478 \ row, like \"\"\"this\"\"\"."
479 ]
480 )
482 NewLineInChar ->
483 parseReport
484 "This character is missing the closing quote."
485 ( reflowParagraph $
486 "Elm characters must start and end with a single quote, like 'a' and 'b'"
487 )
489 BadEscape _ escapeProblem ->
490 uncurry parseReport (escapeProblemToReport escapeProblem)
492 BadChar _ ->
493 parseReport
494 "Ran into a bad use of single quotes."
495 ( Help.stack
496 [ reflowParagraph $
497 "If you want to create a string, switch to double quotes:"
498 , Help.indent 4 $
499 dullyellow (text "'this'")
500 <> text " => "
501 <> green (text "\"this\"")
502 , reflowParagraph $
503 "Unlike JavaScript, Elm distinguishes between strings like \"hello\"\
504 \ and individual characters like 'A' and '3'. If you really do want\
505 \ a character though, something went wrong and I did not find the\
506 \ closing single quote."
507 ]
508 )
510 BadNumberDot ->
511 parseReport
512 "Numbers cannot end with a decimal points."
513 ( reflowParagraph $
514 "Leave it off or add some digits after it, like or 3 or 3.0"
515 )
517 BadNumberEnd ->
518 parseReport
519 "Numbers cannot have letters or underscores in them."
520 ( reflowParagraph $
521 "Maybe a space is missing between a number and a variable?"
522 )
524 BadNumberExp ->
525 parseReport
526 "If you put the letter E in a number, it should followed by more digits."
527 ( reflowParagraph $
528 "If you want to say 1000, you can also say 1e3.\
529 \ You cannot just end it with an E though!"
530 )
532 BadNumberHex ->
533 parseReport
534 "I see the start of a hex number, but not the end."
535 ( reflowParagraph $
536 "A hex number looks like 0x123ABC, where the 0x is followed by hexidecimal digits (i.e. 0123456789abcdefABCDEF)"
537 )
539 BadNumberZero ->
540 parseReport
541 "Normal numbers cannot start with a zero. Take the zeros off the front."
542 ( reflowParagraph $
543 "Only numbers like 0x0040 or 0.25 can start with a zero."
544 )
546 BadShader msg ->
547 parseReport
548 "I ran into a problem while parsing this GLSL block."
549 (reflowParagraph msg)
551 BadUnderscore char ->
552 parseReport
553 ("An underscore cannot be immediately followed by `" <> Text.singleton char <> "`.")
554 ( reflowParagraph $
555 "Variable names cannot start with an underscore in Elm. You can\
556 \ use an underscore as a \"wildcard\" that matches anything\
557 \ though. So maybe you forgot a space after the underscore?"
558 )
560 BadOp op stack ->
561 case op of
562 HasType ->
563 badOp subRegion stack "A" "\"has type\" operator"
564 "type annotations and record types"
565 "Maybe you want :: instead? Or maybe something is indented too much?"
567 Equals ->
568 badEquals subRegion stack
570 Arrow ->
571 if isCaseRelated stack then
572 parseReport
573 "I ran into a stray arrow while parsing this `case` expression."
574 ( reflowParagraph $
575 "All branches in a `case` must be indented the exact\
576 \ same amount, so the patterns are vertically\
577 \ aligned. Maybe this branch is indented too much?"
578 )
580 else
581 badOp subRegion stack "An" "arrow"
582 "cases expressions and anonymous functions"
583 "Maybe you want > or >= instead?"
585 Pipe ->
586 badOp subRegion stack "A" "vertical bar"
587 "type declarations"
588 "Maybe you want || instead?"
590 Dot ->
591 parseReport
592 "I was not expecting this dot."
593 ( reflowParagraph $
594 "Dots are for record access and decimal points, so\
595 \ they cannot float around on their own. Maybe\
596 \ there is some extra whitespace?"
597 )
599 Theories stack allTheories ->
600 let
601 starter =
602 "Something went wrong while parsing "
603 <> contextToText "your code" "" stack
604 <> "."
605 in
606 parseReport starter $
607 case Set.toList (Set.fromList allTheories) of
608 [] ->
609 Help.stack
610 [ reflowParagraph $
611 "I do not have any suggestions though!"
612 , reflowParagraph $
613 "Can you get it down to a <http://sscce.org> and share it at\
614 \ <https://github.com/elm-lang/error-message-catalog/issues>?\
615 \ That way we can figure out how to give better advice!"
616 ]
618 [theory] ->
619 reflowParagraph $
620 "I was expecting to see "
621 <> addPeriod (theoryToText stack theory)
623 theories ->
624 Help.vcat $
625 [ text "I was expecting:"
626 , text ""
627 ]
628 ++ map (bullet . theoryToText stack) theories
631escapeProblemToReport :: EscapeProblem -> ( Text, Help.Doc )
632escapeProblemToReport escapeProblem =
633 case escapeProblem of
634 UnknownEscape ->
635 ( "A backslash starts an escaped character, but I do not recognize this one:"
636 , Help.stack
637 [ "Maybe there is some typo?"
638 , Help.toSimpleHint "Valid escape characters include:"
639 , Help.indent 4 $ Help.vcat $
640 [ "\\n"
641 , "\\r"
642 , "\\t"
643 , "\\\""
644 , "\\\'"
645 , "\\\\"
646 , "\\u{03BB}"
647 ]
648 , reflowParagraph $
649 "The last one lets encode ANY character by its Unicode code\
650 \ point, so there are no other escape characters in Elm."
651 ]
652 )
654 UnicodeSyntax ->
655 ( "I ran into an invalid Unicode escape character:"
656 , Help.stack
657 [ "Here are some examples of valid Unicode escape characters:"
658 , Help.indent 4 $ Help.vcat $
659 [ "\\u{0041}"
660 , "\\u{03BB}"
661 , "\\u{6728}"
662 , "\\u{1F60A}"
663 ]
664 , reflowParagraph $
665 "Notice that the code point is always surrounded by curly\
666 \ braces. They are required!"
667 ]
668 )
670 UnicodeRange ->
671 ( "This is not a real Unicode code point:"
672 , "All valid Unicode code points are between 0 and 10FFFF."
673 )
675 UnicodeLength numDigits badCode ->
676 let
677 moreOrFewer =
678 if numDigits < 4 then "more" else "fewer"
680 goodCode =
681 Text.justifyRight 4 '0' $ Text.dropWhile (== '0') badCode
683 suggestion =
684 [ "Try"
685 , "using"
686 , dullyellow $ text $ "\\u{" <> goodCode <> "}"
687 , "instead."
688 ]
689 in
690 ( "This Unicode code point needs " <> moreOrFewer <> " digits:"
691 , hsep suggestion
692 )
696-- HELPERS
699bullet :: Text -> Help.Doc
700bullet point =
701 Help.hang 4 (" - " <> reflowParagraph point)
704addPeriod :: Text -> Text
705addPeriod msg =
706 if Text.last msg `elem` ['`', ')', '.', '!', '?'] then
707 msg
708 else
709 msg <> "."
712badOp :: Maybe R.Region -> ContextStack -> Text -> Text -> Text -> Text -> Report.Report
713badOp subRegion stack article opName setting hint =
714 let
715 pre =
716 "I was not expecting this " <> opName
717 <> contextToText "" " while parsing " stack <> "."
718 in
719 makeParseReport subRegion pre $
720 reflowParagraph $
721 article <> " " <> opName <> " should only appear in "
722 <> setting <> ". " <> hint
725badEquals :: Maybe R.Region -> ContextStack -> Report.Report
726badEquals subRegion stack =
727 let
728 pre =
729 "I was not expecting this equals sign"
730 <> contextToText "" " while parsing " stack <> "."
731 in
732 makeParseReport subRegion pre (badEqualsHelp stack)
735badEqualsHelp :: ContextStack -> Help.Doc
736badEqualsHelp stack =
737 case stack of
738 [] ->
739 reflowParagraph $
740 "Maybe you want == instead? Or maybe something is indented too much?"
742 (ExprRecord, _) : _ ->
743 reflowParagraph $
744 "Records look like { x = 3, y = 4 } with the equals sign right\
745 \ after the field name. Maybe you forgot a comma?"
747 (Definition _, _) : rest ->
748 reflowParagraph $
749 "Maybe this is supposed to be a separate definition? If so, it\
750 \ is indented too far. "
751 <>
752 if any ((==) ExprLet . fst) rest then
753 "All definitions in a `let` expression must be vertically aligned."
754 else
755 "Spaces are not allowed before top-level definitions."
757 _ : rest ->
758 badEqualsHelp rest
761contextToText :: Text -> Text -> ContextStack -> Text
762contextToText defaultText prefixText stack =
763 case stack of
764 [] ->
765 defaultText
767 (context, _) : rest ->
768 let anchor = getAnchor rest in
769 prefixText <>
770 case context of
771 ExprIf -> "an `if`" <> anchor
772 ExprLet -> "a `let`" <> anchor
773 ExprFunc -> "an anonymous function" <> anchor
774 ExprCase -> "a `case`" <> anchor
775 ExprList -> "a list" <> anchor
776 ExprTuple -> "an expression (in parentheses)" <> anchor
777 ExprRecord -> "a record" <> anchor
778 Definition name -> name <> "'s definition"
779 Annotation name -> name <> "'s type annotation"
780 TypeTuple -> "a type (in parentheses)" <> anchor
781 TypeRecord -> "a record type" <> anchor
782 PatternList -> "a list pattern" <> anchor
783 PatternTuple -> "a pattern (in parentheses)" <> anchor
784 PatternRecord -> "a record pattern" <> anchor
785 Module -> "a module declaration"
786 Import -> "an import"
787 TypeUnion -> "a union type"
788 TypeAlias -> "a type alias"
789 Infix -> "an infix declaration"
790 Port -> "a port declaration"
793getAnchor :: ContextStack -> Text
794getAnchor stack =
795 case stack of
796 [] ->
797 ""
799 (context, _) : rest ->
800 case context of
801 Definition name ->
802 " in " <> name <> "'s definition"
804 Annotation name ->
805 " in " <> name <> "'s type annotation"
807 _ ->
808 getAnchor rest
811isCaseRelated :: ContextStack -> Bool
812isCaseRelated stack =
813 case stack of
814 [] ->
815 False
817 (context, _) : rest ->
818 context == ExprCase || isCaseRelated rest
821theoryToText :: ContextStack -> Theory -> Text
822theoryToText context theory =
823 case theory of
824 Keyword keyword ->
825 "the `" <> keyword <> "` keyword"
827 Symbol symbol ->
828 case symbol of
829 "=" -> equalsTheory context
830 "->" -> "an arrow (->) followed by an expression"
831 ":" -> "the \"has type\" symbol (:) followed by a type"
832 "," -> "a comma"
833 "|" -> barTheory context
834 "::" -> "the cons operator (::) followed by more list elements"
835 "." -> "a dot (.)"
836 "-" -> "a minus sign (-)"
837 "_" -> "an underscore"
838 "(" -> "a left paren, for grouping or starting tuples"
839 ")" -> "a closing paren"
840 "[" -> "a left square bracket, for starting lists"
841 "]" -> "a right square bracket, to end a list"
842 "{" -> "a left curly brace, for starting records"
843 "}" -> "a right curly brace, to end a record"
844 "{-|" -> "a doc comment, like {-| example -}"
845 _ -> "the (" <> symbol <> ") symbol"
847 LowVar ->
848 "a lower-case variable, like `x` or `user`"
850 CapVar ->
851 "an upper-case variable, like `Maybe` or `Just`"
853 InfixOp ->
854 "an infix operator, like (+) or (==)"
856 Digit ->
857 "a digit from 0 to 9"
859 BadSpace ->
860 badSpace context
862 Expecting next ->
863 case next of
864 Decl -> "a declaration, like `x = 5` or `type alias Model = { ... }`"
865 Expr -> "an expression, like x or 42"
866 AfterOpExpr op -> "an expression after that (" <> op <> ") operator, like x or 42"
867 ElseBranch -> "an `else` branch. An `if` must handle both possibilities."
868 Arg -> "an argument, like `name` or `total`"
869 Pattern -> "a pattern, like `name` or (Just x)"
870 Type -> "a type, like Int or (List String)"
871 Listing -> "a list of exposed values and types, like (..) or (x,y,z)"
872 Exposing -> "something like `exposing (..)`"
875equalsTheory :: ContextStack -> Text
876equalsTheory stack =
877 case stack of
878 [] ->
879 "an equals sign (=)"
881 (context, _) : rest ->
882 case context of
883 ExprRecord -> "an equals sign (=) followed by an expression"
884 Definition name -> "an equals sign (=) followed by " <> name <> "'s definition"
885 TypeUnion -> "an equals sign (=) followed by the first union type constructor"
886 TypeAlias -> "an equals sign (=) followed by the aliased type"
887 _ -> equalsTheory rest
890barTheory :: ContextStack -> Text
891barTheory stack =
892 case stack of
893 [] ->
894 "a vertical bar (|)"
896 (context, _) : rest ->
897 case context of
898 ExprRecord -> "a vertical bar (|) followed by the record fields you want to update"
899 TypeRecord -> "a vertical bar (|) followed by some record field types"
900 TypeUnion -> "a vertical bar (|) followed by more union type constructors"
901 _ -> barTheory rest
904badSpace :: ContextStack -> Text
905badSpace stack =
906 case stack of
907 [] ->
908 "more indentation? I was not done with that last thing yet."
910 (context, _) : rest ->
911 case context of
912 ExprIf -> "the end of that `if`" <> badSpaceExprEnd rest
913 ExprLet -> "the end of that `let`" <> badSpaceExprEnd rest
914 ExprFunc -> badSpace rest
915 ExprCase -> "more of that `case`" <> badSpaceExprEnd rest
916 ExprList -> "the end of that list" <> badSpaceExprEnd rest
917 ExprTuple -> "a closing paren" <> badSpaceExprEnd rest
918 ExprRecord -> "the end of that record" <> badSpaceExprEnd rest
919 Definition name -> "the rest of " <> name <> "'s definition" <> badSpaceExprEnd stack
920 Annotation name -> "the rest of " <> name <> "'s type annotation" <> badSpaceEnd
921 TypeTuple -> "a closing paren" <> badSpaceEnd
922 TypeRecord -> "the end of that record" <> badSpaceEnd
923 PatternList -> "the end of that list" <> badSpaceEnd
924 PatternTuple -> "a closing paren" <> badSpaceEnd
925 PatternRecord -> "the end of that record" <> badSpaceEnd
926 Module -> "something like `module Main exposing (..)`"
927 Import -> "something like `import Html exposing (..)`"
928 TypeUnion -> "more of that union type" <> badSpaceEnd
929 TypeAlias -> "more of that type alias" <> badSpaceEnd
930 Infix -> "more of that infix declaration" <> badSpaceEnd
931 Port -> "more of that port declaration" <> badSpaceEnd
934badSpaceEnd :: Text
935badSpaceEnd =
936 ". Maybe you forgot some code? Or you need more indentation?"
939badSpaceExprEnd :: ContextStack -> Text
940badSpaceExprEnd stack =
941 case stack of
942 [] ->
943 badSpaceEnd
945 (Definition name, R.Position _ column) : _ ->
946 let
947 ending =
948 if column <= 1 then
949 "to be indented?"
950 else
951 "more indentation? (Try " <> i2t (column + 1) <> "+ spaces.)"
952 in
953 ". Maybe you forgot some code? Or maybe the body of `" <> name
954 <> "` needs " <> ending
956 _ : rest ->
957 badSpaceExprEnd rest