mirror of
https://github.com/jfmengels/elm-review.git
synced 2024-11-27 12:08:51 +03:00
Backport elm-review-debug
This commit is contained in:
parent
fa8bc02068
commit
63819d3fb7
@ -6,10 +6,11 @@ module NoDebug.Log exposing (rule)
|
|||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Elm.Syntax.Exposing as Exposing
|
|
||||||
import Elm.Syntax.Expression as Expression exposing (Expression)
|
import Elm.Syntax.Expression as Expression exposing (Expression)
|
||||||
import Elm.Syntax.Import exposing (Import)
|
import Elm.Syntax.Node as Node exposing (Node(..))
|
||||||
import Elm.Syntax.Node as Node exposing (Node)
|
import Elm.Syntax.Range exposing (Range)
|
||||||
|
import Review.Fix
|
||||||
|
import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable)
|
||||||
import Review.Rule as Rule exposing (Error, Rule)
|
import Review.Rule as Rule exposing (Error, Rule)
|
||||||
|
|
||||||
|
|
||||||
@ -63,75 +64,223 @@ elm-review --template jfmengels/elm-review-debug/example --rules NoDebug.Log
|
|||||||
-}
|
-}
|
||||||
rule : Rule
|
rule : Rule
|
||||||
rule =
|
rule =
|
||||||
Rule.newModuleRuleSchema "NoDebug.Log" { hasLogBeenImported = False }
|
Rule.newModuleRuleSchemaUsingContextCreator "NoDebug.Log" initContext
|
||||||
|> Rule.withImportVisitor importVisitor
|
|> Rule.withExpressionEnterVisitor expressionVisitor
|
||||||
|> Rule.withExpressionVisitor expressionVisitor
|
|
||||||
|> Rule.fromModuleRuleSchema
|
|> Rule.fromModuleRuleSchema
|
||||||
|
|
||||||
|
|
||||||
type alias Context =
|
type alias Context =
|
||||||
{ hasLogBeenImported : Bool
|
{ lookupTable : ModuleNameLookupTable
|
||||||
|
, rangesToIgnore : List Range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
error : Node a -> Error {}
|
initContext : Rule.ContextCreator () Context
|
||||||
error node =
|
initContext =
|
||||||
Rule.error
|
Rule.initContextCreator
|
||||||
|
(\lookupTable () ->
|
||||||
|
{ lookupTable = lookupTable
|
||||||
|
, rangesToIgnore = []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Rule.withModuleNameLookupTable
|
||||||
|
|
||||||
|
|
||||||
|
error : Node a -> Maybe Range -> Error {}
|
||||||
|
error node rangeToRemove =
|
||||||
|
Rule.errorWithFix
|
||||||
{ message = "Remove the use of `Debug.log` before shipping to production"
|
{ message = "Remove the use of `Debug.log` before shipping to production"
|
||||||
, details =
|
, details =
|
||||||
[ "`Debug.log` is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production."
|
[ "`Debug.log` is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production."
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
(Node.range node)
|
(Node.range node)
|
||||||
|
(case rangeToRemove of
|
||||||
|
Just range ->
|
||||||
importVisitor : Node Import -> Context -> ( List nothing, Context )
|
[ Review.Fix.removeRange range ]
|
||||||
importVisitor node context =
|
|
||||||
let
|
|
||||||
moduleName : List String
|
|
||||||
moduleName =
|
|
||||||
node
|
|
||||||
|> Node.value
|
|
||||||
|> .moduleName
|
|
||||||
|> Node.value
|
|
||||||
in
|
|
||||||
if moduleName == [ "Debug" ] then
|
|
||||||
case node |> Node.value |> .exposingList |> Maybe.map Node.value of
|
|
||||||
Just (Exposing.All _) ->
|
|
||||||
( [], { hasLogBeenImported = True } )
|
|
||||||
|
|
||||||
Just (Exposing.Explicit importedNames) ->
|
|
||||||
( [], { hasLogBeenImported = List.any isLog importedNames } )
|
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
( [], context )
|
[]
|
||||||
|
)
|
||||||
else
|
|
||||||
( [], context )
|
|
||||||
|
|
||||||
|
|
||||||
isLog : Node Exposing.TopLevelExpose -> Bool
|
handleWhenSingleArg : Range -> Context -> Node Expression -> ( List (Error {}), Context )
|
||||||
isLog node =
|
handleWhenSingleArg rangeToPotentiallyRemove context node =
|
||||||
case Node.value node of
|
case Node.value node of
|
||||||
Exposing.FunctionExpose "log" ->
|
Expression.Application (((Node logFunctionRange (Expression.FunctionOrValue _ "log")) as logFunctionNode) :: logArguments) ->
|
||||||
True
|
case ModuleNameLookupTable.moduleNameAt context.lookupTable logFunctionRange of
|
||||||
|
Just [ "Debug" ] ->
|
||||||
|
let
|
||||||
|
rangeToRemove : Maybe Range
|
||||||
|
rangeToRemove =
|
||||||
|
case logArguments of
|
||||||
|
[ _ ] ->
|
||||||
|
Just rangeToPotentiallyRemove
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
False
|
Nothing
|
||||||
|
in
|
||||||
|
( [ error logFunctionNode rangeToRemove ]
|
||||||
|
, { context | rangesToIgnore = Node.range node :: logFunctionRange :: context.rangesToIgnore }
|
||||||
|
)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( [], context )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( [], context )
|
||||||
|
|
||||||
|
|
||||||
expressionVisitor : Node Expression -> Rule.Direction -> Context -> ( List (Error {}), Context )
|
collectPipeline : Node Expression -> ( List Range, List (Node Expression) )
|
||||||
expressionVisitor node direction context =
|
collectPipeline node =
|
||||||
case ( direction, Node.value node ) of
|
collectPipelineHelp node
|
||||||
( Rule.OnEnter, Expression.FunctionOrValue [ "Debug" ] "log" ) ->
|
|
||||||
( [ error node ], context )
|
|
||||||
|
|
||||||
( Rule.OnEnter, Expression.FunctionOrValue [] "log" ) ->
|
|
||||||
if context.hasLogBeenImported then
|
collectPipelineHelp : Node Expression -> ( List Range, List (Node Expression) )
|
||||||
( [ error node ], context )
|
collectPipelineHelp node =
|
||||||
|
case Node.value node of
|
||||||
|
Expression.OperatorApplication "|>" _ left right ->
|
||||||
|
let
|
||||||
|
( x1, y1 ) =
|
||||||
|
collectPipelineHelp left
|
||||||
|
|
||||||
|
( x2, y2 ) =
|
||||||
|
collectPipelineHelp right
|
||||||
|
in
|
||||||
|
( Node.range node :: (x1 ++ x2), y1 ++ y2 )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( [], [ node ] )
|
||||||
|
|
||||||
|
|
||||||
|
expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )
|
||||||
|
expressionVisitor node context =
|
||||||
|
if List.member (Node.range node) context.rangesToIgnore then
|
||||||
|
( [], context )
|
||||||
|
|
||||||
else
|
else
|
||||||
( [], context )
|
expressionVisitorHelp node context
|
||||||
|
|
||||||
|
|
||||||
|
expressionVisitorHelp : Node Expression -> Context -> ( List (Error {}), Context )
|
||||||
|
expressionVisitorHelp node context =
|
||||||
|
case Node.value node of
|
||||||
|
Expression.OperatorApplication "|>" _ left right ->
|
||||||
|
let
|
||||||
|
( pipelineOperators, pipelineOperations ) =
|
||||||
|
collectPipeline node
|
||||||
|
|
||||||
|
contextWithIgnoredElements : Context
|
||||||
|
contextWithIgnoredElements =
|
||||||
|
{ context | rangesToIgnore = List.concat [ pipelineOperators, List.map Node.range pipelineOperations, context.rangesToIgnore ] }
|
||||||
|
|
||||||
|
( errorsAfterLeft, contextAfterLeft ) =
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range left).start
|
||||||
|
, end = (Node.range right).start
|
||||||
|
}
|
||||||
|
contextWithIgnoredElements
|
||||||
|
left
|
||||||
|
in
|
||||||
|
List.foldl
|
||||||
|
(\pipelineItem ( prev, ( errors, ctx ) ) ->
|
||||||
|
let
|
||||||
|
( newErrors, newContext ) =
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range prev).end
|
||||||
|
, end = (Node.range pipelineItem).end
|
||||||
|
}
|
||||||
|
ctx
|
||||||
|
pipelineItem
|
||||||
|
in
|
||||||
|
( pipelineItem, ( newErrors ++ errors, newContext ) )
|
||||||
|
)
|
||||||
|
( left, ( errorsAfterLeft, contextAfterLeft ) )
|
||||||
|
pipelineOperations
|
||||||
|
|> Tuple.second
|
||||||
|
|
||||||
|
Expression.OperatorApplication "<|" _ left right ->
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range left).start
|
||||||
|
, end = (Node.range right).start
|
||||||
|
}
|
||||||
|
context
|
||||||
|
left
|
||||||
|
|
||||||
|
Expression.OperatorApplication "<<" _ left right ->
|
||||||
|
let
|
||||||
|
( errorsLeft, contextAfterLeft ) =
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range left).start
|
||||||
|
, end = (Node.range right).start
|
||||||
|
}
|
||||||
|
context
|
||||||
|
left
|
||||||
|
|
||||||
|
( errorsRight, contextAfterRight ) =
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range left).end
|
||||||
|
, end = (Node.range right).end
|
||||||
|
}
|
||||||
|
contextAfterLeft
|
||||||
|
right
|
||||||
|
in
|
||||||
|
( errorsLeft ++ errorsRight, contextAfterRight )
|
||||||
|
|
||||||
|
Expression.OperatorApplication ">>" _ left right ->
|
||||||
|
let
|
||||||
|
( errorsLeft, contextAfterLeft ) =
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range left).start
|
||||||
|
, end = (Node.range right).start
|
||||||
|
}
|
||||||
|
context
|
||||||
|
left
|
||||||
|
|
||||||
|
( errorsRight, contextAfterRight ) =
|
||||||
|
handleWhenSingleArg
|
||||||
|
{ start = (Node.range left).end
|
||||||
|
, end = (Node.range right).end
|
||||||
|
}
|
||||||
|
contextAfterLeft
|
||||||
|
right
|
||||||
|
in
|
||||||
|
( errorsLeft ++ errorsRight, contextAfterRight )
|
||||||
|
|
||||||
|
Expression.Application (((Node logFunctionRange (Expression.FunctionOrValue _ "log")) as logFunctionNode) :: logArguments) ->
|
||||||
|
let
|
||||||
|
rangeToRemove : Maybe Range
|
||||||
|
rangeToRemove =
|
||||||
|
case logArguments of
|
||||||
|
[ _, valueToLog ] ->
|
||||||
|
Just
|
||||||
|
{ start = logFunctionRange.start
|
||||||
|
, end = (Node.range valueToLog).start
|
||||||
|
}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
in
|
||||||
|
reportIfDebugLog logFunctionNode context rangeToRemove
|
||||||
|
|
||||||
|
Expression.FunctionOrValue _ "log" ->
|
||||||
|
reportIfDebugLog node context Nothing
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( [], context )
|
||||||
|
|
||||||
|
|
||||||
|
reportIfDebugLog : Node Expression -> Context -> Maybe Range -> ( List (Error {}), Context )
|
||||||
|
reportIfDebugLog node context rangeToRemove =
|
||||||
|
if List.member (Node.range node) context.rangesToIgnore then
|
||||||
|
( [], context )
|
||||||
|
|
||||||
|
else
|
||||||
|
case ModuleNameLookupTable.moduleNameFor context.lookupTable node of
|
||||||
|
Just [ "Debug" ] ->
|
||||||
|
( [ error node rangeToRemove ]
|
||||||
|
, { context | rangesToIgnore = Node.range node :: context.rangesToIgnore }
|
||||||
|
)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( [], context )
|
( [], context )
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
module NoDebug.LogTest exposing (all)
|
module NoDebug.LogTest exposing (all)
|
||||||
|
|
||||||
|
import Dependencies.ElmCore
|
||||||
import NoDebug.Log exposing (rule)
|
import NoDebug.Log exposing (rule)
|
||||||
|
import Review.Project as Project exposing (Project)
|
||||||
import Review.Test exposing (ReviewResult)
|
import Review.Test exposing (ReviewResult)
|
||||||
import Test exposing (Test, describe, test)
|
import Test exposing (Test, describe, test)
|
||||||
|
|
||||||
@ -9,7 +11,25 @@ testRule : String -> ReviewResult
|
|||||||
testRule string =
|
testRule string =
|
||||||
"module A exposing (..)\n\n"
|
"module A exposing (..)\n\n"
|
||||||
++ string
|
++ string
|
||||||
|> Review.Test.run rule
|
|> Review.Test.runWithProjectData project rule
|
||||||
|
|
||||||
|
|
||||||
|
errorDetails : { message : String, details : List String, under : String }
|
||||||
|
errorDetails =
|
||||||
|
{ message = message
|
||||||
|
, details = details
|
||||||
|
, under = "Debug.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
project : Project
|
||||||
|
project =
|
||||||
|
Project.addDependency Dependencies.ElmCore.dependency Project.new
|
||||||
|
|
||||||
|
|
||||||
|
whenFixed : String -> Review.Test.ExpectedError -> Review.Test.ExpectedError
|
||||||
|
whenFixed string =
|
||||||
|
Review.Test.whenFixed ("module A exposing (..)\n\n" ++ string)
|
||||||
|
|
||||||
|
|
||||||
message : String
|
message : String
|
||||||
@ -47,21 +67,13 @@ b = Debug.todo ""
|
|||||||
\() ->
|
\() ->
|
||||||
testRule "a = Debug.log"
|
testRule "a = Debug.log"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log calls" <|
|
, test "should report Debug.log calls" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = Debug.log z"
|
testRule "a = Debug.log z"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report multiple Debug.log calls" <|
|
, test "should report multiple Debug.log calls" <|
|
||||||
\() ->
|
\() ->
|
||||||
@ -70,170 +82,176 @@ a = Debug.log z
|
|||||||
b = Debug.log z
|
b = Debug.log z
|
||||||
"""
|
"""
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
|> Review.Test.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }
|
|> Review.Test.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }
|
||||||
, Review.Test.error
|
, Review.Test.error errorDetails
|
||||||
{ message = message
|
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
|> Review.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }
|
|> Review.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in a binary expression" <|
|
, test "should report Debug.log in a binary expression" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = ( Debug.log z ) + 2"
|
testRule "a = ( Debug.log z ) + 2"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in a << binary expression" <|
|
, test "should report Debug.log in a << binary expression (on the right)" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = fn << Debug.log"
|
testRule "a = fn << Debug.log b"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = fn"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in a pipe expression" <|
|
, test "should report Debug.log in a << binary expression (on the left)" <|
|
||||||
|
\() ->
|
||||||
|
testRule "a = Debug.log b << fn"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> whenFixed "a = fn"
|
||||||
|
]
|
||||||
|
, test "should report Debug.log in a >> binary expression (on the right)" <|
|
||||||
|
\() ->
|
||||||
|
testRule "a = fn >> Debug.log b"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> whenFixed "a = fn"
|
||||||
|
]
|
||||||
|
, test "should report Debug.log in a >> binary expression (on the left)" <|
|
||||||
|
\() ->
|
||||||
|
testRule "a = Debug.log b >> fn"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> whenFixed "a = fn"
|
||||||
|
]
|
||||||
|
, test "should report Debug.log in a |> pipe expression" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = fn |> Debug.log z"
|
testRule "a = fn |> Debug.log z"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = fn"
|
||||||
, details = details
|
]
|
||||||
, under = "Debug.log"
|
, test "should report Debug.log in a large |> pipe expression (pipes on each side)" <|
|
||||||
}
|
\() ->
|
||||||
|
testRule "a = fn |> Debug.log z |> fn2"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> whenFixed "a = fn |> fn2"
|
||||||
|
]
|
||||||
|
, test "should report Debug.log multiple times in a pipeline expression with multiple Debug.log calls" <|
|
||||||
|
\() ->
|
||||||
|
testRule "a = fn |> Debug.log z |> fn2 |> Debug.log y"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> Review.Test.atExactly { start = { row = 3, column = 11 }, end = { row = 3, column = 20 } }
|
||||||
|
|> whenFixed "a = fn |> fn2 |> Debug.log y"
|
||||||
|
, Review.Test.error errorDetails
|
||||||
|
|> Review.Test.atExactly { start = { row = 3, column = 33 }, end = { row = 3, column = 42 } }
|
||||||
|
|> whenFixed "a = fn |> Debug.log z |> fn2"
|
||||||
|
]
|
||||||
|
, test "should report Debug.log in a <| pipe expression" <|
|
||||||
|
\() ->
|
||||||
|
testRule "a = Debug.log z <| fn"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> whenFixed "a = fn"
|
||||||
|
]
|
||||||
|
, test "should report Debug.log in a large <| pipe expression" <|
|
||||||
|
\() ->
|
||||||
|
testRule "a = foo <| Debug.log z <| fn"
|
||||||
|
|> Review.Test.expectErrors
|
||||||
|
[ Review.Test.error errorDetails
|
||||||
|
|> whenFixed "a = foo <| fn"
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an list expression" <|
|
, test "should report Debug.log in an list expression" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = [ Debug.log z y ]"
|
testRule "a = [ Debug.log z y ]"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = [ y ]"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an record expression" <|
|
, test "should report Debug.log in a record expression" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = { foo = Debug.log z y }"
|
testRule "a = { foo = Debug.log z y }"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = { foo = y }"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an record update expression" <|
|
, test "should report Debug.log in a record update expression" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = { model | foo = Debug.log z y }"
|
testRule "a = { model | foo = Debug.log z y }"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = { model | foo = y }"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an lambda expression" <|
|
, test "should report Debug.log in an lambda expression" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = (\\foo -> Debug.log z foo)"
|
testRule "a = (\\foo -> Debug.log z foo)"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = (\\foo -> foo)"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an if expression condition" <|
|
, test "should report Debug.log in an if expression condition" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = if Debug.log a b then True else False"
|
testRule "a = if Debug.log a b then True else False"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = if b then True else False"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an if expression then branch" <|
|
, test "should report Debug.log in an if expression then branch" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = if True then Debug.log a b else False"
|
testRule "a = if True then Debug.log a b else False"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = if True then b else False"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in an if expression else branch" <|
|
, test "should report Debug.log in an if expression else branch" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule "a = if True then True else Debug.log a b"
|
testRule "a = if True then True else Debug.log a b"
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed "a = if True then True else b"
|
||||||
, details = details
|
|
||||||
, under = "Debug.log"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in a case value" <|
|
, test "should report Debug.log in a case value" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule """
|
testRule """
|
||||||
a = case Debug.log a b of
|
a = case Debug.log a b of
|
||||||
_ -> []
|
_ -> []"""
|
||||||
"""
|
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed """
|
||||||
, details = details
|
a = case b of
|
||||||
, under = "Debug.log"
|
_ -> []"""
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in a case body" <|
|
, test "should report Debug.log in a case body" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule """
|
testRule """
|
||||||
a = case a of
|
a = case a of
|
||||||
_ -> Debug.log a b
|
_ -> Debug.log a b"""
|
||||||
"""
|
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed """
|
||||||
, details = details
|
a = case a of
|
||||||
, under = "Debug.log"
|
_ -> b"""
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in let declaration section" <|
|
, test "should report Debug.log in let declaration section" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule """
|
testRule """
|
||||||
a = let b = Debug.log a b
|
a = let b = Debug.log a c
|
||||||
in b
|
in b"""
|
||||||
"""
|
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed """
|
||||||
, details = details
|
a = let b = c
|
||||||
, under = "Debug.log"
|
in b"""
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should report Debug.log in let body" <|
|
, test "should report Debug.log in let body" <|
|
||||||
\() ->
|
\() ->
|
||||||
testRule """
|
testRule """
|
||||||
a = let b = c
|
a = let b = c
|
||||||
in Debug.log a b
|
in Debug.log a b"""
|
||||||
"""
|
|
||||||
|> Review.Test.expectErrors
|
|> Review.Test.expectErrors
|
||||||
[ Review.Test.error
|
[ Review.Test.error errorDetails
|
||||||
{ message = message
|
|> whenFixed """
|
||||||
, details = details
|
a = let b = c
|
||||||
, under = "Debug.log"
|
in b"""
|
||||||
}
|
|
||||||
]
|
]
|
||||||
, test "should not report calls from a module containing Debug but that is not Debug" <|
|
, test "should not report calls from a module containing Debug but that is not Debug" <|
|
||||||
\() ->
|
\() ->
|
||||||
@ -259,6 +277,10 @@ a = log "" 1
|
|||||||
, under = "log"
|
, under = "log"
|
||||||
}
|
}
|
||||||
|> Review.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 8 } }
|
|> Review.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 8 } }
|
||||||
|
|> whenFixed """
|
||||||
|
import Debug exposing (log)
|
||||||
|
a = 1
|
||||||
|
"""
|
||||||
]
|
]
|
||||||
, test "should report the use of `log` when it has been implicitly imported" <|
|
, test "should report the use of `log` when it has been implicitly imported" <|
|
||||||
\() ->
|
\() ->
|
||||||
@ -272,6 +294,10 @@ a = log "" 1
|
|||||||
, details = details
|
, details = details
|
||||||
, under = "log"
|
, under = "log"
|
||||||
}
|
}
|
||||||
|
|> whenFixed """
|
||||||
|
import Debug exposing (..)
|
||||||
|
a = 1
|
||||||
|
"""
|
||||||
]
|
]
|
||||||
, test "should not report the use of `log` when it has not been imported" <|
|
, test "should not report the use of `log` when it has not been imported" <|
|
||||||
\() ->
|
\() ->
|
||||||
|
@ -6,10 +6,9 @@ module NoDebug.TodoOrToString exposing (rule)
|
|||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Elm.Syntax.Exposing as Exposing
|
|
||||||
import Elm.Syntax.Expression as Expression exposing (Expression)
|
import Elm.Syntax.Expression as Expression exposing (Expression)
|
||||||
import Elm.Syntax.Import exposing (Import)
|
|
||||||
import Elm.Syntax.Node as Node exposing (Node)
|
import Elm.Syntax.Node as Node exposing (Node)
|
||||||
|
import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable)
|
||||||
import Review.Rule as Rule exposing (Error, Rule)
|
import Review.Rule as Rule exposing (Error, Rule)
|
||||||
|
|
||||||
|
|
||||||
@ -84,96 +83,42 @@ elm-review --template jfmengels/elm-review-debug/example --rules NoDebug.TodoOrT
|
|||||||
-}
|
-}
|
||||||
rule : Rule
|
rule : Rule
|
||||||
rule =
|
rule =
|
||||||
Rule.newModuleRuleSchema "NoDebug.TodoOrToString" init
|
Rule.newModuleRuleSchemaUsingContextCreator "NoDebug.TodoOrToString" init
|
||||||
|> Rule.withImportVisitor importVisitor
|
|> Rule.withExpressionEnterVisitor expressionVisitor
|
||||||
|> Rule.withExpressionVisitor expressionVisitor
|
|
||||||
|> Rule.fromModuleRuleSchema
|
|> Rule.fromModuleRuleSchema
|
||||||
|
|
||||||
|
|
||||||
type alias Context =
|
type alias Context =
|
||||||
{ hasTodoBeenImported : Bool
|
ModuleNameLookupTable
|
||||||
, hasToStringBeenImported : Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init : Context
|
init : Rule.ContextCreator () Context
|
||||||
init =
|
init =
|
||||||
{ hasTodoBeenImported = False
|
Rule.initContextCreator (\lookupTable () -> lookupTable)
|
||||||
, hasToStringBeenImported = False
|
|> Rule.withModuleNameLookupTable
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
error : Node a -> String -> Error {}
|
expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )
|
||||||
error node name =
|
expressionVisitor node context =
|
||||||
Rule.error
|
case Node.value node of
|
||||||
|
Expression.FunctionOrValue _ name ->
|
||||||
|
if name == "todo" || name == "toString" then
|
||||||
|
case ModuleNameLookupTable.moduleNameFor context node of
|
||||||
|
Just [ "Debug" ] ->
|
||||||
|
( [ Rule.error
|
||||||
{ message = "Remove the use of `Debug." ++ name ++ "` before shipping to production"
|
{ message = "Remove the use of `Debug." ++ name ++ "` before shipping to production"
|
||||||
, details =
|
, details =
|
||||||
[ "`Debug." ++ name ++ "` can be useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production."
|
[ "`Debug." ++ name ++ "` can be useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production."
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
(Node.range node)
|
(Node.range node)
|
||||||
|
]
|
||||||
|
, context
|
||||||
importVisitor : Node Import -> Context -> ( List nothing, Context )
|
|
||||||
importVisitor node context =
|
|
||||||
let
|
|
||||||
moduleName : List String
|
|
||||||
moduleName =
|
|
||||||
node
|
|
||||||
|> Node.value
|
|
||||||
|> .moduleName
|
|
||||||
|> Node.value
|
|
||||||
in
|
|
||||||
if moduleName == [ "Debug" ] then
|
|
||||||
case node |> Node.value |> .exposingList |> Maybe.map Node.value of
|
|
||||||
Just (Exposing.All _) ->
|
|
||||||
( [], { hasTodoBeenImported = True, hasToStringBeenImported = True } )
|
|
||||||
|
|
||||||
Just (Exposing.Explicit importedNames) ->
|
|
||||||
( []
|
|
||||||
, { context
|
|
||||||
| hasTodoBeenImported = List.any (is "todo") importedNames
|
|
||||||
, hasToStringBeenImported = List.any (is "toString") importedNames
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
( [], context )
|
|
||||||
|
|
||||||
else
|
|
||||||
( [], context )
|
|
||||||
|
|
||||||
|
|
||||||
is : String -> Node Exposing.TopLevelExpose -> Bool
|
|
||||||
is name node =
|
|
||||||
case Node.value node of
|
|
||||||
Exposing.FunctionExpose functionName ->
|
|
||||||
name == functionName
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
False
|
|
||||||
|
|
||||||
|
|
||||||
expressionVisitor : Node Expression -> Rule.Direction -> Context -> ( List (Error {}), Context )
|
|
||||||
expressionVisitor node direction context =
|
|
||||||
case ( direction, Node.value node ) of
|
|
||||||
( Rule.OnEnter, Expression.FunctionOrValue [ "Debug" ] name ) ->
|
|
||||||
if name == "todo" then
|
|
||||||
( [ error node name ], context )
|
|
||||||
|
|
||||||
else if name == "toString" then
|
|
||||||
( [ error node name ], context )
|
|
||||||
|
|
||||||
else
|
|
||||||
( [], context )
|
( [], context )
|
||||||
|
|
||||||
( Rule.OnEnter, Expression.FunctionOrValue [] name ) ->
|
|
||||||
if name == "todo" && context.hasTodoBeenImported then
|
|
||||||
( [ error node name ], context )
|
|
||||||
|
|
||||||
else if name == "toString" && context.hasToStringBeenImported then
|
|
||||||
( [ error node name ], context )
|
|
||||||
|
|
||||||
else
|
else
|
||||||
( [], context )
|
( [], context )
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
module NoDebug.TodoOrToStringTest exposing (all)
|
module NoDebug.TodoOrToStringTest exposing (all)
|
||||||
|
|
||||||
|
import Dependencies.ElmCore
|
||||||
import NoDebug.TodoOrToString exposing (rule)
|
import NoDebug.TodoOrToString exposing (rule)
|
||||||
|
import Review.Project as Project exposing (Project)
|
||||||
import Review.Test exposing (ReviewResult)
|
import Review.Test exposing (ReviewResult)
|
||||||
import Test exposing (Test, describe, test)
|
import Test exposing (Test, describe, test)
|
||||||
|
|
||||||
@ -9,7 +11,12 @@ testRule : String -> ReviewResult
|
|||||||
testRule string =
|
testRule string =
|
||||||
"module A exposing (..)\n\n"
|
"module A exposing (..)\n\n"
|
||||||
++ string
|
++ string
|
||||||
|> Review.Test.run rule
|
|> Review.Test.runWithProjectData project rule
|
||||||
|
|
||||||
|
|
||||||
|
project : Project
|
||||||
|
project =
|
||||||
|
Project.addDependency Dependencies.ElmCore.dependency Project.new
|
||||||
|
|
||||||
|
|
||||||
todoMessage : String
|
todoMessage : String
|
||||||
|
Loading…
Reference in New Issue
Block a user