From 2b3a3e5a88d909617922bd2675c3ad4779d81c88 Mon Sep 17 00:00:00 2001 From: Jeroen Engels Date: Sun, 22 Jan 2017 23:50:00 +0100 Subject: [PATCH] Add DefaultPatternPosition rule --- README.md | 1 + elm-package.json | 1 + example/Main.elm | 4 +- example/elm-package.json | 1 + rules/DefaultPatternPosition.elm | 94 ++++++++++++++++++++++++++++ tests/DefaultPatternPositionTest.elm | 91 +++++++++++++++++++++++++++ tests/Main.elm | 4 +- tests/elm-package.json | 1 + 8 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 rules/DefaultPatternPosition.elm create mode 100644 tests/DefaultPatternPositionTest.elm diff --git a/README.md b/README.md index 0d0b7bef..6300516c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ You can read the slides for my [presentation](http://slides.com/jeroenengels/elm ## Rules +- [DefaultPatternPosition](rules/DefaultPatternPosition.md) - Enforce the default pattern to always appear first or last. - [NoConstantCondition](rules/NoConstantCondition.md) - Forbid the use of expressions in an If condition whose value are always the same. - [NoDebug](rules/NoDebug.md) - Forbid the use of `Debug` before it goes into production. - [NoDuplicateImports](rules/NoDuplicateImports.md) - Forbid importing the same module several times in a file. diff --git a/elm-package.json b/elm-package.json index 08a43093..851c5450 100644 --- a/elm-package.json +++ b/elm-package.json @@ -9,6 +9,7 @@ ], "exposed-modules": [], "dependencies": { + "elm-community/list-extra": "5.0.1 <= v < 6.0.0", "elm-lang/core": "5.0.0 <= v < 6.0.0", "elm-lang/html": "2.0.0 <= v < 3.0.0", "jfmengels/elm-ast": "1.0.1 <= v < 2.0.0" diff --git a/example/Main.elm b/example/Main.elm index b21c441b..48aeae56 100644 --- a/example/Main.elm +++ b/example/Main.elm @@ -14,6 +14,7 @@ import Regex exposing (regex, escape) -- Rules +import DefaultPatternPosition import NoConstantCondition import NoDebug import NoDuplicateImports @@ -32,7 +33,8 @@ type Msg rules : List (String -> List Types.Error) rules = - [ NoConstantCondition.rule + [ DefaultPatternPosition.rule { position = DefaultPatternPosition.Last } + , NoConstantCondition.rule , NoDebug.rule , NoDuplicateImports.rule , NoExposingEverything.rule diff --git a/example/elm-package.json b/example/elm-package.json index 69eb2bb6..578eb43a 100644 --- a/example/elm-package.json +++ b/example/elm-package.json @@ -10,6 +10,7 @@ ], "exposed-modules": [], "dependencies": { + "elm-community/list-extra": "5.0.1 <= v < 6.0.0", "elm-lang/core": "5.0.0 <= v < 6.0.0", "elm-lang/html": "2.0.0 <= v < 3.0.0", "jfmengels/elm-ast": "1.0.0 <= v < 2.0.0" diff --git a/rules/DefaultPatternPosition.elm b/rules/DefaultPatternPosition.elm new file mode 100644 index 00000000..ca249eed --- /dev/null +++ b/rules/DefaultPatternPosition.elm @@ -0,0 +1,94 @@ +module DefaultPatternPosition exposing (rule, PatternPosition(..)) + +import Ast.Expression exposing (..) +import Html.Attributes exposing (pattern) +import Lint exposing (doNothing, lint) +import List.Extra exposing (findIndex) +import Regex +import Set exposing (Set) +import Types exposing (Direction(..), Error, LintRule) + + +type alias Context = + {} + + +type PatternPosition + = First + | Last + + +type alias Configuration = + { position : PatternPosition + } + + +rule : Configuration -> String -> List Error +rule config input = + lint input (implementation config) + + +implementation : Configuration -> LintRule Context +implementation configuration = + { statementFn = doNothing + , typeFn = doNothing + , expressionFn = expressionFn configuration + , moduleEndFn = (\ctx -> ( [], ctx )) + , initialContext = Context + } + + +error : String -> Error +error = + Error "DefaultPatternPosition" + + +{-| TODO Share this in a util file, already defined in NoUselessPatternMatching +-} +isVariable : String -> Bool +isVariable = + Regex.contains (Regex.regex "^[_a-z][\\w\\d]*$") + + +isDefaultPattern : ( Expression, Expression ) -> Bool +isDefaultPattern pattern = + case Tuple.first pattern of + Variable names -> + if isVariable (String.join "." names) then + True + else + False + + _ -> + False + + +findDefaultPattern : List ( Expression, Expression ) -> Maybe Int +findDefaultPattern = + findIndex isDefaultPattern + + +expressionFn : Configuration -> Context -> Direction Expression -> ( List Error, Context ) +expressionFn config ctx node = + case node of + Enter (Case expr patterns) -> + case findDefaultPattern patterns of + Nothing -> + ( [], ctx ) + + Just index -> + case config.position of + First -> + if index /= 0 then + ( [ error "Expected default pattern to appear first in the list of patterns" ], ctx ) + else + ( [], ctx ) + + Last -> + if index /= (List.length patterns) - 1 then + ( [ error "Expected default pattern to appear last in the list of patterns" ], ctx ) + else + ( [], ctx ) + + _ -> + ( [], ctx ) diff --git a/tests/DefaultPatternPositionTest.elm b/tests/DefaultPatternPositionTest.elm new file mode 100644 index 00000000..6e07f3c7 --- /dev/null +++ b/tests/DefaultPatternPositionTest.elm @@ -0,0 +1,91 @@ +port module DefaultPatternPositionTest exposing (all) + +import Expect +import Test exposing (describe, test, Test) +import DefaultPatternPosition exposing (rule, PatternPosition(First, Last)) +import Types exposing (Error) + + +error : String -> Error +error position = + Error "DefaultPatternPosition" ("Expected default pattern to appear " ++ position ++ " in the list of patterns") + + +tests : List Test +tests = + [ test "should not report when default pattern is at the expected position (first)" <| + \() -> + """a = case b of + _ -> 1 + Bar -> 1 + Foo -> 1 + """ + |> rule { position = First } + |> Expect.equal [] + , test "should not report when default pattern is at the expected position (last)" <| + \() -> + """a = case b of + Foo -> 1 + Bar -> 1 + _ -> 1 + """ + |> rule { position = Last } + |> Expect.equal [] + , test "should not report when there is no default pattern (first)" <| + \() -> + """a = case b of + Foo -> 1 + Bar -> 1 + """ + |> rule { position = First } + |> Expect.equal [] + , test "should not report when there is no default pattern (last)" <| + \() -> + """a = case b of + Foo -> 1 + Bar -> 1 + """ + |> rule { position = Last } + |> Expect.equal [] + , test "should report an error when the default pattern is not at the expected position (first) (opposite expected position)" <| + \() -> + """a = case b of + Foo -> 1 + Bar -> 1 + _ -> 1 + """ + |> rule { position = First } + |> Expect.equal [ error "first" ] + , test "should report an error when the default pattern is not at the expected position (first) (somewhere in the middle)" <| + \() -> + """a = case b of + Foo -> 1 + _ -> 1 + Bar -> 1 + """ + |> rule { position = First } + |> Expect.equal [ error "first" ] + , test "should report an error when the default pattern is not at the expected position (last) (opposite expected position)" <| + \() -> + """a = case b of + _ -> 1 + Foo -> 1 + Bar -> 1 + """ + |> rule { position = Last } + |> Expect.equal [ error "last" ] + , test "should report an error when the default pattern is not at the expected position (last) (somewhere in the middle)" <| + \() -> + """a = case b of + Foo -> 1 + _ -> 1 + Bar -> 1 + """ + |> rule { position = Last } + |> Expect.equal [ error "last" ] + ] + + +all : Test +all = + describe "DefaultPatternPosition" tests diff --git a/tests/Main.elm b/tests/Main.elm index 6961a291..0a4dc842 100644 --- a/tests/Main.elm +++ b/tests/Main.elm @@ -3,6 +3,7 @@ port module Tests exposing (..) import Test exposing (describe, Test) import Test.Runner.Node exposing (run) import Json.Encode exposing (Value) +import DefaultPatternPositionTest import NoConstantConditionTest import NoDebugTest import NoDuplicateImportsTest @@ -25,7 +26,8 @@ port emit : ( String, Value ) -> Cmd msg all : Test all = describe "Visitors" - [ NoConstantConditionTest.all + [ DefaultPatternPositionTest.all + , NoConstantConditionTest.all , NoDebugTest.all , NoDuplicateImportsTest.all , NoImportingEverythingTest.all diff --git a/tests/elm-package.json b/tests/elm-package.json index 112b37dc..86f9c78b 100644 --- a/tests/elm-package.json +++ b/tests/elm-package.json @@ -11,6 +11,7 @@ "exposed-modules": [], "dependencies": { "elm-community/elm-test": "3.1.0 <= v < 4.0.0", + "elm-community/list-extra": "5.0.1 <= v < 6.0.0", "elm-lang/core": "5.0.0 <= v < 6.0.0", "elm-lang/html": "2.0.0 <= v < 3.0.0", "jfmengels/elm-ast": "1.0.1 <= v < 2.0.0",