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
|
|
|
## Fail
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-07-03 15:19:29 +03:00
|
|
|
-- module A exposing (a)
|
2018-11-22 21:19:19 +03:00
|
|
|
a n =
|
|
|
|
n + 1
|
|
|
|
|
|
|
|
b =
|
|
|
|
a 2
|
|
|
|
|
|
|
|
|
2019-07-03 15:19:29 +03:00
|
|
|
## Success
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-07-03 15:19:29 +03:00
|
|
|
-- module A exposing (a)
|
2018-11-22 21:19:19 +03:00
|
|
|
a n =
|
|
|
|
n + 1
|
|
|
|
|
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(..))
|
2019-07-02 12:55:49 +03:00
|
|
|
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(..))
|
2019-06-24 13:32:07 +03:00
|
|
|
import Elm.Syntax.Node as Node exposing (Node)
|
2018-11-22 21:19:19 +03:00
|
|
|
import Elm.Syntax.Range exposing (Range)
|
|
|
|
import Elm.Syntax.TypeAnnotation exposing (TypeAnnotation(..))
|
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
|
|
|
|
2019-06-26 01:34:57 +03:00
|
|
|
config =
|
|
|
|
[ ( Critical, NoUnusedVariables.rule )
|
2018-11-26 13:22:43 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
-}
|
|
|
|
rule : Rule
|
2019-06-15 22:14:40 +03:00
|
|
|
rule =
|
2019-06-24 23:35:30 +03:00
|
|
|
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
|
2019-07-05 01:33:51 +03:00
|
|
|
, constructorNameToTypeName : Dict String 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
|
2018-11-26 20:39:46 +03:00
|
|
|
= Variable
|
|
|
|
| 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
|
2019-07-05 01:33:51 +03:00
|
|
|
, constructorNameToTypeName = Dict.empty
|
2018-11-22 21:19:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emptyScope : Scope
|
|
|
|
emptyScope =
|
|
|
|
Scope Dict.empty Set.empty
|
|
|
|
|
|
|
|
|
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 =
|
2019-06-26 14:47:00 +03:00
|
|
|
Rule.error
|
2018-11-26 21:09:18 +03:00
|
|
|
(variableTypeToString variableType ++ " `" ++ name ++ "` is not used" ++ variableTypeWarning variableType)
|
2018-11-26 20:39:46 +03:00
|
|
|
range_
|
|
|
|
|
|
|
|
|
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 ->
|
|
|
|
"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 ->
|
|
|
|
""
|
|
|
|
|
|
|
|
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 moduleNode context =
|
2019-06-24 13:32:07 +03:00
|
|
|
case Module.exposingList (Node.value moduleNode) of
|
2018-11-22 21:19:19 +03:00
|
|
|
All _ ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], { context | exposesEverything = True } )
|
2018-11-22 21:19:19 +03:00
|
|
|
|
|
|
|
Explicit list ->
|
|
|
|
let
|
|
|
|
names =
|
|
|
|
List.filterMap
|
|
|
|
(\node ->
|
2019-06-24 13:32:07 +03:00
|
|
|
case Node.value 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
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], 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 context =
|
2018-11-22 21:19:19 +03:00
|
|
|
let
|
2019-07-05 01:33:51 +03:00
|
|
|
exposed : Maybe Exposing
|
2018-11-26 13:22:43 +03:00
|
|
|
exposed =
|
|
|
|
node
|
2019-06-24 13:32:07 +03:00
|
|
|
|> Node.value
|
2018-11-22 21:19:19 +03:00
|
|
|
|> .exposingList
|
2019-07-05 01:33:51 +03:00
|
|
|
|> Maybe.map Node.value
|
2018-11-22 21:19:19 +03:00
|
|
|
in
|
2019-07-05 01:33:51 +03:00
|
|
|
case exposed of
|
2018-11-26 20:12:36 +03:00
|
|
|
Nothing ->
|
|
|
|
let
|
2018-11-26 20:39:46 +03:00
|
|
|
( variableType, moduleName ) =
|
2019-06-24 13:32:07 +03:00
|
|
|
case Node.value node |> .moduleAlias of
|
2018-11-26 20:39:46 +03:00
|
|
|
Just moduleAlias ->
|
|
|
|
( ModuleAlias, moduleAlias )
|
|
|
|
|
|
|
|
Nothing ->
|
2019-06-24 13:32:07 +03:00
|
|
|
( ImportedModule, Node.value node |> .moduleName )
|
2018-11-26 20:12:36 +03:00
|
|
|
in
|
|
|
|
( []
|
|
|
|
, register
|
2018-11-26 20:39:46 +03:00
|
|
|
variableType
|
2019-06-24 13:32:07 +03:00
|
|
|
(Node.range moduleName)
|
|
|
|
(Node.value moduleName |> getModuleName)
|
2019-06-16 22:12:37 +03:00
|
|
|
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 ->
|
|
|
|
( []
|
|
|
|
, List.foldl
|
2019-06-16 22:12:37 +03:00
|
|
|
(\( variableType, range, name ) context_ -> register variableType range name context_)
|
|
|
|
context
|
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 =
|
2019-06-24 13:32:07 +03:00
|
|
|
case ( direction, Node.value node ) of
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnEnter, FunctionOrValue [] name ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], markAsUsed name context )
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnEnter, FunctionOrValue moduleName name ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], markAsUsed (getModuleName moduleName) context )
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnEnter, OperatorApplication name _ _ _ ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], markAsUsed name context )
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnEnter, PrefixOperator name ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], 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
|
2019-07-02 12:55:49 +03:00
|
|
|
newContext : Context
|
2018-11-26 13:22:43 +03:00
|
|
|
newContext =
|
|
|
|
List.foldl
|
2019-06-16 22:12:37 +03:00
|
|
|
(\declaration context_ ->
|
2019-06-24 13:32:07 +03:00
|
|
|
case Node.value declaration of
|
2018-11-26 13:22:43 +03:00
|
|
|
LetFunction function ->
|
2019-06-16 22:12:37 +03:00
|
|
|
registerFunction function context_
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2018-11-26 13:22:43 +03:00
|
|
|
LetDestructuring pattern _ ->
|
2019-06-16 22:12:37 +03:00
|
|
|
context_
|
2018-11-26 13:22:43 +03:00
|
|
|
)
|
2019-06-16 22:12:37 +03:00
|
|
|
{ context | scopes = Nonempty.cons emptyScope context.scopes }
|
2018-11-26 13:22:43 +03:00
|
|
|
declarations
|
|
|
|
in
|
|
|
|
( [], newContext )
|
|
|
|
|
2019-07-02 12:16:03 +03:00
|
|
|
( Rule.OnExit, RecordUpdateExpression expr _ ) ->
|
|
|
|
( [], markAsUsed (Node.value expr) context )
|
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnExit, LetExpression _ ) ->
|
2018-11-26 13:22:43 +03:00
|
|
|
let
|
|
|
|
( errors, remainingUsed ) =
|
2019-06-16 22:12:37 +03:00
|
|
|
makeReport (Nonempty.head context.scopes)
|
2018-11-26 13:22:43 +03:00
|
|
|
|
2019-06-16 22:12:37 +03:00
|
|
|
contextWithPoppedScope =
|
|
|
|
{ context | scopes = Nonempty.pop context.scopes }
|
2018-11-26 13:22:43 +03:00
|
|
|
in
|
|
|
|
( errors
|
2019-06-16 22:12:37 +03:00
|
|
|
, markAllAsUsed remainingUsed contextWithPoppedScope
|
2018-11-26 13:22:43 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
_ ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], context )
|
2018-11-22 21:19:19 +03:00
|
|
|
|
|
|
|
|
2019-06-24 01:32:27 +03:00
|
|
|
declarationVisitor : Node Declaration -> Direction -> Context -> ( List Error, Context )
|
|
|
|
declarationVisitor node direction context =
|
2019-06-24 13:32:07 +03:00
|
|
|
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
|
2019-07-05 01:33:51 +03:00
|
|
|
functionImplementation : FunctionImplementation
|
|
|
|
functionImplementation =
|
2019-06-24 13:32:07 +03:00
|
|
|
Node.value function.declaration
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-07-05 01:33:51 +03:00
|
|
|
namesUsedInSignature : List String
|
2018-11-22 21:19:19 +03:00
|
|
|
namesUsedInSignature =
|
|
|
|
function.signature
|
2019-06-24 13:32:07 +03:00
|
|
|
|> Maybe.map (Node.value >> .typeAnnotation >> collectNamesFromTypeAnnotation)
|
2018-11-22 21:19:19 +03:00
|
|
|
|> Maybe.withDefault []
|
|
|
|
|
2019-07-05 01:33:51 +03:00
|
|
|
newContext : Context
|
2018-11-22 21:19:19 +03:00
|
|
|
newContext =
|
2019-06-16 22:12:37 +03:00
|
|
|
context
|
2019-07-05 01:33:51 +03:00
|
|
|
|> register Variable (Node.range functionImplementation.name) (Node.value functionImplementation.name)
|
2018-11-22 21:19:19 +03:00
|
|
|
|> markAllAsUsed namesUsedInSignature
|
|
|
|
in
|
|
|
|
( [], newContext )
|
|
|
|
|
2019-07-02 12:42:07 +03:00
|
|
|
( Rule.OnEnter, CustomTypeDeclaration { name, constructors } ) ->
|
|
|
|
let
|
|
|
|
variablesFromConstructorArguments : List String
|
|
|
|
variablesFromConstructorArguments =
|
|
|
|
constructors
|
|
|
|
|> List.concatMap (Node.value >> .arguments)
|
|
|
|
|> List.concatMap collectNamesFromTypeAnnotation
|
2019-07-05 01:33:51 +03:00
|
|
|
|
|
|
|
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
|
2019-07-02 12:42:07 +03:00
|
|
|
in
|
|
|
|
( []
|
2019-07-05 01:33:51 +03:00
|
|
|
, { context | constructorNameToTypeName = Dict.union constructorsForType context.constructorNameToTypeName }
|
2019-07-02 12:42:07 +03:00
|
|
|
|> register Type (Node.range name) (Node.value name)
|
|
|
|
|> markAllAsUsed variablesFromConstructorArguments
|
|
|
|
)
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-07-02 12:42:07 +03:00
|
|
|
( Rule.OnEnter, AliasDeclaration { name, typeAnnotation } ) ->
|
|
|
|
( []
|
|
|
|
, context
|
|
|
|
|> register Type (Node.range name) (Node.value name)
|
|
|
|
|> markAllAsUsed (collectNamesFromTypeAnnotation typeAnnotation)
|
|
|
|
)
|
2018-11-22 21:19:19 +03:00
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnEnter, PortDeclaration { name, typeAnnotation } ) ->
|
2018-11-26 21:09:18 +03:00
|
|
|
( []
|
2019-06-16 22:12:37 +03:00
|
|
|
, context
|
2018-11-26 21:09:18 +03:00
|
|
|
|> markAllAsUsed (collectNamesFromTypeAnnotation typeAnnotation)
|
2019-06-24 13:32:07 +03:00
|
|
|
|> 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 _ ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], context )
|
2018-11-26 21:09:18 +03:00
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnEnter, Destructuring _ _ ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], context )
|
2018-11-26 21:09:18 +03:00
|
|
|
|
2019-06-26 12:42:17 +03:00
|
|
|
( Rule.OnExit, _ ) ->
|
2019-06-16 22:12:37 +03:00
|
|
|
( [], context )
|
2018-11-22 21:19:19 +03:00
|
|
|
|
|
|
|
|
2019-06-15 22:13:40 +03:00
|
|
|
finalEvaluation : Context -> List Error
|
2019-06-16 22:12:37 +03:00
|
|
|
finalEvaluation context =
|
|
|
|
if context.exposesEverything then
|
2019-06-15 22:13:40 +03:00
|
|
|
[]
|
|
|
|
|
|
|
|
else
|
2019-07-05 01:33:51 +03:00
|
|
|
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 }
|
|
|
|
in
|
|
|
|
newRootScope
|
2019-06-15 22:13:40 +03:00
|
|
|
|> makeReport
|
|
|
|
|> Tuple.first
|
2018-11-26 13:22:43 +03:00
|
|
|
|
|
|
|
|
|
|
|
registerFunction : Function -> Context -> Context
|
2019-06-16 22:12:37 +03:00
|
|
|
registerFunction function context =
|
2018-11-26 13:22:43 +03:00
|
|
|
let
|
2019-07-02 12:55:49 +03:00
|
|
|
declaration : FunctionImplementation
|
2018-11-26 13:22:43 +03:00
|
|
|
declaration =
|
2019-06-24 13:32:07 +03:00
|
|
|
Node.value function.declaration
|
2019-07-02 12:55:49 +03:00
|
|
|
|
|
|
|
namesUsedInSignature : List String
|
|
|
|
namesUsedInSignature =
|
|
|
|
case Maybe.map Node.value function.signature of
|
|
|
|
Just signature ->
|
|
|
|
collectNamesFromTypeAnnotation signature.typeAnnotation
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
[]
|
2018-11-26 13:22:43 +03:00
|
|
|
in
|
2019-07-02 12:55:49 +03:00
|
|
|
context
|
|
|
|
|> register Variable (Node.range declaration.name) (Node.value declaration.name)
|
|
|
|
|> markAllAsUsed 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 ->
|
2019-06-24 13:32:07 +03:00
|
|
|
case Node.value node of
|
2018-11-26 13:22:43 +03:00
|
|
|
FunctionExpose name ->
|
2019-06-24 13:32:07 +03:00
|
|
|
Just ( ImportedVariable, Node.range node, name )
|
2018-11-26 13:22:43 +03:00
|
|
|
|
|
|
|
InfixExpose name ->
|
2019-06-24 13:32:07 +03:00
|
|
|
Just ( ImportedOperator, Node.range node, name )
|
2018-11-26 13:22:43 +03:00
|
|
|
|
2018-11-26 20:12:36 +03:00
|
|
|
TypeOrAliasExpose name ->
|
2019-06-24 13:32:07 +03:00
|
|
|
Just ( ImportedType, Node.range node, name )
|
2018-11-26 20:12:36 +03:00
|
|
|
|
|
|
|
TypeExpose { name, open } ->
|
|
|
|
case open of
|
|
|
|
Just openRange ->
|
|
|
|
Nothing
|
|
|
|
|
|
|
|
Nothing ->
|
2019-06-24 13:32:07 +03:00
|
|
|
Just ( ImportedType, Node.range node, name )
|
2018-11-26 13:22:43 +03:00
|
|
|
)
|
|
|
|
list
|
|
|
|
|
|
|
|
|
2018-11-22 21:19:19 +03:00
|
|
|
collectNamesFromTypeAnnotation : Node TypeAnnotation -> List String
|
|
|
|
collectNamesFromTypeAnnotation node =
|
2019-06-24 13:32:07 +03:00
|
|
|
case Node.value node of
|
2018-11-22 21:19:19 +03:00
|
|
|
FunctionTypeAnnotation a b ->
|
|
|
|
collectNamesFromTypeAnnotation a ++ collectNamesFromTypeAnnotation b
|
|
|
|
|
|
|
|
Typed nameNode params ->
|
|
|
|
let
|
2019-07-05 01:33:51 +03:00
|
|
|
name : String
|
2018-11-22 21:19:19 +03:00
|
|
|
name =
|
2019-06-24 13:32:07 +03:00
|
|
|
case Node.value nameNode of
|
2018-11-26 21:09:18 +03:00
|
|
|
( [], str ) ->
|
|
|
|
str
|
|
|
|
|
|
|
|
( moduleName, _ ) ->
|
|
|
|
getModuleName moduleName
|
2018-11-22 21:19:19 +03:00
|
|
|
in
|
|
|
|
name :: List.concatMap collectNamesFromTypeAnnotation params
|
|
|
|
|
|
|
|
Record list ->
|
|
|
|
list
|
2019-06-24 13:32:07 +03:00
|
|
|
|> List.map (Node.value >> Tuple.second)
|
2018-11-22 21:19:19 +03:00
|
|
|
|> List.concatMap collectNamesFromTypeAnnotation
|
|
|
|
|
|
|
|
GenericRecord name list ->
|
|
|
|
list
|
2019-06-24 13:32:07 +03:00
|
|
|
|> Node.value
|
|
|
|
|> List.map (Node.value >> Tuple.second)
|
2018-11-22 21:19:19 +03:00
|
|
|
|> List.concatMap collectNamesFromTypeAnnotation
|
|
|
|
|
2018-11-26 21:09:18 +03:00
|
|
|
Tupled list ->
|
|
|
|
List.concatMap collectNamesFromTypeAnnotation list
|
|
|
|
|
|
|
|
GenericType _ ->
|
|
|
|
[]
|
|
|
|
|
|
|
|
Unit ->
|
2018-11-22 21:19:19 +03:00
|
|
|
[]
|
|
|
|
|
|
|
|
|
2018-11-26 21:16:04 +03:00
|
|
|
register : VariableType -> Range -> String -> Context -> Context
|
2019-06-16 22:12:37 +03:00
|
|
|
register variableType range name context =
|
2018-11-22 21:19:19 +03:00
|
|
|
let
|
2019-07-05 01:33:51 +03:00
|
|
|
scopes : Nonempty Scope
|
2018-11-22 21:19:19 +03:00
|
|
|
scopes =
|
|
|
|
mapNonemptyHead
|
|
|
|
(\scope ->
|
2018-11-26 20:39:46 +03:00
|
|
|
{ scope | declared = Dict.insert name ( variableType, range ) scope.declared }
|
2018-11-22 21:19:19 +03:00
|
|
|
)
|
2019-06-16 22:12:37 +03:00
|
|
|
context.scopes
|
2018-11-22 21:19:19 +03:00
|
|
|
in
|
2019-06-16 22:12:37 +03:00
|
|
|
{ context | scopes = scopes }
|
2018-11-22 21:19:19 +03:00
|
|
|
|
|
|
|
|
2018-11-26 13:22:43 +03:00
|
|
|
markAllAsUsed : List String -> Context -> Context
|
2019-06-16 22:12:37 +03:00
|
|
|
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
|
2019-06-16 22:12:37 +03:00
|
|
|
markAsUsed name context =
|
2018-11-22 21:19:19 +03:00
|
|
|
let
|
2019-07-05 01:33:51 +03:00
|
|
|
scopes : Nonempty Scope
|
2018-11-22 21:19:19 +03:00
|
|
|
scopes =
|
|
|
|
mapNonemptyHead
|
|
|
|
(\scope ->
|
|
|
|
{ scope | used = Set.insert name scope.used }
|
|
|
|
)
|
2019-06-16 22:12:37 +03:00
|
|
|
context.scopes
|
2018-11-22 21:19:19 +03:00
|
|
|
in
|
2019-06-16 22:12:37 +03:00
|
|
|
{ context | scopes = scopes }
|
2018-11-22 21:19:19 +03:00
|
|
|
|
|
|
|
|
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
|
2019-07-05 01:33:51 +03:00
|
|
|
newHead : a
|
2018-11-22 21:19:19 +03:00
|
|
|
newHead =
|
|
|
|
fn (Nonempty.head nonempty)
|
|
|
|
in
|
|
|
|
Nonempty.replaceHead newHead nonempty
|