1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Error.Syntax
6 , Problem(..), Theory(..)
8 , ContextStack, Context(..)
15import Data.Monoid ((<>))
16import qualified Data.Set as Set
17import qualified Data.Text as Text
19import qualified Elm.Name as N
20import qualified Reporting.Doc as D
21import qualified Reporting.Region as R
22import qualified Reporting.Render.Code as Code
23import qualified Reporting.Report as Report
31 = CommentOnNothing R.Region
32 | UnexpectedPort R.Region N.Name
33 | TypeWithBadDefinition R.Region N.Name N.Name
34 | TypeWithoutDefinition R.Region N.Name
35 | Parse R.Region (Maybe R.Region) Problem
42data ParseError = ParseError !Int !Int !Problem
50 | EndOfFile_MultiString
54 | BadEscape Int EscapeProblem
64 | BadOp BadOp ContextStack
65 | Theories ContextStack [Theory]
72 | UnicodeLength Int String
75data BadOp = HasType | Equals | Arrow | Pipe | Dot
103type ContextStack = [(Context, R.Position)]
138toReport :: Code.Source -> Error -> Report.Report
141 CommentOnNothing region ->
142 Report.Report "STRAY COMMENT" region [] $
143 Report.toCodeSnippet source region Nothing
145 "This documentation comment is not followed by anything."
148 "All documentation comments need to be right above the declaration they\
149 \ describe. Maybe some code got deleted or commented out by accident? Or\
150 \ maybe this comment is here by accident?"
153 UnexpectedPort region name ->
154 Report.Report "BAD PORT" region [] $
155 Report.toCodeSnippet source region Nothing
158 "You are declaring port `" <> N.toString name <> "` in a normal module."
161 [ "It needs to be in a `port` module."
163 "Ports are not a traditional FFI for calling JS functions directly. They need a different mindset! Read"
165 "to learn how to use ports effectively."
169 TypeWithBadDefinition region annName defName ->
170 Report.Report "ANNOTATION MISMATCH" region [] $
171 Report.toCodeSnippet source region Nothing
174 "I see a `" <> N.toString annName
175 <> "` annotation, but it is followed by a `"
176 <> N.toString defName <> "` definition."
179 ["The","annotation","and","definition","names","must","match!"
180 ,"Is","there","a","typo","between"
181 , D.dullyellow (D.fromName annName)
183 , D.dullyellow (D.fromName defName) <> "?"
187 TypeWithoutDefinition region name ->
188 Report.Report "MISSING DEFINITION" region [] $
189 Report.toCodeSnippet source region Nothing
192 "There is a type annotation for `" <> N.toString name
193 <> "` but there is no corresponding definition!"
195 "Directly below the type annotation, put a definition like:\n\n"
196 <> " " <> D.fromName name <> " = 42"
199 Parse region subRegion problem ->
200 Report.Report "PARSE ERROR" (maybe region id subRegion) [] $
201 Report.toCodeSnippet source region subRegion $
202 problemToDocs problem
206-- PARSE ERROR TO DOCS
209problemToDocs :: Problem -> (D.Doc, D.Doc)
210problemToDocs problem =
214 "I ran into a tab, but tabs are not allowed in Elm files."
216 "Replace the tab with spaces."
221 "I got to the end of the file while parsing a multi-line comment."
225 "Multi-line comments look like {- comment -}, and it looks like\
226 \ you are missing the closing marker."
228 "Nested multi-line comments like {- this {- and this -} -} are allowed.\
229 \ The opening and closing markers must be balanced though, just\
230 \ like parentheses in normal code. Maybe that is the problem?"
236 "I got to the end of the file while parsing a GLSL block."
239 "A shader should be defined in a block like this: [glsl| ... |]"
244 "I got to the end of the file while parsing a string."
247 "Strings look like \"this\" with double quotes on each end.\
248 \ Is the closing double quote missing in your code?"
251 EndOfFile_MultiString ->
253 "I got to the end of the file while parsing a multi-line string."
256 "Multi-line strings look like \"\"\"this\"\"\" with three double quotes on each\
257 \ end. Is the closing triple quote missing in your code?"
262 "I got to the end of the file while parsing a character."
265 "Characters look like 'c' with single quotes on each end.\
266 \ Is the closing single quote missing in your code?"
271 "This string is missing the closing quote."
275 "Elm strings like \"this\" cannot contain newlines."
277 "For strings that CAN contain newlines, say \"\"\"this\"\"\" for Elm’s\
278 \ multi-line string syntax. It allows unescaped newlines and double quotes."
284 "This character is missing the closing quote."
286 "Elm characters must start and end with a single quote. Valid examples\n\
287 \ include 'a', 'b', '\n', 'ø', and '\\u{00F8}' for unicode code points."
290 BadEscape _ escapeProblem ->
291 case escapeProblem of
294 "Backslashes always start escaped characters, but I do not recognize this one:"
296 [ "Maybe there is some typo?"
297 , D.toSimpleHint "Valid escape characters include:"
298 , D.indent 4 $ D.vcat $
308 "The last one lets encode ANY character by its Unicode code\
309 \ point, so use that for anything outside the ordinary six."
315 "I ran into an invalid Unicode escape character:"
318 [ "Here are some examples of valid Unicode escape characters:"
319 , D.indent 4 $ D.vcat $
326 "Notice that the code point is always surrounded by curly\
327 \ braces. They are required!"
333 "This is not a real Unicode code point:"
335 "All valid Unicode code points are between 0 and 10FFFF."
338 UnicodeLength numDigits badCode ->
339 if numDigits < 4 then
341 "Every Unicode code point needs at least four digits:"
344 goodCode = replicate (4 - numDigits) '0' ++ badCode
345 escape = "\\u{" <> D.fromString goodCode <> "}"
347 D.hsep [ "Try", D.dullyellow escape, "instead?" ]
352 "This Unicode code point has too many digits:"
355 ["Valid","code","points","are","between"
356 , D.dullyellow "\\u{0000}", "and", D.dullyellow "\\u{10FFFF}"
357 ,"so","it","must","have","between","four","and","six","digits."
363 "Ran into a bad use of single quotes."
367 "If you want to create a string, switch to double quotes:"
369 D.dullyellow "'this'" <> " => " <> D.green "\"this\""
371 "Unlike JavaScript, Elm distinguishes between strings like \"hello\"\
372 \ and individual characters like 'A' and '3'. If you really do want\
373 \ a character though, something went wrong and I did not find the\
374 \ closing single quote."
378 BadNumberDot numberBeforeDot ->
380 "Numbers cannot end with a decimal points."
384 D.fromString (show numberBeforeDot)
386 "Saying " <> D.green number <> " or " <> D.green (number <> ".0") <> " will work though!"
391 "Numbers cannot have letters or underscores in them."
394 "Maybe a space is missing between a number and a variable?"
399 "If you put the letter E in a number, it should followed by more digits."
402 "If you want to say 1000, you can also say 1e3.\
403 \ You cannot just end it with an E though!"
408 "I see the start of a hex number, but not the end."
411 "A hex number looks like 0x123ABC, where the 0x is followed by hexidecimal\
412 \ digits. Valid hexidecimal digits include: 0123456789abcdefABCDEF"
417 "Normal numbers cannot start with zeros. Take the zeros off the front."
420 "Only numbers like 0x0040 or 0.25 can start with a zero."
425 "I cannot pattern match with floating point numbers:"
428 "Equality on floats can be unreliable, so you usually want to check that they\
429 \ are nearby with some sort of (abs (actual - expected) < 0.001) check."
434 "I ran into a problem while parsing this GLSL block."
436 D.reflow (Text.unpack msg)
441 "A variable name cannot start with an underscore:"
444 "You can (1) use a wildcard like _ to ignore the value or you can (2) use\
445 \ a name that starts with a letter to access the value later. Pick one!"
451 badOp stack "A" "\"has type\" operator"
452 "type annotations and record types"
453 "Maybe you want :: instead? Or maybe something is indented too much?"
458 "I was not expecting this equals sign"
459 <> contextToString " here" " while parsing " stack <> "."
461 toBadEqualsHint stack
465 if isCaseRelated stack then
467 "I ran into a stray arrow while parsing this `case` expression."
470 "All branches in a `case` must be indented the exact\
471 \ same amount, so the patterns are vertically\
472 \ aligned. Maybe this branch is indented too much?"
476 badOp stack "An" "arrow"
477 "cases expressions and anonymous functions"
478 "Maybe you want > or >= instead?"
481 badOp stack "A" "vertical bar"
483 "Maybe you want || instead?"
487 "I was not expecting this dot."
490 "Dots are for record access and decimal points, so\
491 \ they cannot float around on their own. Maybe\
492 \ there is some extra whitespace?"
495 Theories stack allTheories ->
498 "Something went wrong while parsing " <> contextToString "your code" "" stack <> "."
500 case Set.toList (Set.fromList allTheories) of
504 "I do not have any suggestions though!"
506 "Can you get it down to a <http://sscce.org> and share it at\
507 \ <https://github.com/elm/error-message-catalog/issues>?\
508 \ That way we can figure out how to give better advice!"
513 "I was expecting to see "
514 <> addPeriod (theoryToString stack theory)
521 ++ map (bullet . theoryToString stack) theories
529badOp :: ContextStack -> String -> String -> String -> String -> ( D.Doc, D.Doc )
530badOp stack article opName setting hint =
533 "I was not expecting this " <> opName
534 <> contextToString " here" " while parsing " stack <> "."
537 article <> " " <> opName <> " should only appear in "
538 <> setting <> ". " <> hint
542toBadEqualsHint :: ContextStack -> D.Doc
543toBadEqualsHint stack =
547 "Maybe you want == instead? Or maybe something is indented too much?"
549 (ExprRecord, _) : _ ->
551 "Records look like { x = 3, y = 4 } with the equals sign right\
552 \ after the field name. Maybe you forgot a comma?"
554 (Definition _, _) : rest ->
556 "Maybe this is supposed to be a separate definition? If so, it\
557 \ is indented too far. "
559 if any ((==) ExprLet . fst) rest then
560 "All definitions in a `let` expression must be vertically aligned."
562 "Spaces are not allowed before top-level definitions."
569isCaseRelated :: ContextStack -> Bool
575 (context, _) : rest ->
576 context == ExprCase || isCaseRelated rest
583contextToString :: String -> String -> ContextStack -> String
584contextToString defaultString prefixString stack =
589 (context, _) : rest ->
590 let anchor = getAnchor rest in
593 ExprIf -> "an `if` expression" <> anchor
594 ExprLet -> "a `let` expression" <> anchor
595 ExprFunc -> "an anonymous function" <> anchor
596 ExprCase -> "a `case` expression" <> anchor
597 ExprList -> "a list" <> anchor
598 ExprTuple -> "an expression (in parentheses)" <> anchor
599 ExprRecord -> "a record" <> anchor
600 Definition name -> N.toString name <> "'s definition"
601 Annotation name -> N.toString name <> "'s type annotation"
602 TypeTuple -> "a type (in parentheses)" <> anchor
603 TypeRecord -> "a record type" <> anchor
604 PatternList -> "a list pattern" <> anchor
605 PatternTuple -> "a pattern (in parentheses)" <> anchor
606 PatternRecord -> "a record pattern" <> anchor
607 Module -> "a module declaration"
608 Import -> "an import"
609 TypeUnion -> "a union type"
610 TypeAlias -> "a type alias"
611 Infix -> "an infix declaration"
612 Port -> "a port declaration"
615getAnchor :: ContextStack -> String
621 (context, _) : rest ->
624 " in " <> N.toString name <> "'s definition"
627 " in " <> N.toString name <> "'s type annotation"
637bullet :: String -> D.Doc
639 D.hang 4 (" - " <> D.fillSep (map D.fromString (words point)))
642addPeriod :: String -> String
644 if last msg `elem` ['`', ')', '.', '!', '?'] then
650theoryToString :: ContextStack -> Theory -> String
651theoryToString context theory =
654 "the `" <> keyword <> "` keyword"
658 "=" -> equalsTheory context
659 "->" -> "an arrow (->) followed by an expression"
660 ":" -> "the \"has type\" symbol (:) followed by a type"
662 "|" -> barTheory context
663 "::" -> "the cons operator (::) followed by more list elements"
665 "-" -> "a minus sign (-)"
666 "_" -> "an underscore"
667 "(" -> "a left paren, for grouping or starting tuples"
668 ")" -> "a closing paren"
669 "[" -> "a left square bracket, for starting lists"
670 "]" -> "a right square bracket, to end a list"
671 "{" -> "a left curly brace, for starting records"
672 "}" -> "a right curly brace, to end a record"
673 "{-|" -> "a doc comment, like {-| this -}"
674 _ -> "the (" <> symbol <> ") symbol"
677 "a lower-case variable, like `x` or `user`"
680 "an upper-case variable, like `Maybe` or `Just`"
683 "an infix operator, like (+) or (==)"
686 "a digit from 0 to 9"
693 Decl -> "a declaration, like `x = 5` or `type alias Model = { ... }`"
694 Expr -> "an expression, like x or 42"
695 AfterOpExpr op -> "an expression after that (" <> N.toString op <> ") operator, like x or 42"
696 ElseBranch -> "an `else` branch. An `if` must handle both possibilities."
697 Arg -> "an argument, like `name` or `total`"
698 Pattern -> "a pattern, like `name` or (Just x)"
699 Type -> "a type, like Int or (List String)"
700 Listing -> "a list of exposed values and types, like (..) or (x,y,z)"
701 Exposing -> "something like `exposing (..)`"
704equalsTheory :: ContextStack -> String
710 (context, _) : rest ->
712 ExprRecord -> "an equals sign (=) followed by an expression"
713 Definition name -> "an equals sign (=) followed by " <> N.toString name <> "'s definition"
714 TypeUnion -> "an equals sign (=) followed by the first union type constructor"
715 TypeAlias -> "an equals sign (=) followed by the aliased type"
716 _ -> equalsTheory rest
719barTheory :: ContextStack -> String
725 (context, _) : rest ->
727 ExprRecord -> "a vertical bar (|) followed by the record fields you want to update"
728 TypeRecord -> "a vertical bar (|) followed by some record field types"
729 TypeUnion -> "a vertical bar (|) followed by more union type constructors"
733badSpace :: ContextStack -> String
737 "more indentation? I was not done with that last thing yet."
739 (context, _) : rest ->
741 ExprIf -> "the end of that `if`" <> badSpaceExprEnd rest
742 ExprLet -> "the end of that `let`" <> badSpaceExprEnd rest
743 ExprFunc -> badSpace rest
744 ExprCase -> "more of that `case`" <> badSpaceExprEnd rest
745 ExprList -> "the end of that list" <> badSpaceExprEnd rest
746 ExprTuple -> "a closing paren" <> badSpaceExprEnd rest
747 ExprRecord -> "the end of that record" <> badSpaceExprEnd rest
748 Definition name -> "the rest of " <> N.toString name <> "'s definition" <> badSpaceExprEnd stack
749 Annotation name -> "the rest of " <> N.toString name <> "'s type annotation" <> badSpaceEnd
750 TypeTuple -> "a closing paren" <> badSpaceEnd
751 TypeRecord -> "the end of that record" <> badSpaceEnd
752 PatternList -> "the end of that list" <> badSpaceEnd
753 PatternTuple -> "a closing paren" <> badSpaceEnd
754 PatternRecord -> "the end of that record" <> badSpaceEnd
755 Module -> "something like `module Main exposing (..)`"
756 Import -> "something like `import Html exposing (..)`"
757 TypeUnion -> "more of that union type" <> badSpaceEnd
758 TypeAlias -> "more of that type alias" <> badSpaceEnd
759 Infix -> "more of that infix declaration" <> badSpaceEnd
760 Port -> "more of that port declaration" <> badSpaceEnd
765 ". Maybe you forgot some code? Or you need more indentation?"
768badSpaceExprEnd :: ContextStack -> String
769badSpaceExprEnd stack =
774 (Definition name, R.Position _ column) : _ ->
780 "more indentation? (Try " <> show (column + 1) <> "+ spaces.)"
782 ". Maybe you forgot some code? Or maybe the body of `"
784 <> "` needs " <> ending
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list