on master help sign in
Improve error messages for incomplete patterns in args and let-bindings

Fix #1078

commit5d8dfd9df60d02e3f16fbc5d09910fcfbdd0daaa authorEvan Czaplicki <info@elm-lang.org> dateMon 5 Oct 2015, 14:14 (11 years ago) parentf4ea9e3
2 files changed+100−38
src/Nitpick/PatternMatches.hs+12−13 src/Reporting/Error/Pattern.hs+88−25
src/Nitpick/PatternMatches.hs+12−13
@@ -119,9 +119,6 @@ checkExpression tagDict (A.A region expression) =
119119
120120 go2 a b =
121121 go a <* go b
122−
123− goPattern =
124− checkPatterns tagDict region
125122 in
126123 case expression of
127124 E.Literal _ ->
@@ -139,8 +136,8 @@ checkExpression tagDict (A.A region expression) =
139136 E.Binop _ leftExpr rightExpr ->
140137 go2 leftExpr rightExpr
141138
142− E.Lambda pattern body ->
143− goPattern [pattern]
139+ E.Lambda pattern@(A.A patRegion _) body ->
140+ checkPatterns tagDict patRegion Error.Arg [pattern]
144141 <* go body
145142
146143 E.App func arg ->
@@ -154,13 +151,13 @@ checkExpression tagDict (A.A region expression) =
154151 go body
155152 <* F.traverse_ goDef defs
156153 where
157− goDef (Canonical.Definition _ pattern expr _) =
158− goPattern [pattern]
154+ goDef (Canonical.Definition _ pattern@(A.A patRegion _) expr _) =
155+ checkPatterns tagDict patRegion Error.LetBound [pattern]
159156 <* go expr
160157
161158 E.Case expr branches ->
162159 go expr
163− <* goPattern (map fst branches)
160+ <* checkPatterns tagDict region Error.Case (map fst branches)
164161 <* F.traverse_ (go . snd) branches
165162
166163 E.Data _ctor exprs ->
@@ -196,29 +193,31 @@ checkExpression tagDict (A.A region expression) =
196193checkPatterns
197194 :: TagDict
198195 -> Region.Region
196+ -> Error.Origin
199197 -> [Pattern.CanonicalPattern]
200198 -> Result.Result w Error.Error ()
201−checkPatterns tagDict region patterns =
202− checkPatternsHelp tagDict region [Anything] patterns
199+checkPatterns tagDict region origin patterns =
200+ checkPatternsHelp tagDict region origin [Anything] patterns
203201
204202
205203checkPatternsHelp
206204 :: TagDict
207205 -> Region.Region
206+ -> Error.Origin
208207 -> [Pattern]
209208 -> [Pattern.CanonicalPattern]
210209 -> Result.Result w Error.Error ()
211−checkPatternsHelp tagDict region unhandled patterns =
210+checkPatternsHelp tagDict region origin unhandled patterns =
212211 case (unhandled, patterns) of
213212 ([], []) ->
214213 return ()
215214
216215 (_:_, []) ->
217− Result.throw region (Error.Incomplete unhandled)
216+ Result.throw region (Error.Incomplete origin unhandled)
218217
219218 (_, pattern@(A.A localRegion _) : remainingPatterns) ->
220219 do newUnhandled <- filterPatterns tagDict localRegion pattern unhandled
221− checkPatternsHelp tagDict region newUnhandled remainingPatterns
220+ checkPatternsHelp tagDict region origin newUnhandled remainingPatterns
222221
223222
224223filterPatterns
src/Reporting/Error/Pattern.hs+88−25
@@ -1,26 +1,54 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Pattern where
33
4+import qualified AST.Module.Name as ModuleName
5+import qualified AST.Variable as Var
46import qualified Nitpick.Pattern as Pattern
57import qualified Reporting.PrettyPrint as P
68import qualified Reporting.Report as Report
79
810
911data Error
10− = Incomplete [Pattern.Pattern]
12+ = Incomplete Origin [Pattern.Pattern]
1113 | Redundant
1214
1315
16+data Origin
17+ = Arg
18+ | LetBound
19+ | Case
20+
21+
1422-- TO REPORT
1523
1624toReport :: P.Dealiaser -> Error -> Report.Report
1725toReport dealiaser err =
1826 case err of
19− Incomplete unhandled ->
27+ Incomplete Arg unhandled ->
28+ Report.simple
29+ "PARTIAL PATTERN"
30+ "This argument does not cover all possible inputs."
31+ ( "Any of these patterns can cause a crash:\n"
32+ ++ viewPatternList unhandled
33+ ++ "\n\n"
34+ ++ partialExplanation unhandled
35+ )
36+
37+ Incomplete LetBound unhandled ->
2038 Report.simple
21− "MISSING PATTERN"
22− "The following `case` does not handle all possible inputs."
23− ( "The following patterns are not being handled:\n"
39+ "PARTIAL PATTERN"
40+ "The pattern used here does not cover all possible values."
41+ ( "Any of these patterns can cause a crash:\n"
42+ ++ viewPatternList unhandled
43+ ++ "\n\n"
44+ ++ partialExplanation unhandled
45+ )
46+
47+ Incomplete Case unhandled ->
48+ Report.simple
49+ "INCOMPLETE PATTERN MATCH"
50+ "This `case` does not have branches for all possibilities."
51+ ( "Any of these patterns can cause a crash:\n"
2452 ++ viewPatternList unhandled
2553 ++ "\n\n"
2654 ++ incompleteExplanation
@@ -44,25 +72,60 @@ viewPatternList unhandledPatterns =
4472 ++ if null rest then [] else ["..."]
4573
4674
75+partialExplanation :: [Pattern.Pattern] -> String
76+partialExplanation unhandledPatterns =
77+
78+ "You probably want to switch to a `case` expression to handle all possible\n"
79+ ++ "patterns in a simple way. If you think some cases are impossible, you can use\n"
80+ ++ "`Debug.crash` to make that thinking explicit in your code.\n"
81+ ++ "<http://package.elm-lang.org/packages/elm-lang/core/latest/Debug#crash>"
82+ ++
83+
84+ if isNothing unhandledPatterns then
85+
86+ "\n\nSometimes you *know* a value will never be Nothing, though such cases are very\n"
87+ ++ "rare. If you are certain you are in that case and that there is no better way\n"
88+ ++ "to do things, I'd recommend creating a function `unsafe` that makes the danger\n"
89+ ++ "explicit in the code. <https://github.com/elm-lang/core/issues/215>"
90+
91+ else if isEmptyList unhandledPatterns then
92+
93+ "\n\nSometimes you *know* a list cannot be empty, though such cases are relatively\n"
94+ ++ "rare. If you are certain you are in that case and that there is no better\n"
95+ ++ "way to do things, I'd recommend creating a function like `unsafe` that makes\n"
96+ ++ "the danger explicit in the code. <https://github.com/elm-lang/core/issues/215>"
97+
98+ else
99+
100+ ""
101+
102+
103+isNothing :: [Pattern.Pattern] -> Bool
104+isNothing unhandledPatterns =
105+ case unhandledPatterns of
106+ [Pattern.Data (Var.Canonical (Var.Module moduleName) "Nothing") []] ->
107+ moduleName == ModuleName.inCore ["Maybe"]
108+
109+ _ ->
110+ False
111+
112+
113+isEmptyList :: [Pattern.Pattern] -> Bool
114+isEmptyList unhandledPatterns =
115+ case unhandledPatterns of
116+ [Pattern.Data (Var.Canonical Var.BuiltIn "[]") []] ->
117+ True
118+
119+ _ ->
120+ False
121+
122+
47123incompleteExplanation :: String
48124incompleteExplanation =
49− unlines
50− [ "Did you add a new tag to a union type? Add how it should be handled here and"
51− , "you will be all set!"
52− , ""
53− , "If you want to go quick and implement this later, you can end the pattern with:"
54− , ""
55− , " _ ->"
56− , " Debug.crash \"TODO\""
57− , ""
58− , "When I see cases that end with this pattern, I augment the runtime error with"
59− , "the module name, the relevant line numbers, and the value led down that branch."
60− , ""
61− , "That said, you almost never want this in production code. If you think there"
62− , "are certain cases that are impossible in practice, consider refining how you"
63− , "model the data. If it can never happen in practice, you can probably define a"
64− , "union type such that the \"impossible\" cannot even be constructed in the"
65− , "first place. That is how you make something \"impossible\" truly impossible."
66− , ""
67− , "I won't say \"I told you so\" in the runtime error, but I'll be thinking it ;)"
68− ]
125+ "Did you add a new tag to a union type? Add how it should be handled here and\n"
126+ ++ "you will be all set!\n"
127+ ++ "\n"
128+ ++ "If you want to go quick and implement this later, you may want to look into\n"
129+ ++ "using `Debug.crash` which has some cool error reporting abilities.\n"
130+ ++ "<http://package.elm-lang.org/packages/elm-lang/core/latest/Debug#crash>"
131+