on master help sign in
Turn incomplete pattern matches into a static error

Explain the situation with Debug.crash such that people know they do not have to do a bunch of work to show the problematic value.

commit99a44a022667e31332d73dcc2fa0fbe410136371 authorEvan Czaplicki <info@elm-lang.org> dateTue 8 Sep 2015, 21:36 (11 years ago) parent2297bbf
6 files changed+89−52
elm-compiler.cabal+1 src/Compile.hs+2−1 src/Nitpick/PatternMatches.hs+9−9 src/Reporting/Error.hs+8 src/Reporting/Error/Pattern.hs+69 src/Reporting/Warning.hs−42
elm-compiler.cabal+1
@@ -103,6 +103,7 @@ Library
103103 Reporting.Error.Canonicalize,
104104 Reporting.Error.Docs,
105105 Reporting.Error.Helpers,
106+ Reporting.Error.Pattern,
106107 Reporting.Error.Syntax,
107108 Reporting.Error.Type,
108109 Reporting.PrettyPrint,
src/Compile.hs+2−1
@@ -45,7 +45,8 @@ compile packageName isRoot canonicalImports interfaces source =
4545 Result.mapError Error.Type $
4646 Nitpick.topLevelTypes types (Module.body validModule)
4747
48− Nitpick.patternMatches interfaces canonicalModule
48+ Result.mapError Error.Pattern $
49+ Nitpick.patternMatches interfaces canonicalModule
4950
5051 -- Add the real list of types
5152 let body = (Module.body canonicalModule) { Module.types = types }
src/Nitpick/PatternMatches.hs+9−9
@@ -17,15 +17,15 @@ import qualified AST.Variable as Var
1717import Elm.Utils ((|>))
1818import Nitpick.Pattern (Pattern(..), fromCanonicalPattern)
1919import qualified Reporting.Annotation as A
20+import qualified Reporting.Error.Pattern as Error
2021import qualified Reporting.Region as Region
2122import qualified Reporting.Result as Result
22−import qualified Reporting.Warning as Warning
2323
2424
2525patternMatches
2626 :: Module.Interfaces
2727 -> Module.CanonicalModule
28− -> Result.Result Warning.Warning e ()
28+ -> Result.Result w Error.Error ()
2929patternMatches interfaces modul =
3030 let
3131 name = Module.name modul
@@ -102,7 +102,7 @@ lookupOtherTags (Var.Canonical home name) tagDict =
102102checkExpression
103103 :: TagDict
104104 -> Canonical.Expr
105− -> Result.Result Warning.Warning e ()
105+ -> Result.Result w Error.Error ()
106106checkExpression tagDict (A.A region expression) =
107107 let
108108 go =
@@ -188,7 +188,7 @@ checkPatterns
188188 :: TagDict
189189 -> Region.Region
190190 -> [Pattern.CanonicalPattern]
191− -> Result.Result Warning.Warning e ()
191+ -> Result.Result w Error.Error ()
192192checkPatterns tagDict region patterns =
193193 checkPatternsHelp tagDict region [Anything] patterns
194194
@@ -198,14 +198,14 @@ checkPatternsHelp
198198 -> Region.Region
199199 -> [Pattern]
200200 -> [Pattern.CanonicalPattern]
201− -> Result.Result Warning.Warning e ()
201+ -> Result.Result w Error.Error ()
202202checkPatternsHelp tagDict region unhandled patterns =
203203 case (unhandled, patterns) of
204204 ([], []) ->
205205 return ()
206206
207207 (_:_, []) ->
208− Result.warn region (Warning.InexhaustivePatternMatch unhandled)
208+ Result.throw region (Error.Incomplete unhandled)
209209
210210 (_, pattern@(A.A localRegion _) : remainingPatterns) ->
211211 do newUnhandled <- filterPatterns tagDict localRegion pattern unhandled
@@ -217,7 +217,7 @@ filterPatterns
217217 -> Region.Region
218218 -> Pattern.CanonicalPattern
219219 -> [Pattern]
220− -> Result.Result Warning.Warning e [Pattern]
220+ -> Result.Result w Error.Error [Pattern]
221221filterPatterns tagDict region pattern unhandled =
222222 let
223223 nitPattern =
@@ -227,8 +227,8 @@ filterPatterns tagDict region pattern unhandled =
227227 intersection pat nitPattern == Nothing
228228 in
229229 if all noIntersection unhandled then
230− do Result.warn region Warning.RedundantPatternMatch
231− return unhandled
230+ Result.throw region Error.Redundant
231+
232232 else
233233 do let complementPatterns = complement tagDict nitPattern
234234 return $
src/Reporting/Error.hs+8
@@ -9,6 +9,7 @@ import Prelude hiding (print)
99import qualified Reporting.Annotation as A
1010import qualified Reporting.Error.Canonicalize as Canonicalize
1111import qualified Reporting.Error.Docs as Docs
12+import qualified Reporting.Error.Pattern as Pattern
1213import qualified Reporting.Error.Syntax as Syntax
1314import qualified Reporting.Error.Type as Type
1415import qualified Reporting.PrettyPrint as P
@@ -21,6 +22,7 @@ data Error
2122 = Syntax Syntax.Error
2223 | Canonicalize Canonicalize.Error
2324 | Type Type.Error
25+ | Pattern Pattern.Error
2426 | Docs Docs.Error
2527
2628
@@ -38,6 +40,9 @@ toReport dealiaser err =
3840 Type typeError ->
3941 Type.toReport dealiaser typeError
4042
43+ Pattern patternError ->
44+ Pattern.toReport dealiaser patternError
45+
4146 Docs docsError ->
4247 Docs.toReport docsError
4348
@@ -76,6 +81,9 @@ toJson dealiaser filePath (A.A region err) =
7681 Type typeError ->
7782 Report.toJson [] (Type.toReport dealiaser typeError)
7883
84+ Pattern patternError ->
85+ Report.toJson [] (Pattern.toReport dealiaser patternError)
86+
7987 Docs docsError ->
8088 Report.toJson [] (Docs.toReport docsError)
8189 in
src/Reporting/Error/Pattern.hsnew file+69
@@ -0,0 +1,69 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Reporting.Error.Pattern where
3+
4+import qualified Data.Char as Char
5+import Text.PrettyPrint ((<+>))
6+import qualified Text.PrettyPrint as P
7+
8+import qualified AST.Helpers as Help
9+import qualified AST.Type as Type
10+import qualified AST.Variable as Var
11+import qualified Nitpick.Pattern as Pattern
12+import qualified Reporting.PrettyPrint as P
13+import qualified Reporting.Region as Region
14+import qualified Reporting.Report as Report
15+
16+
17+data Error
18+ = Incomplete [Pattern.Pattern]
19+ | Redundant
20+
21+
22+-- TO REPORT
23+
24+toReport :: P.Dealiaser -> Error -> Report.Report
25+toReport dealiaser err =
26+ case err of
27+ Incomplete unhandled ->
28+ Report.simple
29+ "MISSING PATTERN"
30+ "The following `case` does not handle all possible inputs."
31+ ( "The following patterns are not being handled:\n"
32+ ++ viewPatternList unhandled
33+ ++ "\n\n"
34+ ++ "Did you add a new tag to a union type? Add how it should be handled here and"
35+ ++ "you will be all set!"
36+ ++ ""
37+ ++ "If you want to go quick and implement this later, you can end the pattern with:"
38+ ++ ""
39+ ++ " _ ->"
40+ ++ " Debug.crash \"TODO\""
41+ ++ ""
42+ ++ "When I see cases that end with this pattern, I augment the runtime error with"
43+ ++ "the module name, the relevant line numbers, and the value led down that branch."
44+ ++ ""
45+ ++ "That said, you almost never want this in production code. If you think there"
46+ ++ "are certain cases that are impossible in practice, consider refining how you"
47+ ++ "model the data. If it can never happen in practice, you can probably define a"
48+ ++ "union type such that the \"impossible\" cannot even be constructed in the"
49+ ++ "first place. That is how you make something \"impossible\" truly impossible."
50+ ++ ""
51+ ++ "I won't say \"I told you so\" in the runtime error, but I'll be thinking it ;)"
52+ )
53+
54+ Redundant ->
55+ Report.simple
56+ "REDUNDANT PATTERN"
57+ "The following pattern is redundant."
58+ "Any value with this shape will be handled by a previous pattern."
59+
60+
61+viewPatternList :: [Pattern.Pattern] -> String
62+viewPatternList unhandledPatterns =
63+ let
64+ (showPatterns, rest) =
65+ splitAt 4 unhandledPatterns
66+ in
67+ concatMap ((++) "\n ") $
68+ map Pattern.toString showPatterns
69+ ++ if null rest then [] else ["..."]
src/Reporting/Warning.hs−42
@@ -9,7 +9,6 @@ import Text.PrettyPrint ((<+>))
99
1010import qualified AST.Module.Name as ModuleName
1111import qualified AST.Type as Type
12−import qualified Nitpick.Pattern as Pattern
1312import qualified Reporting.Annotation as A
1413import qualified Reporting.PrettyPrint as P
1514import qualified Reporting.Report as Report
@@ -20,8 +19,6 @@ import qualified Reporting.Report as Report
2019data Warning
2120 = UnusedImport ModuleName.Raw
2221 | MissingTypeAnnotation String Type.Canonical
23− | InexhaustivePatternMatch [Pattern.Pattern]
24− | RedundantPatternMatch
2522
2623
2724-- TO STRING
@@ -59,32 +56,6 @@ toReport dealiaser warning =
5956 4
6057 (P.pretty dealiaser False inferredType)
6158
62− InexhaustivePatternMatch unhandled ->
63− Report.simple
64− "missing pattern"
65− "The following case expression does not handle all possible inputs."
66− ( "The following patterns are not being handled:\n"
67− ++ viewPatternList unhandled
68− ++ "\n\n"
69− ++ "If we get values like this, we have no choice but to crash! Normally this means\n"
70− ++ "you just added a new tag to a union type, but sometimes it can be because there\n"
71− ++ "is something very odd about the data you are modeling. In those rare cases, you\n"
72− ++ "probably want to either:\n"
73− ++ "\n"
74− ++ " 1. Rethink the model. Maybe some cases are \"impossible\" but it is still\n"
75− ++ " possible to construct such cases. Is there a way to model the values more\n"
76− ++ " precisely such that \"impossible\" values truly are impossible?\n"
77− ++ " 2. End the pattern match with a wildcard match that leads to an expression\n"
78− ++ " like: Debug.crash \"If you are reading this, I have made a mistake!\"\n"
79− ++ " Generally speaking, you do not want it to have to be this way."
80− )
81−
82− RedundantPatternMatch ->
83− Report.simple
84− "redundant pattern"
85− "The following pattern is redundant."
86− "Any value with this shape will be handled by a previous pattern."
87−
8859
8960-- TO JSON
9061
@@ -100,16 +71,3 @@ toJson dealiaser filePath (A.A region warning) =
10071 , "type" .= ("warning" :: String)
10172 ]
10273 ++ additionalFields
103−
104−
105−-- PATTERN WARNINGS
106−
107−viewPatternList :: [Pattern.Pattern] -> String
108−viewPatternList unhandledPatterns =
109− let
110− (showPatterns, rest) =
111− splitAt 4 unhandledPatterns
112− in
113− concatMap ((++) "\n ") $
114− map Pattern.toString showPatterns
115− ++ if null rest then [] else ["..."]