on master help sign in
Pleasant messages when a pattern is missing
commitc2e4a69a902a2a9cddc7f3a475131f6fcb230a9d authorEvan Czaplicki <info@elm-lang.org> dateTue 18 Aug 2015, 18:36 (11 years ago) parentc61d87f
4 files changed+101−56
elm-compiler.cabal+1 src/Nitpick/Pattern.hs+61 src/Nitpick/PatternMatches.hs+11−48 src/Reporting/Warning.hs+28−8
elm-compiler.cabal+1
@@ -79,6 +79,7 @@ Library
7979 Generate.JavaScript.Helpers,
8080 Generate.JavaScript.Port,
8181 Generate.JavaScript.Variable,
82+ Nitpick.Pattern,
8283 Nitpick.PatternMatches,
8384 Nitpick.TopLevelTypes,
8485 Optimize,
src/Nitpick/Pattern.hs+61
@@ -1,5 +1,6 @@
11module Nitpick.Pattern where
22
3+import qualified Data.List as List
34import qualified Data.Set as Set
45
56import qualified AST.Literal as L
@@ -8,3 +9,63 @@ import qualified AST.Variable as Var
89import qualified Reporting.Annotation as A
910
1011
12+data Pattern
13+ = Data Var.Canonical [Pattern]
14+ | Record [String]
15+ | Alias String Pattern
16+ | Var String
17+ | Anything
18+ | Literal L.Literal
19+ | AnythingBut (Set.Set L.Literal)
20+ deriving (Show, Eq)
21+
22+
23+fromCanonicalPattern :: P.CanonicalPattern -> Pattern
24+fromCanonicalPattern (A.A _ pattern) =
25+ case pattern of
26+ P.Data tag patterns ->
27+ Data tag (map fromCanonicalPattern patterns)
28+
29+ P.Record fields ->
30+ Record fields
31+
32+ P.Alias name pattern ->
33+ Alias name (fromCanonicalPattern pattern)
34+
35+ P.Var name ->
36+ Var name
37+
38+ P.Anything ->
39+ Anything
40+
41+ P.Literal literal ->
42+ Literal literal
43+
44+
45+-- TO STRING
46+
47+toString :: Pattern -> String
48+toString pattern =
49+ case pattern of
50+ Data tag args ->
51+ List.intercalate " " (Var.toString tag : map toString args)
52+
53+ Record fields ->
54+ "{ " ++ List.intercalate ", " fields ++ " }"
55+
56+ Alias alias realPattern ->
57+ toString realPattern ++ " as " ++ alias
58+
59+ Var name ->
60+ name
61+
62+ Anything ->
63+ "_"
64+
65+ Literal literal ->
66+ L.toString literal
67+
68+ AnythingBut literalSet ->
69+ List.intercalate " " ("<values besides:" : map L.toString (Set.toList literalSet))
70+
71+
src/Nitpick/PatternMatches.hs+11−48
@@ -10,11 +10,11 @@ import qualified Data.Set as Set
1010import qualified AST.Expression.General as E
1111import qualified AST.Expression.Canonical as Canonical
1212import qualified AST.Helpers as Help
13−import qualified AST.Literal as L
1413import qualified AST.Module as Module
15−import qualified AST.Pattern as P
14+import qualified AST.Pattern as Pattern
1615import qualified AST.Variable as Var
1716import Elm.Utils ((|>))
17+import Nitpick.Pattern (Pattern(..), fromCanonicalPattern)
1818import qualified Reporting.Annotation as A
1919import qualified Reporting.Region as Region
2020import qualified Reporting.Result as Result
@@ -35,42 +35,6 @@ patternMatches interfaces modul =
3535 (Module.program body)
3636
3737
38−-- PATTERN MATCH DATA STRUCTURE
39−
40−data Pattern
41− = Data Var.Canonical [Pattern]
42− | Record [String]
43− | Alias String Pattern
44− | Var String
45− | Anything
46− | Literal L.Literal
47− | AnythingBut (Set.Set L.Literal)
48− deriving (Show, Eq)
49−
50−
51−fromCanonicalPattern :: P.CanonicalPattern -> Pattern
52−fromCanonicalPattern (A.A _ pattern) =
53− case pattern of
54− P.Data ctor patterns ->
55− Data ctor (map fromCanonicalPattern patterns)
56−
57− P.Record fields ->
58− Record fields
59−
60− P.Alias name pattern ->
61− Alias name (fromCanonicalPattern pattern)
62−
63− P.Var name ->
64− Var name
65−
66− P.Anything ->
67− Anything
68−
69− P.Literal literal ->
70− Literal literal
71−
72−
73−
7438-- TAG DICT
7539
7640type TagDict =
@@ -116,8 +80,8 @@ toTagMapping adts =
11680 |> Map.fromList
11781
11882
119−lookupOtherCtors :: Var.Canonical -> TagDict -> [(Tag, Int)]
120−lookupOtherCtors (Var.Canonical home name) tagDict =
83+lookupOtherTags :: Var.Canonical -> TagDict -> [(Tag, Int)]
84+lookupOtherTags (Var.Canonical home name) tagDict =
12185 case Map.lookup name =<< Map.lookup home tagDict of
12286 Just otherTags ->
12387 otherTags
@@ -144,7 +108,7 @@ checkExpression tagDict (A.A region expression) =
144108 checkExpression tagDict
145109
146110 go2 a b =
147− F.traverse_ go [a,b]
111+ go a <* go b
148112
149113 goPattern =
150114 checkPatterns tagDict region
@@ -157,8 +121,7 @@ checkExpression tagDict (A.A region expression) =
157121 Result.ok ()
158122
159123 E.Range low high ->
160− go low
161− <* go high
124+ go2 low high
162125
163126 E.ExplicitList listExprs ->
164127 F.traverse_ go listExprs
@@ -232,7 +195,7 @@ checkExpression tagDict (A.A region expression) =
232195checkPatterns
233196 :: TagDict
234197 -> Region.Region
235− -> [P.CanonicalPattern]
198+ -> [Pattern.CanonicalPattern]
236199 -> Result.Result Warning.Warning e ()
237200checkPatterns tagDict region patterns =
238201 checkPatternsHelp tagDict region [Anything] patterns
@@ -242,7 +205,7 @@ checkPatternsHelp
242205 :: TagDict
243206 -> Region.Region
244207 -> [Pattern]
245− -> [P.CanonicalPattern]
208+ -> [Pattern.CanonicalPattern]
246209 -> Result.Result Warning.Warning e ()
247210checkPatternsHelp tagDict region unhandled patterns =
248211 case (unhandled, patterns) of
@@ -250,7 +213,7 @@ checkPatternsHelp tagDict region unhandled patterns =
250213 return ()
251214
252215 (_:_, []) ->
253− Result.warn region Warning.InexhaustivePatternMatch
216+ Result.warn region (Warning.InexhaustivePatternMatch unhandled)
254217
255218 (_, pattern@(A.A localRegion _) : remainingPatterns) ->
256219 do newUnhandled <- filterPatterns tagDict localRegion pattern unhandled
@@ -260,7 +223,7 @@ checkPatternsHelp tagDict region unhandled patterns =
260223filterPatterns
261224 :: TagDict
262225 -> Region.Region
263− -> P.CanonicalPattern
226+ -> Pattern.CanonicalPattern
264227 -> [Pattern]
265228 -> Result.Result Warning.Warning e [Pattern]
266229filterPatterns tagDict region pattern unhandled =
@@ -370,7 +333,7 @@ complementData :: TagDict -> Var.Canonical -> [Pattern] -> [Pattern]
370333complementData tagDict tag patterns =
371334 let
372335 otherTags =
373− lookupOtherCtors tag tagDict
336+ lookupOtherTags tag tagDict
374337
375338 tagComplements =
376339 Maybe.mapMaybe (tagToPattern tag) otherTags
src/Reporting/Warning.hs+28−8
@@ -9,6 +9,7 @@ import Text.PrettyPrint ((<+>))
99
1010import qualified AST.Module as Module
1111import qualified AST.Type as Type
12+import qualified Nitpick.Pattern as Pattern
1213import qualified Reporting.Annotation as A
1314import qualified Reporting.PrettyPrint as P
1415import qualified Reporting.Report as Report
@@ -19,7 +20,7 @@ import qualified Reporting.Report as Report
1920data Warning
2021 = UnusedImport Module.Name
2122 | MissingTypeAnnotation String Type.Canonical
22− | InexhaustivePatternMatch
23+ | InexhaustivePatternMatch [Pattern.Pattern]
2324 | RedundantPatternMatch
2425
2526
@@ -58,18 +59,24 @@ toReport dealiaser warning =
5859 4
5960 (P.pretty dealiaser False inferredType)
6061
61− InexhaustivePatternMatch ->
62+ InexhaustivePatternMatch unhandled ->
6263 Report.simple
6364 "missing pattern"
6465 "The following case expression does not handle all possible inputs."
65− ( "If none of the patterns match the given value, we have no choice but to crash!\n"
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"
6673 ++ "\n"
67− ++ "If you somehow know that this pattern can never happen, you want to:\n"
68− ++ "\n"
69− ++ " 1. Rethink the data structures you are using. Is there a way to model the\n"
70− ++ " values more precisely?\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"
7177 ++ " 2. End the pattern match with a wildcard match that leads to an expression\n"
72− ++ " like: Debug.crash \"This should never happen, but I guess it can!\""
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."
7380 )
7481
7582 RedundantPatternMatch ->
@@ -93,3 +100,16 @@ toJson dealiaser filePath (A.A region warning) =
93100 , "type" .= ("warning" :: String)
94101 ]
95102 ++ 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 ["..."]