Re-add DefaultPatternPosition

This commit is contained in:
Jeroen Engels 2019-06-07 19:52:17 +02:00
parent 21411aaf8c
commit ede98bc179
5 changed files with 281 additions and 4 deletions

View File

@ -8,15 +8,19 @@
"Lint",
"Lint.Rule",
"Lint.Rule.NoDebug",
"Lint.Rule.NoUnusedVariables"
"Lint.Rule.NoUnusedVariables",
"Lint.Rule.NoImportingEverything",
"Lint.Rule.DefaultPatternPosition"
],
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0",
"elm/regex": "1.0.0 <= v < 2.0.0",
"elm-community/list-extra": "8.2.0 <= v < 9.0.0",
"mgold/elm-nonempty-list": "4.0.1 <= v < 5.0.0",
"stil4m/elm-syntax": "7.1.0 <= v < 8.0.0"
},
"test-dependencies": {
"elm-explorations/test": "1.2.1 <= v < 2.0.0"
}
}
}

View File

@ -8,6 +8,7 @@ import Html.Attributes exposing (class, id, style)
import Html.Events exposing (onInput)
import Lint exposing (Rule, Severity(..), lintSource)
import Lint.Error exposing (Error)
import Lint.Rule.DefaultPatternPosition
import Lint.Rule.NoDebug
import Lint.Rule.NoImportingEverything
import Lint.Rule.NoUnusedVariables
@ -23,8 +24,8 @@ config =
[ ( Critical, Lint.Rule.NoDebug.rule )
, ( Critical, Lint.Rule.NoUnusedVariables.rule )
, ( Critical, Lint.Rule.NoImportingEverything.rule { exceptions = [ "Html" ] } )
, ( Critical, Lint.Rule.DefaultPatternPosition.rule { position = Lint.Rule.DefaultPatternPosition.Last } )
-- , ( Critical, Lint.Rule.DefaultPatternPosition.rule { position = Lint.Rule.DefaultPatternPosition.Last } )
-- , ( Critical, Lint.Rule.NoConstantCondition.rule )
-- , ( Critical, Lint.Rule.NoDuplicateImports.rule )
-- , ( Critical, Lint.Rule.NoExposingEverything.rule )

View File

@ -10,6 +10,8 @@
"elm/browser": "1.0.1",
"elm/core": "1.0.0",
"elm/html": "1.0.0",
"elm/regex": "1.0.0",
"elm-community/list-extra": "8.2.0",
"mgold/elm-nonempty-list": "4.0.0",
"stil4m/elm-syntax": "7.0.2"
},
@ -21,7 +23,6 @@
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2",
"elm-community/json-extra": "4.0.0",
"elm-community/list-extra": "8.1.0",
"rtfeldman/elm-hex": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.2",
"stil4m/structured-writer": "1.0.2"

View File

@ -0,0 +1,161 @@
module Lint.Rule.DefaultPatternPosition exposing (rule, Configuration, PatternPosition(..))
{-|
@docs rule, Configuration, PatternPosition
# Fail
rules =
[ DefaultPatternPosition.rule { position = Lint.Rules.DefaultPatternPosition.Last }
]
case value of
-- LintError, this pattern should appear last
_ -> result
Foo -> bar
-- --------------------
rules =
[ DefaultPatternPosition.rule { position = Lint.Rules.DefaultPatternPosition.First }
]
case value of
Foo -> bar
-- LintError, this pattern should appear first
_ -> result
# Success
rules =
[ DefaultPatternPosition.rule { position = Lint.Rules.DefaultPatternPosition.Last }
]
case value of
Foo -> bar
_ -> result
case value of
-- No default pattern
Foo -> bar
Bar -> foo
-- --------------------
rules =
[ DefaultPatternPosition.rule { position = Lint.Rules.DefaultPatternPosition.First }
]
case value of
_ -> result
Foo -> bar
-}
import Elm.Syntax.Expression exposing (Expression(..))
import Elm.Syntax.Node as Node exposing (Node)
import Elm.Syntax.Pattern exposing (Pattern(..))
import Lint exposing (Rule, lint)
import Lint.Error exposing (Error)
import Lint.Rule exposing (Direction(..), Implementation, createRule)
import List.Extra exposing (findIndex)
import Regex
type alias Context =
{}
{-| Configures whether the default pattern should appear first or last.
-}
type PatternPosition
= First
| Last
{-| Configuration for the rule.
-}
type alias Configuration =
{ position : PatternPosition
}
{-| Enforce the default pattern to always appear first or last.
-}
rule : Configuration -> Rule
rule config input =
lint input (implementation config)
implementation : Configuration -> Implementation Context
implementation configuration =
createRule
{}
(\v -> { v | visitExpression = visitExpression configuration })
error : Node a -> String -> Error
error node message =
Error
"DefaultPatternPosition"
message
(Node.range node)
{- TODO Share isVariable this in a util file, already defined in NoUselessPatternMatching -}
isVariable : String -> Bool
isVariable =
Regex.fromString "^[_a-z][\\w\\d]*$"
|> Maybe.withDefault Regex.never
|> Regex.contains
isDefaultPattern : Pattern -> Bool
isDefaultPattern pattern =
case pattern of
AllPattern ->
True
_ ->
False
findDefaultPattern : List ( Node Pattern, Node Expression ) -> Maybe Int
findDefaultPattern patterns =
patterns
|> List.map (Tuple.first >> Node.value)
|> findIndex isDefaultPattern
visitExpression : Configuration -> Context -> Direction -> Node Expression -> ( List Error, Context )
visitExpression config ctx direction node =
case ( direction, Node.value node ) of
( Enter, CaseExpression { cases } ) ->
case findDefaultPattern cases of
Nothing ->
( [], ctx )
Just index ->
case config.position of
First ->
if index /= 0 then
( [ error node "Expected default pattern to appear first in the list of patterns" ], ctx )
else
( [], ctx )
Last ->
if index /= List.length cases - 1 then
( [ error node "Expected default pattern to appear last in the list of patterns" ], ctx )
else
( [], ctx )
_ ->
( [], ctx )

