2020-01-12 20:01:49 +03:00
|
|
|
module Scope2Test exposing (all)
|
|
|
|
|
|
|
|
import Dependencies
|
2020-01-13 18:29:20 +03:00
|
|
|
import Elm.Syntax.Declaration as Declaration exposing (Declaration)
|
2020-01-12 20:01:49 +03:00
|
|
|
import Elm.Syntax.Expression as Expression exposing (Expression)
|
2020-01-13 18:29:20 +03:00
|
|
|
import Elm.Syntax.Node as Node exposing (Node(..))
|
|
|
|
import Elm.Syntax.TypeAnnotation as TypeAnnotation exposing (TypeAnnotation)
|
2020-01-12 20:01:49 +03:00
|
|
|
import Review.Project as Project exposing (Project)
|
|
|
|
import Review.Rule as Rule exposing (Rule)
|
2020-01-15 11:54:26 +03:00
|
|
|
import Review.Test
|
2020-01-13 01:43:21 +03:00
|
|
|
import Scope2 as Scope
|
2020-01-12 20:01:49 +03:00
|
|
|
import Test exposing (Test, test)
|
|
|
|
|
|
|
|
|
|
|
|
all : Test
|
|
|
|
all =
|
2020-01-13 18:46:24 +03:00
|
|
|
Test.describe "Scope"
|
|
|
|
[ realFunctionOrTypeTests
|
|
|
|
]
|
2020-01-12 20:01:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
realFunctionOrTypeTests : Test
|
|
|
|
realFunctionOrTypeTests =
|
|
|
|
Test.describe "Scope.realFunctionOrType"
|
2020-01-13 18:29:20 +03:00
|
|
|
[ test "should indicate that module from which a function or value comes from, with knowledge of what is in other modules" <|
|
2020-01-13 01:43:21 +03:00
|
|
|
\() ->
|
|
|
|
[ """module A exposing (..)
|
|
|
|
import Bar as Baz exposing (baz)
|
2020-01-13 13:03:52 +03:00
|
|
|
import ExposesSomeThings exposing (..)
|
|
|
|
import ExposesEverything exposing (..)
|
2020-01-13 01:43:21 +03:00
|
|
|
import Foo.Bar
|
|
|
|
import Html exposing (..)
|
|
|
|
import Http exposing (get)
|
|
|
|
|
2020-01-13 18:29:20 +03:00
|
|
|
localValue = 1
|
|
|
|
|
|
|
|
a : SomeCustomType -> SomeTypeAlias -> SomeOtherTypeAlias -> NonExposedCustomType
|
|
|
|
a = localValue
|
|
|
|
unknownValue
|
2020-01-13 13:03:52 +03:00
|
|
|
exposedElement
|
|
|
|
nonExposedElement
|
|
|
|
elementFromExposesEverything
|
2020-01-13 18:46:24 +03:00
|
|
|
VariantA
|
2020-01-13 01:43:21 +03:00
|
|
|
Foo.bar
|
|
|
|
Foo.Bar
|
|
|
|
Baz.foo
|
|
|
|
baz
|
|
|
|
button
|
|
|
|
Http.get
|
|
|
|
get
|
|
|
|
always
|
2020-01-13 18:46:24 +03:00
|
|
|
True
|
2020-01-13 01:43:21 +03:00
|
|
|
Just
|
2020-01-13 18:29:20 +03:00
|
|
|
""", """module ExposesSomeThings exposing (SomeOtherTypeAlias, exposedElement)
|
|
|
|
type NonExposedCustomType = Variant
|
|
|
|
type alias SomeOtherTypeAlias = {}
|
2020-01-13 13:03:52 +03:00
|
|
|
exposedElement = 1
|
|
|
|
nonExposedElement = 2
|
|
|
|
""", """module ExposesEverything exposing (..)
|
2020-01-13 18:29:20 +03:00
|
|
|
type SomeCustomType = VariantA | VariantB
|
|
|
|
type alias SomeTypeAlias = {}
|
2020-01-13 13:03:52 +03:00
|
|
|
elementFromExposesEverything = 1
|
2020-01-13 01:43:21 +03:00
|
|
|
""" ]
|
|
|
|
|> Review.Test.runOnModulesWithProjectData project rule
|
|
|
|
|> Review.Test.expectErrorsForModules
|
|
|
|
[ ( "A"
|
|
|
|
, [ Review.Test.error
|
|
|
|
{ message = """
|
2020-01-13 18:29:20 +03:00
|
|
|
<nothing>.SomeCustomType -> ExposesEverything.SomeCustomType
|
|
|
|
<nothing>.SomeTypeAlias -> ExposesEverything.SomeTypeAlias
|
|
|
|
<nothing>.SomeOtherTypeAlias -> ExposesSomeThings.SomeOtherTypeAlias
|
|
|
|
<nothing>.NonExposedCustomType -> <nothing>.NonExposedCustomType
|
|
|
|
<nothing>.localValue -> <nothing>.localValue
|
|
|
|
<nothing>.unknownValue -> <nothing>.unknownValue
|
2020-01-13 13:03:52 +03:00
|
|
|
<nothing>.exposedElement -> ExposesSomeThings.exposedElement
|
|
|
|
<nothing>.nonExposedElement -> <nothing>.nonExposedElement
|
|
|
|
<nothing>.elementFromExposesEverything -> ExposesEverything.elementFromExposesEverything
|
2020-01-13 18:46:24 +03:00
|
|
|
<nothing>.VariantA -> ExposesEverything.VariantA
|
2020-01-13 01:43:21 +03:00
|
|
|
Foo.bar -> Foo.bar
|
|
|
|
Foo.Bar -> Foo.Bar
|
|
|
|
Baz.foo -> Bar.foo
|
|
|
|
<nothing>.baz -> Bar.baz
|
|
|
|
<nothing>.button -> Html.button
|
|
|
|
Http.get -> Http.get
|
|
|
|
<nothing>.get -> Http.get
|
|
|
|
<nothing>.always -> Basics.always
|
2020-01-13 18:46:24 +03:00
|
|
|
<nothing>.True -> Basics.True
|
2020-01-13 01:43:21 +03:00
|
|
|
<nothing>.Just -> Maybe.Just"""
|
|
|
|
, details = [ "details" ]
|
|
|
|
, under = "module"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)
|
2020-01-13 13:03:52 +03:00
|
|
|
, ( "ExposesSomeThings"
|
|
|
|
, [ Review.Test.error
|
|
|
|
{ message = ""
|
|
|
|
, details = [ "details" ]
|
|
|
|
, under = "module"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)
|
|
|
|
, ( "ExposesEverything"
|
2020-01-13 01:43:21 +03:00
|
|
|
, [ Review.Test.error
|
|
|
|
{ message = ""
|
|
|
|
, details = [ "details" ]
|
|
|
|
, under = "module"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)
|
|
|
|
]
|
2020-01-12 20:01:49 +03:00
|
|
|
]
|
2020-01-13 01:43:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
type alias ModuleContext =
|
|
|
|
{ scope : Scope.ModuleContext
|
|
|
|
, text : String
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
project : Project
|
|
|
|
project =
|
|
|
|
Project.new
|
2020-03-20 01:50:41 +03:00
|
|
|
|> Project.addDependency Dependencies.elmCore
|
|
|
|
|> Project.addDependency Dependencies.elmHtml
|
2020-01-13 01:43:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
rule : Rule
|
|
|
|
rule =
|
2020-03-19 21:21:49 +03:00
|
|
|
Rule.newProjectRuleSchema "TestRule" { scope = Scope.initialProjectContext }
|
2020-03-19 21:28:01 +03:00
|
|
|
|> Scope.addProjectVisitors
|
2020-03-19 21:21:49 +03:00
|
|
|
|> Rule.withModuleVisitor moduleVisitor
|
|
|
|
|> Rule.withModuleContext
|
|
|
|
{ fromProjectToModule =
|
|
|
|
\_ _ projectContext ->
|
|
|
|
{ scope = Scope.fromProjectToModule projectContext.scope
|
|
|
|
, text = ""
|
|
|
|
}
|
|
|
|
, fromModuleToProject =
|
|
|
|
\_ moduleNameNode moduleContext ->
|
|
|
|
{ scope = Scope.fromModuleToProject moduleNameNode moduleContext.scope
|
|
|
|
}
|
|
|
|
, foldProjectContexts = \a b -> { scope = Scope.foldProjectContexts a.scope b.scope }
|
|
|
|
}
|
2020-01-19 22:37:19 +03:00
|
|
|
|> Rule.fromProjectRuleSchema
|
2020-01-13 01:43:21 +03:00
|
|
|
|
|
|
|
|
2020-03-19 21:21:49 +03:00
|
|
|
moduleVisitor : Rule.ModuleRuleSchema anything ModuleContext -> Rule.ModuleRuleSchema { anything | hasAtLeastOneVisitor : () } ModuleContext
|
|
|
|
moduleVisitor schema =
|
|
|
|
schema
|
|
|
|
|> Rule.withDeclarationVisitor declarationVisitor
|
|
|
|
|> Rule.withExpressionVisitor expressionVisitor
|
|
|
|
|> Rule.withFinalModuleEvaluation finalEvaluation
|
|
|
|
|
|
|
|
|
2020-01-13 18:29:20 +03:00
|
|
|
declarationVisitor : Node Declaration -> Rule.Direction -> ModuleContext -> ( List Rule.Error, ModuleContext )
|
|
|
|
declarationVisitor node direction context =
|
|
|
|
case ( direction, Node.value node ) of
|
|
|
|
( Rule.OnEnter, Declaration.FunctionDeclaration function ) ->
|
|
|
|
case function.signature |> Maybe.map (Node.value >> .typeAnnotation) of
|
|
|
|
Nothing ->
|
|
|
|
( [], context )
|
|
|
|
|
|
|
|
Just typeAnnotation ->
|
|
|
|
( [], { context | text = context.text ++ "\n" ++ typeAnnotationNames context.scope typeAnnotation } )
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
( [], context )
|
|
|
|
|
|
|
|
|
|
|
|
typeAnnotationNames : Scope.ModuleContext -> Node TypeAnnotation -> String
|
|
|
|
typeAnnotationNames scope typeAnnotation =
|
|
|
|
case Node.value typeAnnotation of
|
|
|
|
TypeAnnotation.GenericType name ->
|
|
|
|
"<nothing>." ++ name ++ " -> <generic>"
|
|
|
|
|
|
|
|
TypeAnnotation.Typed (Node _ ( moduleName, typeName )) typeParameters ->
|
|
|
|
-- Elm.Type.Type (String.join "." moduleName ++ "." ++ typeName) (List.map syntaxTypeAnnotationToDocsType typeParameters)
|
|
|
|
let
|
|
|
|
nameInCode : String
|
|
|
|
nameInCode =
|
|
|
|
case moduleName of
|
|
|
|
[] ->
|
|
|
|
"<nothing>." ++ typeName
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
String.join "." moduleName ++ "." ++ typeName
|
|
|
|
|
|
|
|
realName : String
|
|
|
|
realName =
|
|
|
|
case Scope.realFunctionOrType moduleName typeName scope of
|
|
|
|
( [], name_ ) ->
|
|
|
|
"<nothing>." ++ name_
|
|
|
|
|
|
|
|
( moduleName_, name_ ) ->
|
|
|
|
String.join "." moduleName_ ++ "." ++ name_
|
|
|
|
in
|
|
|
|
nameInCode ++ " -> " ++ realName
|
|
|
|
|
|
|
|
TypeAnnotation.Unit ->
|
|
|
|
"unknown"
|
|
|
|
|
|
|
|
TypeAnnotation.Tupled typeAnnotationTypeAnnotationSyntaxElmNodeNodeSyntaxElmListList ->
|
|
|
|
"unknown"
|
|
|
|
|
|
|
|
TypeAnnotation.Record recordDefinitionTypeAnnotationSyntaxElm ->
|
|
|
|
"unknown"
|
|
|
|
|
|
|
|
TypeAnnotation.GenericRecord stringStringNodeNodeSyntaxElm recordDefinitionTypeAnnotationSyntaxElmNodeNodeSyntaxElm ->
|
|
|
|
"unknown"
|
|
|
|
|
|
|
|
TypeAnnotation.FunctionTypeAnnotation arg returnType ->
|
|
|
|
typeAnnotationNames scope arg ++ "\n" ++ typeAnnotationNames scope returnType
|
|
|
|
|
|
|
|
|
2020-01-13 01:43:21 +03:00
|
|
|
expressionVisitor : Node Expression -> Rule.Direction -> ModuleContext -> ( List Rule.Error, ModuleContext )
|
|
|
|
expressionVisitor node direction context =
|
|
|
|
case ( direction, Node.value node ) of
|
|
|
|
( Rule.OnEnter, Expression.FunctionOrValue moduleName name ) ->
|
|
|
|
let
|
|
|
|
nameInCode : String
|
|
|
|
nameInCode =
|
|
|
|
case moduleName of
|
|
|
|
[] ->
|
|
|
|
"<nothing>." ++ name
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
String.join "." moduleName ++ "." ++ name
|
|
|
|
|
|
|
|
realName : String
|
|
|
|
realName =
|
|
|
|
case Scope.realFunctionOrType moduleName name context.scope of
|
|
|
|
( [], name_ ) ->
|
|
|
|
"<nothing>." ++ name_
|
|
|
|
|
|
|
|
( moduleName_, name_ ) ->
|
|
|
|
String.join "." moduleName_ ++ "." ++ name_
|
|
|
|
in
|
|
|
|
( [], { context | text = context.text ++ "\n" ++ nameInCode ++ " -> " ++ realName } )
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
( [], context )
|
|
|
|
|
|
|
|
|
|
|
|
finalEvaluation : ModuleContext -> List Rule.Error
|
|
|
|
finalEvaluation context =
|
|
|
|
[ Rule.error { message = context.text, details = [ "details" ] }
|
|
|
|
{ start = { row = 1, column = 1 }
|
|
|
|
, end = { row = 1, column = 7 }
|
|
|
|
}
|
|
|
|
]
|