on master help sign in
Give warnings on unused bindings and variables
dfaef18Evan Czaplicki
src/Canonicalize/Expression.hs+30−15
@@ -18,6 +18,7 @@ import qualified Canonicalize.Environment as Env
18import qualified Canonicalize.Environment.Dups as Dups
18import qualified Canonicalize.Environment.Dups as Dups
19import qualified Canonicalize.Pattern as Pattern
19import qualified Canonicalize.Pattern as Pattern
20import qualified Canonicalize.Type as Type
20import qualified Canonicalize.Type as Type
21+import qualified Data.Bag as Bag
21import qualified Elm.Name as N
22import qualified Elm.Name as N
22import qualified Reporting.Annotation as A
23import qualified Reporting.Annotation as A
23import qualified Reporting.Error.Canonicalize as Error
24import qualified Reporting.Error.Canonicalize as Error
@@ -69,7 +70,7 @@ canonicalize env (A.A region expression) =
69 addLocals $
70 addLocals $
70 do cargs@(Can.Args _ destructors) <- Pattern.canonicalizeArgs env args
71 do cargs@(Can.Args _ destructors) <- Pattern.canonicalizeArgs env args
71 newEnv <- Env.addLocals destructors env
72 newEnv <- Env.addLocals destructors env
72− removeLocals destructors $
73+ removeLocals Warning.Pattern destructors $
73 Can.Lambda cargs <$> canonicalize newEnv body
74 Can.Lambda cargs <$> canonicalize newEnv body
74
75
75 Valid.Call func args ->
76 Valid.Call func args ->
@@ -159,7 +160,7 @@ canonicalizeCaseBranch env (pattern, expr) =
159 addLocals $
160 addLocals $
160 do match@(Can.Match _ destructors) <- Pattern.canonicalizeMatch env pattern
161 do match@(Can.Match _ destructors) <- Pattern.canonicalizeMatch env pattern
161 newEnv <- Env.addLocals destructors env
162 newEnv <- Env.addLocals destructors env
162− removeLocals destructors $
163+ removeLocals Warning.Pattern destructors $
163 (,) match <$> canonicalize newEnv expr
164 (,) match <$> canonicalize newEnv expr
164
165
165
166
@@ -257,7 +258,7 @@ canonicalizeLet region env defs body =
257 newEnv <- Env.addLocals boundNames env
258 newEnv <- Env.addLocals boundNames env
258 let defKeys = Map.map A.drop boundNames
259 let defKeys = Map.map A.drop boundNames
259 nodes <- Result.untracked $ traverse (bindingToNode defKeys newEnv) bindings
260 nodes <- Result.untracked $ traverse (bindingToNode defKeys newEnv) bindings
260− removeLocals defKeys $
261+ removeLocals Warning.Binding boundNames $
261 do cbody <- canonicalize env body
262 do cbody <- canonicalize env body
262 detectCycles region (Graph.stronglyConnComp nodes) cbody
263 detectCycles region (Graph.stronglyConnComp nodes) cbody
263
264
@@ -272,18 +273,32 @@ addLocals (Result.Result () warnings answer) =
272 Result.Result Set.empty warnings (Result.Err err)
273 Result.Result Set.empty warnings (Result.Err err)
273
274
274
275
275−removeLocals :: Map.Map N.Name a -> Result FreeLocals expr -> Result () (expr, FreeLocals)
276+removeLocals :: Warning.Unused -> Map.Map N.Name (A.Located a) -> Result FreeLocals expr -> Result () (expr, FreeLocals)
276−removeLocals boundNames (Result.Result freeLocals warnings answer) =
277+removeLocals unused boundNames (Result.Result freeLocals warnings answer) =
277− Result.Result () warnings $
278− case answer of
278+ case answer of
279+ Result.Err err ->
280+ Result.Result () warnings (Result.Err err)
281+
279− Result.Ok value ->
282+ Result.Ok value ->
280− Result.Ok
283+ let
281− ( value
284+ newFreeLocals =
282− , Set.difference freeLocals (Map.keysSet boundNames)
285+ Set.difference freeLocals (Map.keysSet boundNames)
283− )
286+
284−
287+ newWarnings =
285− Result.Err err ->
288+ -- NOTE: check for unused variables without any large allocations.
286− Result.Err err
289+ -- This relies on the fact that Map.size and Set.size are O(1)
290+ if Set.size freeLocals - Map.size boundNames == Set.size newFreeLocals then
291+ warnings
292+ else
293+ Bag.append warnings $ Bag.fromList (toUnusedWarning unused) $
294+ Map.toList (Map.withoutKeys boundNames freeLocals)
295+ in
296+ Result.Result () newWarnings (Result.Ok (value, newFreeLocals))
297+
298+
299+toUnusedWarning :: Warning.Unused -> (N.Name, A.Located a) -> A.Located Warning.Warning
300+toUnusedWarning unused (name, A.A region _) =
301+ A.A region (Warning.UnusedVariable unused name)
287
302
288
303
289
304
src/Canonicalize/Module.hs+4−2
@@ -160,14 +160,16 @@ toNode env (A.A region (Valid.Decl name@(A.A _ key) args body tipe)) =
160 case args of
160 case args of
161 [] ->
161 [] ->
162 do ctipe <- traverse (Type.canonicalize env) tipe
162 do ctipe <- traverse (Type.canonicalize env) tipe
163− (cbody, freeLocals) <- Expr.removeLocals Map.empty $ Expr.canonicalize env body
163+ (cbody, freeLocals) <- Expr.removeLocals Warning.Pattern Map.empty $
164+ Expr.canonicalize env body
164 return (Value region name cbody ctipe, key, Set.toList freeLocals)
165 return (Value region name cbody ctipe, key, Set.toList freeLocals)
165
166
166 _ ->
167 _ ->
167 do ctipe <- traverse (Type.canonicalize env) tipe
168 do ctipe <- traverse (Type.canonicalize env) tipe
168 cargs@(Can.Args _ destructors) <- Pattern.canonicalizeArgs env args
169 cargs@(Can.Args _ destructors) <- Pattern.canonicalizeArgs env args
169 newEnv <- Env.addLocals destructors env
170 newEnv <- Env.addLocals destructors env
170− (cbody, freeLocals) <- Expr.removeLocals destructors $ Expr.canonicalize newEnv body
171+ (cbody, freeLocals) <- Expr.removeLocals Warning.Pattern destructors $
172+ Expr.canonicalize newEnv body
171 return (Function key cargs cbody ctipe, key, Set.toList freeLocals)
173 return (Function key cargs cbody ctipe, key, Set.toList freeLocals)
172
174
173
175
src/Reporting/Warning.hs+6−6
@@ -31,7 +31,7 @@ data Warning
31 | MissingTypeAnnotation Text Type.Canonical
31 | MissingTypeAnnotation Text Type.Canonical
32
32
33
33
34−data Unused = Argument | Binding
34+data Unused = Pattern | Binding
35
35
36
36
37
37
@@ -48,14 +48,14 @@ toReport localizer warning =
48 ("Nothing from the `" <> moduleName <> "` module is used in this file.")
48 ("Nothing from the `" <> moduleName <> "` module is used in this file.")
49 (text "I recommend removing unused imports.")
49 (text "I recommend removing unused imports.")
50
50
51− UnusedVariable Argument name ->
51+ UnusedVariable Pattern name ->
52 Report.report
52 Report.report
53− "unused argument"
53+ "unused variable"
54 Nothing
54 Nothing
55− ("The `" <> name <> "` argument is unused.")
55+ ("The `" <> name <> "` variable is unused.")
56 ( Help.reflowParagraph $
56 ( Help.reflowParagraph $
57− "Maybe this indicates there is a mistake around here? Switching the\
57+ "Maybe this indicates there is a mistake around here? Switching `"
58− \ argument to a _ will indicate that it is intentionally unused."
58+ <> name <> "` to _ will indicate that it is intentionally unused."
59 )
59 )
60
60
61 UnusedVariable Binding name ->
61 UnusedVariable Binding name ->