View File

@ -0,0 +1,110 @@
module DefaultPatternPositionTest exposing (all)
import Elm.Syntax.Range exposing (Location, Range)
import Lint exposing (Rule)
import Lint.Error exposing (Error)
import Lint.Rule exposing (LintResult)
import Lint.Rule.DefaultPatternPosition exposing (Configuration, PatternPosition(..), rule)
import Test exposing (Test, describe, test)
import TestUtil exposing (expectErrorsWithoutRange, location, ruleTester)
testRule : Configuration -> String -> LintResult
testRule options =
ruleTester (rule options)
error : String -> Error
error position =
Error
"DefaultPatternPosition"
("Expected default pattern to appear " ++ position ++ " in the list of patterns")
(location ( 0, 0 ) ( 0, 0 ))
tests : List Test
tests =
[ test "should not report when default pattern is at the expected position (first)" <|
\() ->
"""module A exposing(..)
a = case b of
_ -> 1
Bar -> 1
Foo -> 1
"""
|> testRule { position = First }
|> expectErrorsWithoutRange []
, test "should not report when default pattern is at the expected position (last)" <|
\() ->
"""module A exposing(..)
a = case b of
Foo -> 1
Bar -> 1
_ -> 1
"""
|> testRule { position = Last }
|> expectErrorsWithoutRange []
, test "should not report when there is no default pattern (first)" <|
\() ->
"""module A exposing(..)
a = case b of
Foo -> 1
Bar -> 1
"""
|> testRule { position = First }
|> expectErrorsWithoutRange []
, test "should not report when there is no default pattern (last)" <|
\() ->
"""module A exposing(..)
a = case b of
Foo -> 1
Bar -> 1
"""
|> testRule { position = Last }
|> expectErrorsWithoutRange []
, test "should report an error when the default pattern is not at the expected position (first) (opposite expected position)" <|
\() ->
"""module A exposing(..)
a = case b of
Foo -> 1
Bar -> 1
_ -> 1
"""
|> testRule { position = First }
|> expectErrorsWithoutRange [ error "first" ]
, test "should report an error when the default pattern is not at the expected position (first) (somewhere in the middle)" <|
\() ->
"""module A exposing(..)
a = case b of
Foo -> 1
_ -> 1
Bar -> 1
"""
|> testRule { position = First }
|> expectErrorsWithoutRange [ error "first" ]
, test "should report an error when the default pattern is not at the expected position (last) (opposite expected position)" <|
\() ->
"""module A exposing(..)
a = case b of
_ -> 1
Foo -> 1
Bar -> 1
"""
|> testRule { position = Last }
|> expectErrorsWithoutRange [ error "last" ]
, test "should report an error when the default pattern is not at the expected position (last) (somewhere in the middle)" <|
\() ->
"""module A exposing(..)
a = case b of
Foo -> 1
_ -> 1
Bar -> 1
"""
|> testRule { position = Last }
|> expectErrorsWithoutRange [ error "last" ]
]
all : Test
all =
describe "DefaultPatternPosition" tests