NoUnused.CustomTypeConstructors: Transform into a project rule

This commit is contained in:
Jeroen Engels 2020-03-12 16:55:15 +01:00
parent b2fd92f529
commit ce1c059d4c
2 changed files with 92 additions and 37 deletions

View File

@ -14,6 +14,7 @@ import Elm.Syntax.Declaration as Declaration exposing (Declaration)
import Elm.Syntax.Exposing as Exposing exposing (Exposing, TopLevelExpose)
import Elm.Syntax.Expression as Expression exposing (Expression)
import Elm.Syntax.Module as Module exposing (Module)
import Elm.Syntax.ModuleName exposing (ModuleName)
import Elm.Syntax.Node as Node exposing (Node)
import Elm.Syntax.Signature as Signature exposing (Signature)
import Elm.Syntax.TypeAnnotation as TypeAnnotation exposing (TypeAnnotation)
@ -73,16 +74,25 @@ in your editor, rather than when running your tests or [elm-xref](https://github
-}
rule : Rule
rule =
Rule.newModuleRuleSchema "NoUnused.CustomTypeConstructors" initialContext
|> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor
|> Rule.withDeclarationListVisitor declarationListVisitor
|> Rule.withDeclarationVisitor declarationVisitor
|> Rule.withExpressionVisitor expressionVisitor
|> Rule.withFinalModuleEvaluation finalEvaluation
|> Rule.fromModuleRuleSchema
Rule.newProjectRuleSchema "NoUnused.CustomTypeConstructors"
{ moduleVisitor = moduleVisitor
, initProjectContext = initProjectContext
, fromProjectToModule = fromProjectToModule
, fromModuleToProject = fromModuleToProject
, foldProjectContexts = foldProjectContexts
}
|> Rule.fromProjectRuleSchema
type alias Context =
-- CONTEXT
type alias ProjectContext =
{}
type alias ModuleContext =
{ exposedCustomTypesWithConstructors : Set String
, exposesEverything : Bool
, declaredTypesWithConstructors : Dict String (Node String)
@ -91,8 +101,13 @@ type alias Context =
}
initialContext : Context
initialContext =
initProjectContext : ProjectContext
initProjectContext =
{}
fromProjectToModule : Rule.ModuleKey -> Node ModuleName -> ProjectContext -> ModuleContext
fromProjectToModule _ _ projectContext =
{ exposedCustomTypesWithConstructors = Set.empty
, exposesEverything = False
, declaredTypesWithConstructors = Dict.empty
@ -101,6 +116,16 @@ initialContext =
}
fromModuleToProject : Rule.ModuleKey -> Node ModuleName -> ModuleContext -> ProjectContext
fromModuleToProject _ _ moduleContext =
{}
foldProjectContexts : ProjectContext -> ProjectContext -> ProjectContext
foldProjectContexts _ previousContext =
previousContext
error : Node String -> Error
error node =
Rule.error
@ -115,10 +140,25 @@ error node =
-- MODULE VISITOR
moduleVisitor : Rule.ModuleRuleSchema {} ModuleContext -> Rule.ModuleRuleSchema { hasAtLeastOneVisitor : () } ModuleContext
moduleVisitor schema =
schema
|> Rule.withModuleDefinitionVisitor (\_ context -> ( [], context ))
|> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor
|> Rule.withDeclarationListVisitor declarationListVisitor
|> Rule.withDeclarationVisitor declarationVisitor
|> Rule.withExpressionVisitor expressionVisitor
|> Rule.withFinalModuleEvaluation finalEvaluation
-- MODULE DEFINITION VISITOR
moduleDefinitionVisitor : Node Module -> Context -> ( List nothing, Context )
moduleDefinitionVisitor : Node Module -> ModuleContext -> ( List nothing, ModuleContext )
moduleDefinitionVisitor moduleNode context =
case Module.exposingList (Node.value moduleNode) of
Exposing.All _ ->
@ -156,12 +196,12 @@ moduleDefinitionVisitor moduleNode context =
-- DECLARATION LIST VISITOR
declarationListVisitor : List (Node Declaration) -> Context -> ( List nothing, Context )
declarationListVisitor : List (Node Declaration) -> ModuleContext -> ( List nothing, ModuleContext )
declarationListVisitor nodes context =
( [], List.foldl register context nodes )
register : Node Declaration -> Context -> Context
register : Node Declaration -> ModuleContext -> ModuleContext
register node context =
case Node.value node of
Declaration.CustomTypeDeclaration { name, generics, constructors } ->
@ -191,7 +231,7 @@ register node context =
-- DECLARATION VISITOR
declarationVisitor : Node Declaration -> Direction -> Context -> ( List nothing, Context )
declarationVisitor : Node Declaration -> Direction -> ModuleContext -> ( List nothing, ModuleContext )
declarationVisitor node direction context =
case ( direction, Node.value node ) of
( Rule.OnEnter, Declaration.CustomTypeDeclaration { name, constructors } ) ->
@ -200,7 +240,7 @@ declarationVisitor node direction context =
else
let
newContext : Context
newContext : ModuleContext
newContext =
List.foldl
(\constructor ctx ->
@ -233,7 +273,7 @@ declarationVisitor node direction context =
-- EXPRESSION VISITOR
expressionVisitor : Node Expression -> Direction -> Context -> ( List nothing, Context )
expressionVisitor : Node Expression -> Direction -> ModuleContext -> ( List nothing, ModuleContext )
expressionVisitor node direction context =
if context.exposesEverything then
( [], context )
@ -266,7 +306,7 @@ expressionVisitor node direction context =
-- FINAL EVALUATION
finalEvaluation : Context -> List Error
finalEvaluation : ModuleContext -> List Error
finalEvaluation context =
if context.exposesEverything then
[]
@ -282,7 +322,7 @@ finalEvaluation context =
-- TYPE ANNOTATION UTILITARY FUNCTIONS
markPhantomTypesFromTypeSignatureAsUsed : Maybe (Node Signature) -> Context -> Context
markPhantomTypesFromTypeSignatureAsUsed : Maybe (Node Signature) -> ModuleContext -> ModuleContext
markPhantomTypesFromTypeSignatureAsUsed maybeSignature context =
let
used : List String

View File

@ -103,7 +103,8 @@ exposingTypeConstructors =
describe "Exposed constructors"
[ test "should not report unused type constructors when package module is exposing all and module is exposed" <|
\() ->
"""module MyModule exposing (..)
"""
module MyModule exposing (..)
type Foo = Bar | Baz
"""
|> Review.Test.runWithProjectData packageProject rule
@ -116,13 +117,15 @@ unusedTests typeOfProject project =
describe ("Unused variables for " ++ typeOfProject)
[ test "should not report non-exposed variables" <|
\() ->
"""module MyModule exposing (b)
"""
module MyModule exposing (b)
a = 1"""
|> Review.Test.runWithProjectData project rule
|> Review.Test.expectNoErrors
, test "should not report used type constructors" <|
\() ->
"""module MyModule exposing (b)
"""
module MyModule exposing (b)
type Foo = Bar | Baz
a = Bar
b = Baz"""
@ -130,21 +133,24 @@ b = Baz"""
|> Review.Test.expectNoErrors
, test "should not report unused type constructors when module is exposing all" <|
\() ->
"""module MyModule exposing (..)
"""
module MyModule exposing (..)
type Foo = Bar | Baz
"""
|> Review.Test.runWithProjectData project rule
|> Review.Test.expectNoErrors
, test "should not report unused type constructors when module is exposing the constructors of that type" <|
\() ->
"""module MyModule exposing (Foo(..))
"""
module MyModule exposing (Foo(..))
type Foo = Bar | Baz
"""
|> Review.Test.runWithProjectData project rule
|> Review.Test.expectNoErrors
, test "should report unused type constructors" <|
\() ->
"""module MyModule exposing (b)
"""
module MyModule exposing (b)
type Foo = Bar | Baz"""
|> Review.Test.runWithProjectData project rule
|> Review.Test.expectErrors
@ -161,7 +167,8 @@ type Foo = Bar | Baz"""
]
, test "should report unused type constructors, even if the type is exposed" <|
\() ->
"""module MyModule exposing (Foo)
"""
module MyModule exposing (Foo)
type Foo = Bar | Baz"""
|> Review.Test.runWithProjectData project rule
|> Review.Test.expectErrors
@ -184,7 +191,8 @@ phantomTypeTests typeOfProject project =
describe ("Phantom type for " ++ typeOfProject)
[ test "should not report a custom type with one constructor, when it is used in the stead of a phantom variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User = User
type Id a = Id
@ -195,7 +203,8 @@ id = Id
|> Review.Test.expectNoErrors
, test "should not report a custom type with one constructor, when it is used in the stead of a phantom variable in a let variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User = User
type Id a = Id
@ -211,7 +220,8 @@ id =
|> Review.Test.expectNoErrors
, test "should report a custom type with multiple constructors, when it is used in the stead of a phantom variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type Something = A | B
type Id a = Id
@ -233,7 +243,8 @@ id = Id
]
, test "should report a custom type with one constructor, when there is a phantom type available but it isn't used" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User = User
type Id a = Id
id = Id
@ -245,11 +256,12 @@ id = Id
, details = details
, under = "User"
}
|> Review.Test.atExactly { start = { row = 2, column = 13 }, end = { row = 2, column = 17 } }
|> Review.Test.atExactly { start = { row = 3, column = 13 }, end = { row = 3, column = 17 } }
]
, test "should report a custom type with one constructor when the constructor is named differently than the type, even when it is used in the stead of a phantom variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User = UserConstructor
type Id a = Id
@ -266,7 +278,8 @@ id = Id
]
, test "should report a custom type with one constructor, when it is used in the stead of a non-phantom variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User = User
type Id a = Id a
@ -280,11 +293,12 @@ id = Id
, details = details
, under = "User"
}
|> Review.Test.atExactly { start = { row = 2, column = 13 }, end = { row = 2, column = 17 } }
|> Review.Test.atExactly { start = { row = 3, column = 13 }, end = { row = 3, column = 17 } }
]
, test "should report a custom type with a type variable, when it is used in the stead of a phantom variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User something = User
type Id a = Id a
@ -298,11 +312,12 @@ id = Id
, details = details
, under = "User"
}
|> Review.Test.atExactly { start = { row = 2, column = 23 }, end = { row = 2, column = 27 } }
|> Review.Test.atExactly { start = { row = 3, column = 23 }, end = { row = 3, column = 27 } }
]
, test "should report a custom type with one constructor that has arguments, when it is used in the stead of a phantom variable" <|
\() ->
"""module MyModule exposing (id)
"""
module MyModule exposing (id)
type User = User Something
type Id a = Id a
@ -316,6 +331,6 @@ id = Id
, details = details
, under = "User"
}
|> Review.Test.atExactly { start = { row = 2, column = 13 }, end = { row = 2, column = 17 } }
|> Review.Test.atExactly { start = { row = 3, column = 13 }, end = { row = 3, column = 17 } }
]
]