{-# OPTIONS_GHC -Wall #-} {-# LANGUAGE OverloadedStrings #-} module Reporting.Error.Syntax ( Error(..) , ParseError(..) , Problem(..), Theory(..) , toReport ) where import Data.Text (Text) import qualified Reporting.Render.Type as RenderType import qualified Reporting.Region as Region import qualified Reporting.Report as Report import qualified Reporting.Helpers as Help import Reporting.Helpers ((<>), dullyellow, hsep, i2t, text) -- ALL SYNTAX ERRORS data Error = Parse Problem | BadFunctionName Int | BadPattern Text | CommentOnNothing | SettingsOnNormalModule | SettingsOnPortModule | DuplicateSettingOnEffectModule Text | BadSettingOnEffectModule Text | NoSettingsOnEffectModule | MissingManagerOnEffectModule Text | UnexpectedPort Text | TypeWithoutDefinition Text | DuplicateArgument Text Text | DuplicateFieldName Region.Region Text | DuplicateValueDeclaration Text | DuplicateTypeDeclaration Text | DuplicateDefinition Text | UnboundTypeVarsInUnion Text [Text] [Text] | UnboundTypeVarsInAlias Text [Text] [Text] | UnusedTypeVarsInAlias Text [Text] [Text] | MessyTypeVarsInAlias Text [Text] [Text] [Text] -- PARSE ERRORS data ParseError = ParseError !Int !Int !Problem data Problem = Tab | EndOfFile Text | NewLineInString | BadEscape | BadChar | BadNumberDot | BadNumberEnd | BadNumberExp | BadNumberHex | BadNumberZero | BadChunkInQualifiedCapVar | HasType -- "A single colon is for type annotations. Maybe you want :: instead? Or maybe you are defining a type annotation, but there is whitespace before it?" | Equals -- "The = operator is reserved for defining variables. Maybe you want == instead? Or maybe you are defining a variable, but there is whitespace before it?" | Arrow -- "Arrows are reserved for cases and anonymous functions. Maybe you want > or >= instead?" | Pipe -- "Vertical bars are reserved for use in union type declarations. Maybe you want || instead?" | Dot -- "Dots are for record access. They cannot float around on their own!" | Theories [Theory] data Theory = Keyword Text | Symbol Text | Variable | InfixOp | Digit | BadIndent | FreshLine | Expecting Text -- TO REPORT toReport :: RenderType.Localizer -> Error -> Report.Report toReport _localizer err = case err of Parse _ -> Report.report "PARSE ERROR" Nothing "Something is wrong." (text "TODO") BadFunctionName arity -> Report.report "BAD FUNCTION DEFINITION" Nothing "Complex patterns cannot be used as function names." ( Help.reflowParagraph $ "This function definition has " <> i2t arity <> " arguments, but instead\ \ of a normal name like `add` or `reverse` it has a pattern. There is no\ \ way to \"deconstruct\" a function with pattern matching, so this needs to\ \ be changed to a normal name." ) BadPattern name -> Report.report "BAD PATTERN" Nothing ("The free variable `" <> name <> "` appears more than once in this pattern.") ( Help.stack [ text "Rename the variables so there are no duplicates." , Help.reflowParagraph $ "In Elm, pattern matching works by binding these free variables to subsections\ \ of the matching value. It does not make sense to have the same name for two\ \ different subsections though! When you say `" <> name <> "` in some code, there\ \ is no way for me to know which subsection you mean!" ] ) CommentOnNothing -> Report.report "STRAY COMMENT" Nothing ("This documentation comment is not followed by anything.") ( Help.reflowParagraph $ "All documentation comments need to be right above the declaration they\ \ describe. Maybe some code got deleted or commented out by accident? Or\ \ maybe this comment is here by accident?" ) SettingsOnNormalModule -> Report.report "BAD MODULE DECLARATION" Nothing "A normal module can expose values, but not settings like this." ( Help.reflowParagraph $ "If you want a normal module, just remove this stuff. If you want to create\ \ an `effect module` you just forgot to use the `effect` keyword. In that\ \ case, just change `module` to `effect module` and you should be headed in\ \ the right direction!" ) SettingsOnPortModule -> Report.report "BAD MODULE DECLARATION" Nothing "A port module can expose values, but not have settings like this." ( Help.reflowParagraph $ "If you want a port module, just remove this stuff. If you want to create\ \ a custom effect module instead (which is rare) just change `port module`\ \ to `effect module` and you should be headed in the right direction!" ) DuplicateSettingOnEffectModule name -> Report.report "EFFECT MODULE PROBLEM" Nothing ("You have defined " <> Help.functionName name <> " multiple times.") ( Help.reflowParagraph $ "There can only be one " <> Help.functionName name <> " though! Remove until there is one left." ) BadSettingOnEffectModule name -> Report.report "EFFECT MODULE PROBLEM" Nothing ("Setting " <> Help.functionName name <> " is not recognized.") ( Help.reflowParagraph $ "Remove this entry and you should be all set!" ) NoSettingsOnEffectModule -> Report.report "EFFECT MODULE PROBLEM" Nothing ("You are defining an effect module, but it has no settings.") ( Help.reflowParagraph $ "If you just wanted a normal module, change the keywords `effect module`\ \ to `module` and you should be all set. If you want a proper effect module,\ \ you need to specify your commands and/or subscriptions. Read more about this\ \ here: " ) MissingManagerOnEffectModule name -> Report.report "EFFECT MODULE PROBLEM" Nothing ("You are defining an effect module, but there is no " <> Help.functionName name <> " defined.") ( Help.reflowParagraph $ "There is a small set of top-level functions and values that must be defined\ \ in any complete effect module. The best thing is probably to just read more\ \ about effect modules here:\ \ " ) UnexpectedPort name -> Report.report "BAD PORT" Nothing ("You are declaring port " <> Help.functionName name <> " in a normal module.") ( Help.reflowParagraph $ "All ports must be defined in a `port module`. You should probably have just\ \ one of these for your project. This way all of your foreign interactions\ \ stay relatively organized." ) TypeWithoutDefinition valueName -> Report.report "MISSING DEFINITION" Nothing ("There is a type annotation for `" <> valueName <> "` but there" <> " is no corresponding definition!" ) ( text $ "Directly below the type annotation, put a definition like:\n\n" <> " " <> valueName <> " = 42" ) DuplicateArgument funcName argName -> Report.report "DUPLICATE ARGUMENT" Nothing ( "The name `" <> argName <> "` is used more than once in the arguments of " <> Help.functionName funcName <> "." ) ( Help.reflowParagraph $ "Rename things until `" <> argName <> "` is used only once.\ \ Otherwise how can we tell which one you want when you\ \ say `" <> argName <> "` it in the body of your function?" ) DuplicateFieldName region name -> Report.report "DUPLICATE FIELD" (Just region) ("This record has more than one field named `" <> name <> "`.") (text "There can only be one. Do some renaming to make sure the names are distinct!") DuplicateValueDeclaration name -> Report.report "DUPLICATE DEFINITION" Nothing ( "Naming multiple top-level values " <> Help.functionName name <> " makes things\n" <> "ambiguous. When you say " <> Help.functionName name <> " which one do you want?" ) ( Help.reflowParagraph $ "Find all the top-level values named " <> Help.functionName name <> " and do some renaming. Make sure the names are distinct!" ) DuplicateTypeDeclaration name -> Report.report "DUPLICATE DEFINITION" Nothing ( "There are multiple types named `" <> name <> "` in this module." ) ( Help.reflowParagraph $ "Search through this module, find all the types named `" <> name <> "`, and give each of them a unique name." ) DuplicateDefinition name -> Report.report "DUPLICATE DEFINITION" Nothing ("There are multiple values named `" <> name <> "` in this let-expression." ) ( Help.reflowParagraph $ "Search through this let-expression, find all the values named `" <> name <> "`, and give each of them a unique name." ) UnboundTypeVarsInUnion typeName givenVars unbound -> unboundTypeVars "type" typeName givenVars unbound UnboundTypeVarsInAlias typeName givenVars unbound -> unboundTypeVars "type alias" typeName givenVars unbound UnusedTypeVarsInAlias typeName givenVars unused -> Report.report "UNUSED TYPE VARIABLES" Nothing ( "Type alias `" <> typeName <> "` cannot have unused type variables: " <> Help.commaSep unused ) ( Help.stack [ text "You probably need to change the declaration like this:" , dullyellow $ hsep $ map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ ["=", "..."]) ] ) MessyTypeVarsInAlias typeName givenVars unused unbound -> Report.report "TYPE VARIABLE PROBLEMS" Nothing ( "Type alias `" <> typeName <> "` has some problems with type variables." ) ( Help.stack [ Help.reflowParagraph $ "The declaration says it uses certain type variables (" <> Help.commaSep unused <> ") but they do not appear in the aliased type. " <> "Furthermore, the aliased type says it uses type variables (" <> Help.commaSep unbound <> ") that do not appear in the declaration." , text "You probably need to change the declaration like this:" , dullyellow $ hsep $ map text ("type" : "alias" : typeName : filter (`notElem` unused) givenVars ++ unbound ++ ["=", "..."]) ] ) unboundTypeVars :: Text -> Text -> [Text] -> [Text] -> Report.Report unboundTypeVars declKind typeName givenVars unboundVars = Report.report "UNBOUND TYPE VARIABLES" Nothing ( Help.capitalize declKind <> " `" <> typeName <> "` must declare its use of type variable" <> Help.commaSep unboundVars ) ( Help.stack [ text "You probably need to change the declaration like this:" , hsep $ map text (declKind : typeName : givenVars) ++ map (dullyellow . text) unboundVars ++ map text ["=", "..."] , Help.reflowParagraph $ "Here's why. Imagine one `" <> typeName <> "` where `" <> head unboundVars <> "` is an Int and another where it is a Bool. When we explicitly list the type\ \ variables, the type checker can see that they are actually different types." ] )