on master help sign in
Create Reporting.Report and move towards nicer messages
commit123f8674a7b14675c656f59306ee45af6369df51 authorEvan Czaplicki <info@elm-lang.org> dateWed 6 May 2015, 12:42 (11 years ago) parent9dde1e3
7 files changed+283−301
src/Elm/Compiler.hs+2−19 src/Reporting/Error.hs+6−43 src/Reporting/Error/Canonicalize.hs+78−80 src/Reporting/Error/Syntax.hs+61−56 src/Reporting/Error/Type.hs+26−41 src/Reporting/Region.hs−62 src/Reporting/Report.hs+110
src/Elm/Compiler.hs+2−19
@@ -3,8 +3,8 @@
33module Elm.Compiler
44 ( version, rawVersion
55 , parseDependencies, compile
6− , Error, errorToString, errorToJson
7− , Warning, warningToString, warningToJson
6+ , Error, errorToString
7+ , Warning
88 ) where
99
1010import Control.Monad.Error.Class (MonadError, throwError)
@@ -88,23 +88,6 @@ errorToString location source (Error err) =
8888 Error.toString location source err
8989
9090
91−errorToJson :: Error -> String
92−errorToJson (Error err) =
93− Error.toJson err
94−
95−
9691-- WARNINGS
9792
9893newtype Warning = Warning (A.Located Warning.Warning)
99−
100−
101−warningToString :: Warning -> String
102−warningToString (Warning warning) =
103− Warning.toString warning
104−
105−
106−warningToJson :: Warning -> String
107−warningToJson (Warning warning) =
108− Warning.toJson warning
109−
110−
src/Reporting/Error.hs+6−43
@@ -1,15 +1,11 @@
11{-# LANGUAGE OverloadedStrings #-}
22module Reporting.Error where
33
4−import Data.Aeson ((.=))
5−import qualified Data.Aeson as Json
6−import qualified Data.ByteString.Lazy.Char8 as BS
7−
84import qualified Reporting.Annotation as A
95import qualified Reporting.Error.Canonicalize as Canonicalize
106import qualified Reporting.Error.Syntax as Syntax
117import qualified Reporting.Error.Type as Type
12−import qualified Reporting.Region as R
8+import qualified Reporting.Report as Report
139
1410
1511-- ALL POSSIBLE ERRORS
@@ -25,54 +21,21 @@ data Error
2521toString :: String -> String -> A.Located Error -> String
2622toString location source (A.A region err) =
2723 let
28− (tag, hint) =
24+ (tag, report) =
2925 case err of
3026 Syntax syntaxError ->
3127 ( "SYNTAX ERROR"
32− , Syntax.toHint syntaxError
28+ , Syntax.toReport syntaxError
3329 )
3430
3531 Canonicalize canonicalizeError ->
3632 ( "NAMING ERROR"
37− , Canonicalize.toHint canonicalizeError
33+ , Canonicalize.toReport canonicalizeError
3834 )
3935
4036 Type typeError ->
4137 ( "TYPE ERROR"
42− , Type.toHint typeError
38+ , Type.toReport typeError
4339 )
44−
45− usedSpace =
46− 4 + length tag + 1 + length location
47−
48− messageBar =
49− "-- " ++ tag ++ " "
50− ++ replicate (max 1 (80 - usedSpace)) '-'
51− ++ " " ++ location
52− in
53− messageBar ++ "\n\n"
54− ++ R.select region source ++ "\n"
55− ++ hint ++ "\n\n\n"
56−
57−
58−-- JSON
59−
60−toJson :: A.Located Error -> String
61−toJson (A.A region err) =
62− let json =
63− case err of
64− Syntax syntaxError ->
65− Syntax.toJson syntaxError
66−
67− Canonicalize canonicalizeError ->
68− Canonicalize.toJson canonicalizeError
69−
70− Type typeError ->
71− Type.toJson typeError
7240 in
73− BS.unpack $ Json.encode $
74− Json.object
75− [ "tag" .= ("error" :: String)
76− , "region" .= region
77− , "error" .= json
78− ]
41+ Report.toString tag location region report source
src/Reporting/Error/Canonicalize.hs+78−80
@@ -2,9 +2,6 @@
22{-# LANGUAGE OverloadedStrings #-}
33module Reporting.Error.Canonicalize where
44
5−--import Data.Aeson ((.=))
6−import qualified Data.Aeson as Json
7−--import qualified Data.Aeson.Types as Json
85import Data.Function (on)
96import qualified Data.List as List
107import qualified Text.EditDistance as Dist
@@ -15,7 +12,8 @@ import qualified AST.Type as Type
1512import qualified AST.Variable as Var
1613import Elm.Utils ((|>))
1714import qualified Reporting.PrettyPrint as P
18−import qualified Reporting.Region as R
15+import qualified Reporting.Region as Region
16+import qualified Reporting.Report as Report
1917
2018
2119data Error
@@ -82,7 +80,7 @@ valueNotFound name value possibilities =
8280data AliasError
8381 = ArgMismatch Var.Canonical Int Int
8482 | SelfRecursive String [String] Type.Raw
85− | MutuallyRecursive [(R.Region, String, [String], Type.Raw)]
83+ | MutuallyRecursive [(Region.Region, String, [String], Type.Raw)]
8684
8785
8886alias :: Var.Canonical -> Int -> Int -> Error
@@ -131,126 +129,126 @@ distance x y =
131129 Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
132130
133131
134−-- TO STRING
132+-- TO REPORT
135133
136−toString :: R.Region -> Error -> String
137−toString region err =
138− case err of
139− _ -> error "Canonicalize.toString" region
140−
141−
142−-- TO JSON
143−
144−toJson :: Error -> Json.Value
145−toJson err =
146− case err of
147− _ -> error "Canonicalize.toJson"
148−
149−
150−-- HINTS
151−
152−toHint :: Error -> String
153−toHint err =
134+toReport :: Error -> Report.Report
135+toReport err =
154136 case err of
155137 Var (VarError kind name problem suggestions) ->
156− let var = kind ++ "'" ++ name ++ "'"
138+ let var = kind ++ " '" ++ name ++ "'"
157139 in
158140 case problem of
159141 Ambiguous ->
160− "This usage of " ++ var ++ " is ambiguous"
161− ++ maybeYouWant "\n\n" suggestions
142+ Report.simple
143+ ("This usage of " ++ var ++ " is ambiguous.")
144+ (maybeYouWant suggestions)
162145
163146 UnknownQualifier qualifier ->
164− "Cannot find " ++ var ++ "\n\n"
165− ++ "The qualifier " ++ qualifier ++ " is not in scope."
166− ++ maybeYouWant "\n" suggestions
147+ Report.simple
148+ ("Cannot find " ++ var ++ ", qualifier '" ++ qualifier ++ "' is not in scope.")
149+ (maybeYouWant suggestions)
167150
168151 QualifiedUnknown qualifier ->
169− "Cannot find " ++ var ++ "\n\n"
170− ++ "The module called " ++ qualifier ++ " does not expose that value."
171− ++ maybeYouWant "\n" suggestions
152+ Report.simple
153+ ("Cannot find " ++ var ++ ", module '" ++ qualifier ++ "'' does not expose that value.")
154+ (maybeYouWant suggestions)
172155
173156 ExposedUnknown ->
174− "Cannot find " ++ var
175− ++ maybeYouWant "\n\n" suggestions
157+ Report.simple
158+ ("Cannot find " ++ var)
159+ (maybeYouWant suggestions)
176160
177161 Pattern patternError ->
178162 case patternError of
179163 PatternArgMismatch var expected actual ->
180− argMismatchHint "pattern" var expected actual
164+ argMismatchReport "Pattern" var expected actual
181165
182166 Alias aliasError ->
183167 case aliasError of
184168 ArgMismatch var expected actual ->
185− argMismatchHint "type" var expected actual
169+ argMismatchReport "Type" var expected actual
186170
187171 SelfRecursive name tvars tipe ->
188− "This type alias is recursive, forming an infinite type!\n\n"
189− ++ "Type aliases are just names for more fundamental types, so I go through\n"
190− ++ "and expand them all to know the real underlying type. The problem is that\n"
191− ++ "when I expand a recursive type alias, it keeps getting bigger and bigger.\n"
192− ++ "Dealiasing results in an infinitely large type! Try this instead:\n\n"
193− ++ " type " ++ name ++ concatMap (' ':) tvars ++ " ="
194− ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty True tipe)))
195− ++ "\n\nThis creates a brand new type that cannot be expanded. It is similar types\n"
196− ++ "like List which are recursive, but do not get expanded into infinite types."
172+ Report.simple "This type alias is recursive, forming an infinite type!" $
173+ "Type aliases are just names for more fundamental types, so I go through\n"
174+ ++ "and expand them all to know the real underlying type. The problem is that\n"
175+ ++ "when I expand a recursive type alias, it keeps getting bigger and bigger.\n"
176+ ++ "Dealiasing results in an infinitely large type! Try this instead:\n\n"
177+ ++ " type " ++ name ++ concatMap (' ':) tvars ++ " ="
178+ ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty True tipe)))
179+ ++ "\n\nThis creates a brand new type that cannot be expanded. It is similar types\n"
180+ ++ "like List which are recursive, but do not get expanded into infinite types."
197181
198182 MutuallyRecursive aliases ->
199− "This type alias is part of a mutually recursive set of type aliases.\n"
200− ++ "The following type aliases all depend on each other in some way:\n"
201− ++ concatMap showName aliases ++ "\n\n"
202− ++ "The problem is that when you try to expand any of these type aliases, they\n"
203− ++ "expand infinitely! To fix, first try to centralize them into one alias."
183+ Report.simple "This type alias is part of a mutually recursive set of type aliases." $
184+ "The following type aliases all depend on each other in some way:\n"
185+ ++ concatMap showName aliases ++ "\n\n"
186+ ++ "The problem is that when you try to expand any of these type aliases, they\n"
187+ ++ "expand infinitely! To fix, first try to centralize them into one alias."
204188 where
205− showName (R.Region start _, name, _, _) =
189+ showName (Region.Region start _, name, _, _) =
206190 "\n " ++ name ++ replicate (65 - length name) ' '
207− ++ "line " ++ show (R.line start)
191+ ++ "line " ++ show (Region.line start)
208192
209193 Import name importError ->
194+ let moduleName = Module.nameToString name
195+ in
210196 case importError of
211197 ModuleNotFound suggestions ->
212− "Could not find a module named '" ++ Module.nameToString name ++ "'"
213− ++ maybeYouWant "\n\n" (map Module.nameToString suggestions)
198+ Report.simple
199+ ("Could not find a module named '" ++ moduleName ++ "'")
200+ (maybeYouWant (map Module.nameToString suggestions))
214201
215202 ValueNotFound value suggestions ->
216− "Module '" ++ Module.nameToString name ++ "' does not expose '" ++ value ++ "'"
217− ++ maybeYouWant "\n\n" suggestions
203+ Report.simple
204+ ("Module '" ++ moduleName ++ "' does not expose '" ++ value ++ "'")
205+ (maybeYouWant suggestions)
218206
219207 Export name suggestions ->
220− "Could not export '" ++ name ++ "'\n\n"
221− ++ "It is not defined in this module."
222− ++ maybeYouWant " " suggestions
208+ Report.simple
209+ ("Could not export '" ++ name ++ "' which is not defined in this module.")
210+ (maybeYouWant suggestions)
223211
224212 Port (PortError name isInbound _rootType localType maybeMessage) ->
225− "Cannot pass " ++ message ++ " through " ++ boundPort ++ " '" ++ name ++ "':\n\n"
226− ++ P.render (P.nest 4 (P.pretty False localType)) ++ "\n\n"
227− ++ "The kinds of values that can flow through " ++ boundPort ++ "s include:\n"
228− ++ "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples,\n"
229− ++ "Json.Values, and concrete records."
230− where
231− boundPort =
232− if isInbound then "inbound port" else "outbound port"
213+ let
214+ boundPort =
215+ if isInbound then "inbound port" else "outbound port"
216+
217+ preHint =
218+ "Trying to send " ++ maybe "an unsupported type" id maybeMessage
219+ ++ " through " ++ boundPort ++ " '" ++ name ++ "'"
220+
221+ postHint =
222+ "The specific unsupported type is:\n\n"
223+ ++ P.render (P.nest 4 (P.pretty False localType)) ++ "\n\n"
224+ ++ "The types of values that can flow through " ++ boundPort ++ "s include:\n"
225+ ++ "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples,\n"
226+ ++ "Json.Values, and concrete records."
227+ in
228+ Report.simple preHint postHint
233229
234− message =
235− maybe "the following type" id maybeMessage
236230
231+argMismatchReport :: String -> Var.Canonical -> Int -> Int -> Report.Report
232+argMismatchReport kind var expected actual =
233+ let
234+ preHint =
235+ kind ++ " " ++ Var.toString var ++ " has too "
236+ ++ (if actual < expected then "few" else "many")
237+ ++ "arguments."
237238
238−argMismatchHint :: String -> Var.Canonical -> Int -> Int -> String
239−argMismatchHint kind var expected actual =
240− "The " ++ kind ++ " " ++ Var.toString var ++ " has too "
241− ++ (if actual < expected then "few" else "many")
242− ++ "arguments.\n\n"
243− ++ "Expecting " ++ show expected ++ ", but got " ++ show actual
239+ postHint =
240+ "Expecting " ++ show expected ++ ", but got " ++ show actual ++ "."
241+ in
242+ Report.Report Nothing preHint postHint
244243
245244
246−maybeYouWant :: String -> [String] -> String
247−maybeYouWant prefix suggestions =
245+maybeYouWant :: [String] -> String
246+maybeYouWant suggestions =
248247 case suggestions of
249248 [] ->
250249 ""
251250
252251 _:_ ->
253− prefix
254− ++ "Maybe you want one of the following?\n"
252+ "Maybe you want one of the following?\n"
255253 ++ concatMap ("\n "++) suggestions
256254
src/Reporting/Error/Syntax.hs+61−56
@@ -1,7 +1,6 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Syntax where
33
4−import qualified Data.Aeson as Json
54import qualified Data.List as List
65import qualified Text.Parsec.Error as Parsec
76import qualified Text.PrettyPrint as P
@@ -11,6 +10,7 @@ import qualified AST.Helpers as Help
1110import qualified AST.Type as Type
1211import Elm.Utils ((|>))
1312import qualified Reporting.PrettyPrint as P
13+import qualified Reporting.Report as Report
1414
1515
1616data Error
@@ -25,26 +25,22 @@ data Error
2525 | UnboundTypeVarsInUnion String [String] String [String] [(String, [Type.Raw])]
2626
2727
28−-- TO JSON
28+-- TO REPORT
2929
30−toJson :: Error -> Json.Value
31−toJson err =
32− case err of
33− _ -> error "Syntax.toJson"
34−
35−
36−-- HINTS
37−
38−toHint :: Error -> String
39−toHint err =
30+toReport :: Error -> Report.Report
31+toReport err =
4032 case err of
4133 Parse messages ->
42− unlines (map parseErrorHint messages)
34+ Report.simple
35+ "Problem when parsing your code!"
36+ (unlines (map parseErrorHint messages))
4337
4438 InfixDuplicate opName ->
45− "The infix declarations for " ++ operator ++ " must be removed.\n\n"
46− ++ "The precedence and associativity can only be set in one place, and\n"
47− ++ "this information has already been set somewhere else."
39+ Report.simple
40+ ("The infix declarations for " ++ operator ++ " must be removed.")
41+ ("The precedence and associativity can only be set in one place, and\n"
42+ ++ "this information has already been set somewhere else."
43+ )
4844 where
4945 operator =
5046 if Help.isOp opName
@@ -52,37 +48,47 @@ toHint err =
5248 else "`" ++ opName ++ "`"
5349
5450 TypeWithoutDefinition valueName ->
55− "There is a type annotation for '" ++ valueName ++ "' but there"
56− ++ "is no corresponding definition! I need something like this:\n\n"
57− ++ " " ++ valueName ++ " = 42\n\n"
58− ++ "And be sure to put the definition directly below the type annotation."
51+ Report.simple
52+ ("There is a type annotation for '" ++ valueName ++ "' but there"
53+ ++ "is no corresponding definition!"
54+ )
55+ ("Directly below the type annotation, put a definition like:\n\n"
56+ ++ " " ++ valueName ++ " = 42"
57+ )
5958
6059 PortWithoutAnnotation portName ->
61− "There is a port named '" ++ portName ++ "' but it does not have"
62− ++ "a type annotation! I need something like this:\n\n"
63− ++ indent ("port " ++ portName ++ " : Signal Int") ++ "\n\n"
64− ++ "And be sure to put this annotation directly above the port definition."
60+ Report.simple
61+ ("Port '" ++ portName ++ "' does not have a type annotation!")
62+ ("Directly above the port definition, I need something like this:\n\n"
63+ ++ " port " ++ portName ++ " : Signal Int"
64+ )
6565
6666 DuplicateValueDeclaration name ->
67− "Multiple top-level declarations define '" ++ name ++ "',\n"
68− ++ "but the name overlap makes all uses of '" ++ name ++ "' ambiguous.\n"
69− ++ "How can we know which you want?!\n\n"
70− ++ "Find all the top-level variables named '" ++ name ++ "' and\n"
71− ++ "do some renaming. Make sure the names are distinct!"
72−
73− DuplicateTypeDeclaration tipeName ->
74− "Multiple declarations define a type '" ++ tipeName ++ "',\n"
75− ++ "but the name overlap makes all uses of '" ++ tipeName ++ "' ambiguous.\n"
76− ++ "How can we know which you want?!\n\n"
77− ++ "Find all the types named '" ++ tipeName ++ "' and\n"
78− ++ "do some renaming. Make sure the names are distinct!"
67+ Report.simple
68+ ("Naming multiple top-level values '" ++ name ++ "' makes things\n"
69+ ++ "ambiguous. When you say '" ++ name ++ "' which one do you want?!"
70+ )
71+ ("Find all the top-level values named '" ++ name ++ "' and\n"
72+ ++ "do some renaming. Make sure the names are distinct!"
73+ )
74+
75+ DuplicateTypeDeclaration name ->
76+ Report.simple
77+ ("Naming multiple types '" ++ name ++ "' makes things ambiguous\n"
78+ ++ "When you say '" ++ name ++ "' which one do you want?!"
79+ )
80+ ("Find all the types named '" ++ name ++ "' and\n"
81+ ++ "do some renaming. Make sure the names are distinct!"
82+ )
7983
8084 DuplicateDefinition name ->
81− "This let-expression defines multiple variables named '" ++ name ++ "',\n"
82− ++ "but the name overlap makes all uses of '" ++ name ++ "' ambiguous.\n"
83− ++ "How can we know which you want?!\n\n"
84− ++ "Find the variables named '" ++ name ++ "' in this let-expression and\n"
85− ++ "do some renaming. Make sure the names are distinct!"
85+ Report.simple
86+ ("Naming multiple values '" ++ name ++ "' in a single let-expression makes\n"
87+ ++ "things ambiguous. When you say '" ++ name ++ "' which one do you want?!"
88+ )
89+ ("Find all the values named '" ++ name ++ "' in this let-expression and\n"
90+ ++ "do some renaming. Make sure the names are distinct!"
91+ )
8692
8793 UnboundTypeVarsInAlias typeName givenVars tvar tvars tipe ->
8894 unboundTypeVars typeName tvar tvars $ P.render $
@@ -109,23 +115,22 @@ toHint err =
109115 P.text ctor <+> P.hsep (map (P.pretty True) args)
110116
111117
112−unboundTypeVars :: String -> String -> [String] -> String -> String
118+unboundTypeVars :: String -> String -> [String] -> String -> Report.Report
113119unboundTypeVars typeName tvar tvars revisedDeclaration =
114− "In the definition of type '" ++ typeName ++ "' you use the following\n"
115− ++ "unbound type variable" ++ s ++ ": " ++ List.intercalate ", " (tvar:tvars) ++ "\n\n"
116− ++ "All type variables must be listed to avoid sneaky type errors.\n"
117− ++ "Imagine one '" ++ typeName ++ "' where '" ++ tvar ++ "' is an Int and\n"
118− ++ "another where it is a Bool. They both look like a '" ++ typeName ++ "'\n"
119− ++ "to the type checker, but they are actually different types!\n\n"
120− ++ "Maybe you want a definition like this?\n\n"
121− ++ indent revisedDeclaration ++ "\n\n"
122− where
120+ let
123121 s = if null tvars then "" else "s"
124−
125−
126−indent :: String -> String
127−indent string =
128− List.intercalate "\n" (map (" " ++ ) (lines string))
122+ in
123+ Report.simple
124+ ("Type '" ++ typeName ++ "' uses unbound type variable" ++ s ++ ": "
125+ ++ List.intercalate ", " (tvar:tvars)
126+ )
127+ ("All type variables must be listed to avoid sneaky type errors.\n"
128+ ++ "Imagine one '" ++ typeName ++ "' where '" ++ tvar ++ "' is an Int and\n"
129+ ++ "another where it is a Bool. They both look like a '" ++ typeName ++ "'\n"
130+ ++ "to the type checker, but they are actually different types!\n\n"
131+ ++ "Maybe you want a definition like this?\n"
132+ ++ concatMap ("\n "++) (lines revisedDeclaration)
133+ )
129134
130135
131136parseErrorHint :: Parsec.Message -> String
src/Reporting/Error/Type.hs+26−41
@@ -1,13 +1,11 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Type where
33
4−--import Data.Aeson ((.=))
5−import qualified Data.Aeson as Json
64import qualified Text.PrettyPrint as P
75
86import qualified AST.Type as T
97import qualified Reporting.PrettyPrint as P
10−import qualified Reporting.Region as R
8+import qualified Reporting.Report as Report
119
1210
1311data Error
@@ -23,48 +21,35 @@ data Mismatch = MismatchInfo
2321 }
2422
2523
26−-- TO STRING
24+-- TO REPORT
2725
28−toString :: R.Region -> Error -> String
29−toString region err =
30− case err of
31− _ -> error "Type.toString" region
32−
33−
34−-- TO JSON
35−
36−toJson :: Error -> Json.Value
37−toJson err =
38− case err of
39− _ -> error "Type.toJson"
40−
41−
42−-- HINTS
43−
44−toHint :: Error -> String
45−toHint err =
26+toReport :: Error -> Report.Report
27+toReport err =
4628 case err of
4729 Mismatch (MismatchInfo maybeHint leftType rightType) ->
48− maybe "This expression is triggering a type mismatch." id maybeHint
49− ++ "\n\n"
50− ++ "I am trying to infer the type, but I am getting conflicting information.\n"
51− ++ "Some evidence suggests it has the type:\n\n"
52− ++ P.render (P.nest 4 (P.pretty False leftType))
53− ++ "\n\nBut other evidence suggests:\n\n"
54− ++ P.render (P.nest 4 (P.pretty False rightType))
30+ let
31+ preHint =
32+ maybe "This expression is triggering a type mismatch." id maybeHint
33+
34+ postHint =
35+ "During type inference, I am seeing a conflict between this type:\n\n"
36+ ++ P.render (P.nest 4 (P.pretty False leftType))
37+ ++ "\n\nand this type:\n\n"
38+ ++ P.render (P.nest 4 (P.pretty False rightType))
39+ in
40+ Report.simple preHint postHint
5541
5642 InfiniteType var tipe ->
57− "This expression is leading me to infer an infinite type. Maybe you are trying\n"
58− ++ "to do some tricky recursion? Try breaking the expression into smaller pieces.\n"
59− ++ "Give each piece a name and try to write down its type.\n\n"
60− ++ "The evidence is suggesting that type '" ++ var ++ "' is equal to:\n\n"
61− ++ P.render (P.nest 4 (P.pretty False tipe))
62− ++ "\n\nNotice that type variable '" ++ var ++ "' appears there too, so if we\n"
63− ++ "expanded this type, it would just keep getting bigger and bigger."
43+ Report.simple "This expression is leading me to infer an infinite type." $
44+ "Maybe you are trying to do some tricky recursion? Try breaking the expression\n"
45+ ++ "into smaller pieces. Give each piece a name and try to write down its type.\n\n"
46+ ++ "Type inference got stuck when type '" ++ var ++ "' needed equal to:\n\n"
47+ ++ P.render (P.nest 4 (P.pretty False tipe))
48+ ++ "\n\nNotice that type variable '" ++ var ++ "' appears there too, so if we\n"
49+ ++ "expanded this type, it would just keep getting bigger and bigger."
6450
6551 BadMain tipe ->
66− "The 'main' value has an unsupported type:\n\n"
67− ++ P.render (P.nest 4 (P.pretty False tipe))
68− ++ "\n\n"
69− ++ "Please give me an Element, Html, (Signal Element), or (Signal Html) so\n"
70− ++ "that I can render it on screen!"
52+ Report.simple "The 'main' value has an unsupported type." $
53+ "I need an Element, Html, (Signal Element), or (Signal Html) so I can render it\n"
54+ ++ "on screen, but you gave me:\n\n"
55+ ++ P.render (P.nest 4 (P.pretty False tipe))
src/Reporting/Region.hs−62
@@ -5,8 +5,6 @@ import Data.Aeson ((.=))
55import qualified Data.Aeson as Json
66import qualified Text.Parsec.Pos as Parsec
77
8−import Elm.Utils ((|>))
9−
108
119data Region = Region
1210 { start :: Position
@@ -48,66 +46,6 @@ toString (Region start end) =
4846 ++ show (column start) ++ " to " ++ show (column end)
4947
5048
51−-- EXTRACT REGION from SOURCE with LINE NUMBERS
52−
53−select :: Region -> String -> String
54−select (Region start end) source =
55− let
56− relevantLines =
57− lines source
58− |> drop (line start - 1)
59− |> take (line end - line start + 1)
60− in
61− case relevantLines of
62− [] ->
63− error "something has gone badly wrong with reporting source locations"
64−
65− [theLine] ->
66− let
67− n = line start
68− w = length (show (n + 1))
69− underline =
70− replicate (column start - 1) ' '
71− ++ replicate (max 1 (column end - column start)) '^'
72− in
73− unlines
74− [ addLineNumber w (n-1) ""
75− , addLineNumber w n theLine
76− , addLineNumber w (n+1) underline
77− ]
78−
79− firstLine : rest ->
80− let
81− filteredFirstLine =
82− replicate (column start - 1) ' '
83− ++ drop (column start - 1) firstLine
84−
85− filteredLastLine =
86− take (column end) (last rest)
87−
88− focusedRelevantLines =
89− filteredFirstLine : init rest ++ [filteredLastLine]
90−
91− lineNumbersWidth =
92− length (show (line end + 1))
93−
94− numberedLines =
95− zipWith
96− (addLineNumber lineNumbersWidth)
97− [line start - 1 .. line end + 1]
98− ("" : focusedRelevantLines ++ [""])
99− in
100− unlines numberedLines ++ "\n"
101−
102−
103−addLineNumber :: Int -> Int -> String -> String
104−addLineNumber width n line =
105− let
106− number = if n < 0 then " " else show n
107− in
108− replicate (width - length number) ' ' ++ number ++ "| " ++ line
109−
110−
11149-- JSON
11250
11351instance Json.ToJSON Region where
src/Reporting/Report.hsnew file+110
@@ -0,0 +1,110 @@
1+module Reporting.Report
2+ ( Report(Report)
3+ , simple
4+ , toString
5+ ) where
6+
7+import Elm.Utils ((|>))
8+import qualified Reporting.Region as R
9+
10+
11+data Report = Report
12+ { highlight :: Maybe (Int,Int)
13+ , preHint :: String
14+ , postHint :: String
15+ }
16+
17+
18+simple :: String -> String -> Report
19+simple pre post =
20+ Report Nothing pre post
21+
22+
23+toString :: String -> String -> R.Region -> Report -> String -> String
24+toString tag location region report source =
25+ concat
26+ [ messageBar tag location
27+ , preHint report ++ "\n\n"
28+ , grabRegion (highlight report) region source ++ "\n"
29+ , postHint report ++ "\n\n\n"
30+ ]
31+
32+
33+-- REPORT HEADER
34+
35+messageBar :: String -> String -> String
36+messageBar tag location =
37+ let usedSpace = 4 + length tag + 1 + length location
38+ in
39+ "-- " ++ tag ++ " "
40+ ++ replicate (max 1 (80 - usedSpace)) '-'
41+ ++ " " ++ location ++ "\n\n"
42+
43+
44+-- REGIONS
45+
46+grabRegion :: Maybe (Int,Int) -> R.Region -> String -> String
47+grabRegion maybeSubRegion (R.Region start end) source =
48+ let
49+ (R.Position startLine startColumn) = start
50+ (R.Position endLine endColumn) = end
51+
52+ relevantLines =
53+ lines source
54+ |> drop (startLine - 1)
55+ |> take (endLine - startLine + 1)
56+ in
57+ case relevantLines of
58+ [] ->
59+ error "something has gone badly wrong with reporting source locations"
60+
61+ [theLine] ->
62+ let
63+ n = startLine
64+ w = length (show (n + 1))
65+ underline =
66+ replicate (startColumn - 1) ' '
67+ ++ replicate (max 1 (endColumn - startColumn)) '^'
68+ number =
69+ addLineNumber Nothing w
70+ in
71+ unlines
72+ [ number (n-1) ""
73+ , number n theLine
74+ , number (n+1) underline
75+ ]
76+
77+ firstLine : rest ->
78+ let
79+ filteredFirstLine =
80+ replicate (startColumn - 1) ' '
81+ ++ drop (startColumn - 1) firstLine
82+
83+ filteredLastLine =
84+ take (endColumn) (last rest)
85+
86+ focusedRelevantLines =
87+ filteredFirstLine : init rest ++ [filteredLastLine]
88+
89+ lineNumbersWidth =
90+ length (show (endLine + 1))
91+
92+ numberedLines =
93+ zipWith
94+ (addLineNumber maybeSubRegion lineNumbersWidth)
95+ [startLine - 1 .. endLine + 1]
96+ ("" : focusedRelevantLines ++ [""])
97+ in
98+ unlines numberedLines ++ "\n"
99+
100+
101+addLineNumber :: Maybe (Int,Int) -> Int -> Int -> String -> String
102+addLineNumber maybeSubRegion width n line =
103+ let
104+ number = if n <= 0 then " " else show n
105+ spacer =
106+ case maybeSubRegion of
107+ Just (lo,hi) | lo <= n && n <= hi -> ">"
108+ _ -> " "
109+ in
110+ replicate (width - length number) ' ' ++ number ++ "|" ++ spacer ++ line