elm-review/src/Lint/Rule/NoUnusedVariables.elm

825 lines
24 KiB
Elm
Raw Normal View History

2018-11-22 21:19:19 +03:00
module Lint.Rule.NoUnusedVariables exposing (rule)
2019-07-03 15:19:29 +03:00
{-| Forbid variables or types that are declared or imported but never used.
2018-11-22 21:19:19 +03:00
2019-07-03 15:19:29 +03:00
# Rule
@docs rule
2018-11-22 21:19:19 +03:00
-}
import Dict exposing (Dict)
import Elm.Syntax.Declaration exposing (Declaration(..))
import Elm.Syntax.Exposing exposing (Exposing(..), TopLevelExpose(..))
import Elm.Syntax.Expression exposing (Expression(..), Function, FunctionImplementation, LetDeclaration(..))
2018-11-22 21:19:19 +03:00
import Elm.Syntax.Import exposing (Import)
import Elm.Syntax.Module as Module exposing (Module(..))
import Elm.Syntax.Node as Node exposing (Node(..))
import Elm.Syntax.Pattern as Pattern exposing (Pattern)
2018-11-22 21:19:19 +03:00
import Elm.Syntax.Range exposing (Range)
import Elm.Syntax.TypeAnnotation exposing (TypeAnnotation(..))
import Lint.Fix as Fix exposing (Fix)
2019-06-26 14:47:00 +03:00
import Lint.Rule as Rule exposing (Direction, Error, Rule)
2018-11-22 21:19:19 +03:00
import List.Nonempty as Nonempty exposing (Nonempty)
import Set exposing (Set)
2019-07-03 15:19:29 +03:00
{-| Forbid variables or types that are declared or imported but never used.
2018-11-26 13:22:43 +03:00
config =
2019-07-25 15:35:58 +03:00
[ NoUnusedVariables.rule
2018-11-26 13:22:43 +03:00
]
## Fail
-- module A exposing (a)
a n =
n + 1
b =
a 2
## Success
-- module A exposing (a)
a n =
n + 1
2018-11-26 13:22:43 +03:00
-}
rule : Rule
2019-06-15 22:14:40 +03:00
rule =
Rule.newSchema "NoUnusedVariables"
2019-06-24 01:32:27 +03:00
|> Rule.withInitialContext initialContext
|> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor
|> Rule.withImportVisitor importVisitor
|> Rule.withExpressionVisitor expressionVisitor
|> Rule.withDeclarationVisitor declarationVisitor
|> Rule.withFinalEvaluation finalEvaluation
|> Rule.fromSchema
type alias Context =
{ scopes : Nonempty Scope
, exposesEverything : Bool
, constructorNameToTypeName : Dict String String
, declaredModules : Dict String ( VariableType, Range )
, usedModules : Set String
2019-06-24 01:32:27 +03:00
}
type alias Scope =
{ declared : Dict String ( VariableType, Range )
, used : Set String
}
2018-11-26 13:22:43 +03:00
2018-11-26 21:16:04 +03:00
type VariableType
= Variable Range
2018-11-26 20:39:46 +03:00
| ImportedModule
2018-11-26 21:16:04 +03:00
| ImportedVariable
| ImportedType
| ImportedOperator
2018-11-26 20:39:46 +03:00
| ModuleAlias
| Type
2018-11-26 21:09:18 +03:00
| Port
2018-11-26 20:39:46 +03:00
2019-06-24 01:32:27 +03:00
initialContext : Context
initialContext =
{ scopes = Nonempty.fromElement emptyScope
, exposesEverything = False
, constructorNameToTypeName = Dict.empty
, declaredModules = Dict.empty
, usedModules = Set.empty
2018-11-22 21:19:19 +03:00
}
emptyScope : Scope
emptyScope =
{ declared = Dict.empty
, used = Set.empty
}
2018-11-22 21:19:19 +03:00
2018-11-26 21:16:04 +03:00
error : VariableType -> Range -> String -> Error
2018-11-26 20:39:46 +03:00
error variableType range_ name =
Rule.error
{ message = variableTypeToString variableType ++ " `" ++ name ++ "` is not used" ++ variableTypeWarning variableType
, details = [ "Since it is not being used, I recommend removing it. It should make the code clearer to read for other people." ]
}
range_
|> Rule.withFixes (fixes variableType)
fixes : VariableType -> List Fix
fixes variableType =
case variableType of
Variable range ->
[ Fix.removeRange range ]
ImportedModule ->
[]
ImportedVariable ->
[]
ImportedType ->
[]
ImportedOperator ->
[]
ModuleAlias ->
[]
Type ->
[]
Port ->
[]
2018-11-26 20:39:46 +03:00
2018-11-26 21:16:04 +03:00
variableTypeToString : VariableType -> String
2018-11-26 20:39:46 +03:00
variableTypeToString value =
case value of
Variable _ ->
2018-11-26 20:39:46 +03:00
"Variable"
ImportedModule ->
"Imported module"
2018-11-26 21:16:04 +03:00
ImportedVariable ->
"Imported variable"
ImportedType ->
"Imported type"
ImportedOperator ->
"Imported operator"
2018-11-26 20:39:46 +03:00
ModuleAlias ->
"Module alias"
Type ->
"Type"
2018-11-22 21:19:19 +03:00
2018-11-26 21:09:18 +03:00
Port ->
"Port"
2018-11-26 21:16:04 +03:00
variableTypeWarning : VariableType -> String
2018-11-26 21:09:18 +03:00
variableTypeWarning value =
case value of
Variable _ ->
2018-11-26 21:09:18 +03:00
""
ImportedModule ->
""
2018-11-26 21:16:04 +03:00
ImportedVariable ->
""
ImportedType ->
""
ImportedOperator ->
""
2018-11-26 21:09:18 +03:00
ModuleAlias ->
""
Type ->
""
Port ->
" (Warning: Removing this port may break your application if it is used in the JS code)"
2018-11-22 21:19:19 +03:00
2019-06-24 01:32:27 +03:00
moduleDefinitionVisitor : Node Module -> Context -> ( List Error, Context )
moduleDefinitionVisitor (Node _ moduleNode) context =
case Module.exposingList moduleNode of
2018-11-22 21:19:19 +03:00
All _ ->
( [], { context | exposesEverything = True } )
2018-11-22 21:19:19 +03:00
Explicit list ->
let
names =
List.filterMap
(\(Node _ node) ->
case node of
2018-11-22 21:19:19 +03:00
FunctionExpose name ->
Just name
TypeOrAliasExpose name ->
Just name
TypeExpose { name } ->
Just name
InfixExpose name ->
-- Just name
Nothing
)
list
in
( [], markAllAsUsed names context )
2018-11-22 21:19:19 +03:00
2019-06-24 01:32:27 +03:00
importVisitor : Node Import -> Context -> ( List Error, Context )
importVisitor (Node _ { exposingList, moduleAlias, moduleName }) context =
case Maybe.map Node.value exposingList of
2018-11-26 20:12:36 +03:00
Nothing ->
let
( variableType, Node nameNodeRange nameNodeValue ) =
case moduleAlias of
Just moduleAlias_ ->
( ModuleAlias, moduleAlias_ )
2018-11-26 20:39:46 +03:00
Nothing ->
( ImportedModule, moduleName )
2018-11-26 20:12:36 +03:00
in
( []
, register
2018-11-26 20:39:46 +03:00
variableType
nameNodeRange
(getModuleName nameNodeValue)
context
2018-11-26 20:12:36 +03:00
)
2018-11-22 21:19:19 +03:00
2018-11-26 20:12:36 +03:00
Just declaredImports ->
2019-07-25 10:14:33 +03:00
let
contextWithoutImports : Context
contextWithoutImports =
case moduleAlias of
Just (Node range value) ->
2019-07-25 10:14:33 +03:00
register
ModuleAlias
range
(getModuleName value)
2019-07-25 10:14:33 +03:00
context
Nothing ->
context
in
2018-11-26 20:12:36 +03:00
( []
, List.foldl
(\( variableType, range, name ) context_ -> register variableType range name context_)
2019-07-25 10:14:33 +03:00
contextWithoutImports
2018-11-26 20:12:36 +03:00
(collectFromExposing declaredImports)
)
2018-11-22 21:19:19 +03:00
2019-06-24 01:32:27 +03:00
expressionVisitor : Node Expression -> Direction -> Context -> ( List Error, Context )
expressionVisitor node direction context =
case ( direction, Node.value node ) of
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, FunctionOrValue [] name ) ->
( [], markAsUsed name context )
2018-11-22 21:19:19 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, FunctionOrValue moduleName name ) ->
( [], markModuleAsUsed (getModuleName moduleName) context )
2018-11-22 21:19:19 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, OperatorApplication name _ _ _ ) ->
( [], markAsUsed name context )
2018-11-22 21:19:19 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, PrefixOperator name ) ->
( [], markAsUsed name context )
2018-11-22 21:19:19 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, LetExpression { declarations } ) ->
2018-11-26 13:22:43 +03:00
let
newContext : Context
2018-11-26 13:22:43 +03:00
newContext =
List.foldl
(\declaration context_ ->
case Node.value declaration of
2018-11-26 13:22:43 +03:00
LetFunction function ->
registerFunction function context_
2018-11-22 21:19:19 +03:00
2018-11-26 13:22:43 +03:00
LetDestructuring pattern _ ->
context_
2018-11-26 13:22:43 +03:00
)
{ context | scopes = Nonempty.cons emptyScope context.scopes }
2018-11-26 13:22:43 +03:00
declarations
in
( [], newContext )
( Rule.OnExit, RecordUpdateExpression expr _ ) ->
( [], markAsUsed (Node.value expr) context )
( Rule.OnExit, CaseExpression { cases } ) ->
let
usedVariables : { types : List String, modules : List String }
usedVariables =
cases
|> List.map
(\( patternNode, expressionNode ) ->
getUsedVariablesFromPattern patternNode
)
|> foldUsedTypesAndModules
in
( []
, markUsedTypesAndModules usedVariables context
)
2019-06-26 12:42:17 +03:00
( Rule.OnExit, LetExpression _ ) ->
2018-11-26 13:22:43 +03:00
let
( errors, remainingUsed ) =
makeReport (Nonempty.head context.scopes)
2018-11-26 13:22:43 +03:00
contextWithPoppedScope =
{ context | scopes = Nonempty.pop context.scopes }
2018-11-26 13:22:43 +03:00
in
( errors
, markAllAsUsed remainingUsed contextWithPoppedScope
2018-11-26 13:22:43 +03:00
)
_ ->
( [], context )
2018-11-22 21:19:19 +03:00
getUsedVariablesFromPattern : Node Pattern -> { types : List String, modules : List String }
getUsedVariablesFromPattern patternNode =
{ types = getUsedTypesFromPattern patternNode
, modules = getUsedModulesFromPattern patternNode
}
getUsedTypesFromPattern : Node Pattern -> List String
getUsedTypesFromPattern patternNode =
case Node.value patternNode of
Pattern.AllPattern ->
[]
Pattern.UnitPattern ->
[]
Pattern.CharPattern _ ->
[]
Pattern.StringPattern _ ->
[]
Pattern.IntPattern _ ->
[]
Pattern.HexPattern _ ->
[]
Pattern.FloatPattern _ ->
[]
Pattern.TuplePattern patterns ->
List.concatMap getUsedTypesFromPattern patterns
Pattern.RecordPattern _ ->
[]
Pattern.UnConsPattern pattern1 pattern2 ->
List.concatMap getUsedTypesFromPattern [ pattern1, pattern2 ]
Pattern.ListPattern patterns ->
List.concatMap getUsedTypesFromPattern patterns
Pattern.VarPattern _ ->
[]
Pattern.NamedPattern qualifiedNameRef patterns ->
let
usedVariable : List String
usedVariable =
case qualifiedNameRef.moduleName of
[] ->
[ qualifiedNameRef.name ]
moduleName ->
[]
in
usedVariable ++ List.concatMap getUsedTypesFromPattern patterns
Pattern.AsPattern pattern alias_ ->
getUsedTypesFromPattern pattern
Pattern.ParenthesizedPattern pattern ->
getUsedTypesFromPattern pattern
getUsedModulesFromPattern : Node Pattern -> List String
getUsedModulesFromPattern patternNode =
case Node.value patternNode of
Pattern.AllPattern ->
[]
Pattern.UnitPattern ->
[]
Pattern.CharPattern _ ->
[]
Pattern.StringPattern _ ->
[]
Pattern.IntPattern _ ->
[]
Pattern.HexPattern _ ->
[]
Pattern.FloatPattern _ ->
[]
Pattern.TuplePattern patterns ->
List.concatMap getUsedModulesFromPattern patterns
Pattern.RecordPattern _ ->
[]
Pattern.UnConsPattern pattern1 pattern2 ->
List.concatMap getUsedModulesFromPattern [ pattern1, pattern2 ]
Pattern.ListPattern patterns ->
List.concatMap getUsedModulesFromPattern patterns
Pattern.VarPattern _ ->
[]
Pattern.NamedPattern qualifiedNameRef patterns ->
let
usedVariable : List String
usedVariable =
case qualifiedNameRef.moduleName of
[] ->
[]
moduleName ->
[ getModuleName moduleName ]
in
usedVariable ++ List.concatMap getUsedModulesFromPattern patterns
Pattern.AsPattern pattern alias_ ->
getUsedModulesFromPattern pattern
Pattern.ParenthesizedPattern pattern ->
getUsedModulesFromPattern pattern
2019-06-24 01:32:27 +03:00
declarationVisitor : Node Declaration -> Direction -> Context -> ( List Error, Context )
declarationVisitor node direction context =
case ( direction, Node.value node ) of
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, FunctionDeclaration function ) ->
2018-11-22 21:19:19 +03:00
let
functionImplementation : FunctionImplementation
functionImplementation =
Node.value function.declaration
2018-11-22 21:19:19 +03:00
namesUsedInSignature : { types : List String, modules : List String }
2018-11-22 21:19:19 +03:00
namesUsedInSignature =
function.signature
|> Maybe.map (Node.value >> .typeAnnotation >> collectNamesFromTypeAnnotation)
|> Maybe.withDefault { types = [], modules = [] }
2018-11-22 21:19:19 +03:00
newContext : Context
2018-11-22 21:19:19 +03:00
newContext =
context
|> register (Variable <| Node.range node) (Node.range functionImplementation.name) (Node.value functionImplementation.name)
|> markUsedTypesAndModules namesUsedInSignature
2018-11-22 21:19:19 +03:00
in
( [], newContext )
( Rule.OnEnter, CustomTypeDeclaration { name, constructors } ) ->
let
variablesFromConstructorArguments : { types : List String, modules : List String }
variablesFromConstructorArguments =
constructors
|> List.concatMap (Node.value >> .arguments)
|> List.map collectNamesFromTypeAnnotation
|> foldUsedTypesAndModules
typeName : String
typeName =
Node.value name
constructorsForType : Dict String String
constructorsForType =
constructors
|> List.map (Node.value >> .name >> Node.value)
|> List.map (\constructorName -> ( constructorName, typeName ))
|> Dict.fromList
in
( []
, { context | constructorNameToTypeName = Dict.union constructorsForType context.constructorNameToTypeName }
|> register Type (Node.range name) (Node.value name)
|> markUsedTypesAndModules variablesFromConstructorArguments
)
2018-11-22 21:19:19 +03:00
( Rule.OnEnter, AliasDeclaration { name, typeAnnotation } ) ->
let
namesUsedInTypeAnnotation : { types : List String, modules : List String }
namesUsedInTypeAnnotation =
collectNamesFromTypeAnnotation typeAnnotation
in
( []
, context
|> register Type (Node.range name) (Node.value name)
|> markUsedTypesAndModules namesUsedInTypeAnnotation
)
2018-11-22 21:19:19 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, PortDeclaration { name, typeAnnotation } ) ->
let
namesUsedInTypeAnnotation : { types : List String, modules : List String }
namesUsedInTypeAnnotation =
collectNamesFromTypeAnnotation typeAnnotation
in
2018-11-26 21:09:18 +03:00
( []
, context
|> markUsedTypesAndModules namesUsedInTypeAnnotation
|> register Port (Node.range name) (Node.value name)
2018-11-26 21:09:18 +03:00
)
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, InfixDeclaration _ ) ->
( [], context )
2018-11-26 21:09:18 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnEnter, Destructuring _ _ ) ->
( [], context )
2018-11-26 21:09:18 +03:00
2019-06-26 12:42:17 +03:00
( Rule.OnExit, _ ) ->
( [], context )
2018-11-22 21:19:19 +03:00
foldUsedTypesAndModules : List { types : List String, modules : List String } -> { types : List String, modules : List String }
foldUsedTypesAndModules =
List.foldl (\a b -> { types = a.types ++ b.types, modules = a.modules ++ b.modules }) { types = [], modules = [] }
markUsedTypesAndModules : { types : List String, modules : List String } -> Context -> Context
markUsedTypesAndModules { types, modules } context =
context
|> markAllAsUsed types
|> markAllModulesAsUsed modules
finalEvaluation : Context -> List Error
finalEvaluation context =
if context.exposesEverything then
[]
else
let
rootScope : Scope
rootScope =
Nonempty.head context.scopes
namesOfCustomTypesUsedByCallingAConstructor : Set String
namesOfCustomTypesUsedByCallingAConstructor =
context.constructorNameToTypeName
|> Dict.filter (\usedName _ -> Set.member usedName rootScope.used)
|> Dict.values
|> Set.fromList
newRootScope : Scope
newRootScope =
{ rootScope | used = Set.union namesOfCustomTypesUsedByCallingAConstructor rootScope.used }
moduleErrors : List Error
moduleErrors =
context.declaredModules
|> Dict.filter (\key _ -> not <| Set.member key context.usedModules)
|> Dict.toList
|> List.map (\( key, ( variableType, range ) ) -> error variableType range key)
in
List.concat
[ newRootScope
|> makeReport
|> Tuple.first
, moduleErrors
]
2018-11-26 13:22:43 +03:00
registerFunction : Function -> Context -> Context
registerFunction function context =
2018-11-26 13:22:43 +03:00
let
declaration : FunctionImplementation
2018-11-26 13:22:43 +03:00
declaration =
Node.value function.declaration
namesUsedInSignature : { types : List String, modules : List String }
namesUsedInSignature =
case Maybe.map Node.value function.signature of
Just signature ->
collectNamesFromTypeAnnotation signature.typeAnnotation
Nothing ->
{ types = [], modules = [] }
functionRange : Range
functionRange =
case function.signature of
Just signature ->
Fix.mergeRanges
(Node.range function.declaration)
(Node.range signature)
Nothing ->
Node.range function.declaration
2018-11-26 13:22:43 +03:00
in
context
|> register (Variable functionRange) (Node.range declaration.name) (Node.value declaration.name)
|> markUsedTypesAndModules namesUsedInSignature
2018-11-26 13:22:43 +03:00
2018-11-26 21:16:04 +03:00
collectFromExposing : Exposing -> List ( VariableType, Range, String )
2018-11-26 13:22:43 +03:00
collectFromExposing exposing_ =
case exposing_ of
All _ ->
[]
Explicit list ->
List.filterMap
(\node ->
case Node.value node of
2018-11-26 13:22:43 +03:00
FunctionExpose name ->
Just ( ImportedVariable, Node.range node, name )
2018-11-26 13:22:43 +03:00
InfixExpose name ->
Just ( ImportedOperator, Node.range node, name )
2018-11-26 13:22:43 +03:00
2018-11-26 20:12:36 +03:00
TypeOrAliasExpose name ->
Just ( ImportedType, Node.range node, name )
2018-11-26 20:12:36 +03:00
TypeExpose { name, open } ->
case open of
Just openRange ->
Nothing
Nothing ->
Just ( ImportedType, Node.range node, name )
2018-11-26 13:22:43 +03:00
)
list
collectNamesFromTypeAnnotation : Node TypeAnnotation -> { types : List String, modules : List String }
2018-11-22 21:19:19 +03:00
collectNamesFromTypeAnnotation node =
{ types = collectTypesFromTypeAnnotation node
, modules = collectModuleNamesFromTypeAnnotation node
}
collectTypesFromTypeAnnotation : Node TypeAnnotation -> List String
collectTypesFromTypeAnnotation node =
case Node.value node of
FunctionTypeAnnotation a b ->
collectTypesFromTypeAnnotation a ++ collectTypesFromTypeAnnotation b
Typed nameNode params ->
let
name : List String
name =
case Node.value nameNode of
( [], str ) ->
[ str ]
( moduleName, _ ) ->
[]
in
name ++ List.concatMap collectTypesFromTypeAnnotation params
Record list ->
list
|> List.map (Node.value >> Tuple.second)
|> List.concatMap collectTypesFromTypeAnnotation
GenericRecord name list ->
list
|> Node.value
|> List.map (Node.value >> Tuple.second)
|> List.concatMap collectTypesFromTypeAnnotation
Tupled list ->
List.concatMap collectTypesFromTypeAnnotation list
GenericType _ ->
[]
Unit ->
[]
collectModuleNamesFromTypeAnnotation : Node TypeAnnotation -> List String
collectModuleNamesFromTypeAnnotation node =
case Node.value node of
2018-11-22 21:19:19 +03:00
FunctionTypeAnnotation a b ->
collectModuleNamesFromTypeAnnotation a ++ collectModuleNamesFromTypeAnnotation b
2018-11-22 21:19:19 +03:00
Typed nameNode params ->
let
name : List String
2018-11-22 21:19:19 +03:00
name =
case Node.value nameNode of
2018-11-26 21:09:18 +03:00
( [], str ) ->
[]
2018-11-26 21:09:18 +03:00
( moduleName, _ ) ->
[ getModuleName moduleName ]
2018-11-22 21:19:19 +03:00
in
name ++ List.concatMap collectModuleNamesFromTypeAnnotation params
2018-11-22 21:19:19 +03:00
Record list ->
list
|> List.map (Node.value >> Tuple.second)
|> List.concatMap collectModuleNamesFromTypeAnnotation
2018-11-22 21:19:19 +03:00
GenericRecord name list ->
list
|> Node.value
|> List.map (Node.value >> Tuple.second)
|> List.concatMap collectModuleNamesFromTypeAnnotation
2018-11-22 21:19:19 +03:00
2018-11-26 21:09:18 +03:00
Tupled list ->
List.concatMap collectModuleNamesFromTypeAnnotation list
2018-11-26 21:09:18 +03:00
GenericType _ ->
[]
Unit ->
2018-11-22 21:19:19 +03:00
[]
2018-11-26 21:16:04 +03:00
register : VariableType -> Range -> String -> Context -> Context
register variableType range name context =
if variableType == ImportedModule || variableType == ModuleAlias then
{ context | declaredModules = Dict.insert name ( variableType, range ) context.declaredModules }
else
let
scopes : Nonempty Scope
scopes =
mapNonemptyHead
(\scope ->
{ scope | declared = Dict.insert name ( variableType, range ) scope.declared }
)
context.scopes
in
{ context | scopes = scopes }
2018-11-22 21:19:19 +03:00
2018-11-26 13:22:43 +03:00
markAllAsUsed : List String -> Context -> Context
markAllAsUsed names context =
List.foldl markAsUsed context names
2018-11-26 13:22:43 +03:00
2018-11-22 21:19:19 +03:00
markAsUsed : String -> Context -> Context
markAsUsed name context =
2018-11-22 21:19:19 +03:00
let
scopes : Nonempty Scope
2018-11-22 21:19:19 +03:00
scopes =
mapNonemptyHead
(\scope ->
{ scope | used = Set.insert name scope.used }
)
context.scopes
2018-11-22 21:19:19 +03:00
in
{ context | scopes = scopes }
2018-11-22 21:19:19 +03:00
markAllModulesAsUsed : List String -> Context -> Context
markAllModulesAsUsed names context =
{ context | usedModules = Set.union (Set.fromList names) context.usedModules }
markModuleAsUsed : String -> Context -> Context
markModuleAsUsed name context =
{ context | usedModules = Set.insert name context.usedModules }
2018-11-26 13:22:43 +03:00
getModuleName : List String -> String
getModuleName name =
String.join "." name
2018-11-22 21:19:19 +03:00
makeReport : Scope -> ( List Error, List String )
makeReport { declared, used } =
let
2019-06-24 01:32:27 +03:00
nonUsedVars : List String
2018-11-22 21:19:19 +03:00
nonUsedVars =
Set.diff used (Set.fromList <| Dict.keys declared)
|> Set.toList
2019-06-24 01:32:27 +03:00
errors : List Error
2018-11-22 21:19:19 +03:00
errors =
Dict.filter (\key _ -> not <| Set.member key used) declared
|> Dict.toList
2018-11-26 20:39:46 +03:00
|> List.map (\( key, ( variableType, range ) ) -> error variableType range key)
2018-11-22 21:19:19 +03:00
in
( errors, nonUsedVars )
mapNonemptyHead : (a -> a) -> Nonempty a -> Nonempty a
mapNonemptyHead fn nonempty =
let
newHead : a
2018-11-22 21:19:19 +03:00
newHead =
fn (Nonempty.head nonempty)
in
Nonempty.replaceHead newHead nonempty