diff --git a/src/Scope.elm b/src/Scope.elm index 977818d1..536040b5 100644 --- a/src/Scope.elm +++ b/src/Scope.elm @@ -68,6 +68,9 @@ initialContext = Context { scopes = Nonempty.fromElement Dict.empty , importAliases = Dict.empty + + -- TODO Add elm/core's prelude + -- https://package.elm-lang.org/packages/elm/core/latest , importedFunctionOrTypes = Dict.empty , dependencies = Dict.empty } diff --git a/tests/Dependencies.elm b/tests/Dependencies.elm new file mode 100644 index 00000000..668618d8 --- /dev/null +++ b/tests/Dependencies.elm @@ -0,0 +1,31 @@ +module Dependencies exposing (elmHtml) + +import Elm.Docs +import Elm.Type as Type + + +elmHtml : { packageName : String, modules : List Elm.Docs.Module } +elmHtml = + { packageName = "elm/html" + , modules = + [ { name = "Html" + , comment = "" + , unions = [] + , aliases = [] + , values = + [ { name = "button" + , comment = "" + , tipe = + -- List.List (Html.Attribute msg) -> + Type.Lambda (Type.Type "List.List" [ Type.Type "Html.Attribute" [ Type.Var "msg" ] ]) + -- List.List (Html.Html msg) -> + (Type.Lambda (Type.Type "List.List" [ Type.Type "Html.Html" [ Type.Var "msg" ] ]) + -- Html.Html msg + (Type.Type "Html.Html" [ Type.Var "msg" ]) + ) + } + ] + , binops = [] + } + ] + } diff --git a/tests/NoHtmlButtonTest.elm b/tests/NoHtmlButtonTest.elm index c0f868c0..2a03b666 100644 --- a/tests/NoHtmlButtonTest.elm +++ b/tests/NoHtmlButtonTest.elm @@ -1,5 +1,6 @@ module NoHtmlButtonTest exposing (all) +import Dependencies import Elm.Type as Type import NoHtmlButton exposing (rule) import Review.Project as Project exposing (Project) @@ -17,26 +18,7 @@ testRule string = projectWithHtmlDependency : Project projectWithHtmlDependency = Project.new - |> Project.withDependency - { packageName = "elm/html" - , modules = - [ { name = "Html" - , comment = "" - , unions = [] - , aliases = [] - , values = - [ { name = "button" - , comment = "" - , tipe = - -- "List.List (Html.Attribute msg) -> List.List (Html.Html msg) -> Html.Html msg" - Type.Lambda (Type.Type "List.List" [ Type.Type "Html.Attribute" [ Type.Var "msg" ] ]) - (Type.Lambda (Type.Type "List.List" [ Type.Type "Html.Html" [ Type.Var "msg" ] ]) (Type.Type "Html.Html" [ Type.Var "msg" ])) - } - ] - , binops = [] - } - ] - } + |> Project.withDependency Dependencies.elmHtml testRuleWithHtmlDependency : String -> ReviewResult diff --git a/tests/ScopeTest.elm b/tests/ScopeTest.elm new file mode 100644 index 00000000..0e017be3 --- /dev/null +++ b/tests/ScopeTest.elm @@ -0,0 +1,132 @@ +module ScopeTest exposing (all) + +import Dependencies +import Elm.Syntax.Declaration exposing (Declaration) +import Elm.Syntax.Expression as Expression exposing (Expression) +import Elm.Syntax.Node as Node exposing (Node) +import Review.Project as Project exposing (Project) +import Review.Rule as Rule exposing (Rule) +import Review.Test exposing (ReviewResult) +import Scope +import Test exposing (Test, test) + + +type alias Context = + { scope : Scope.Context + , text : String + } + + +project : Project +project = + Project.new + |> Project.withDependency Dependencies.elmHtml + + +testRule : Rule -> String -> ReviewResult +testRule rule string = + "module A exposing (..)\n\n" + ++ string + |> Review.Test.runWithProjectData project rule + + +baseRule : Rule.Schema Rule.ForLookingAtASingleFile { hasAtLeastOneVisitor : () } Context +baseRule = + Rule.newSchema "TestRule" + |> Rule.withInitialContext initialContext + |> Scope.addVisitors + { setter = \scope context -> { context | scope = scope } + , getter = .scope + } + + +initialContext : Context +initialContext = + { scope = Scope.initialContext + , text = "" + } + + +all : Test +all = + Test.describe "Scope" + [ Test.describe "Scope.realFunctionOrType" + [ test "should indicate that module from which a function or value comes from" <| + \() -> + let + rule : Rule + rule = + baseRule + |> Rule.withExpressionVisitor expressionVisitor + |> Rule.withFinalEvaluation finalEvaluation + |> Rule.fromSchema + + expressionVisitor : Node Expression -> Rule.Direction -> Context -> ( List Rule.Error, Context ) + expressionVisitor node direction context = + case ( direction, Node.value node ) of + ( Rule.OnEnter, Expression.FunctionOrValue moduleName name ) -> + let + nameInCode : String + nameInCode = + case moduleName of + [] -> + "." ++ name + + _ -> + String.join "." moduleName ++ "." ++ name + + realName : String + realName = + case Scope.realFunctionOrType moduleName name context.scope of + ( [], name_ ) -> + "." ++ name_ + + ( moduleName_, name_ ) -> + String.join "." moduleName_ ++ "." ++ name_ + in + ( [], { context | text = context.text ++ "\n" ++ nameInCode ++ " -> " ++ realName } ) + + _ -> + ( [], context ) + + finalEvaluation : Context -> List Rule.Error + finalEvaluation context = + [ Rule.error { message = context.text, details = [ "details" ] } + { start = { row = 1, column = 1 } + , end = { row = 1, column = 7 } + } + ] + in + testRule rule """ +import Bar as Baz exposing (baz) +import Foo +import Foo.Bar +import Html exposing (..) +import Http exposing (get) + +a = b + Foo.bar + Foo.Bar + Baz.foo + baz + button + Http.get + get +""" + |> Review.Test.expectErrors + [ Review.Test.error + { message = """ +.b -> .b +Foo.bar -> Foo.bar +Foo.Bar -> Foo.Bar +Baz.foo -> Bar.foo +.baz -> Bar.baz +.button -> Html.button +Http.get -> Http.get +.get -> Http.get""" + , details = [ "details" ] + , under = "module" + } + ] + ] + ]