Backport rules from cognitive-complexity

This commit is contained in:
Jeroen Engels 2023-09-03 11:45:53 +02:00
parent cf2a955d40
commit 60a15216a9
2 changed files with 135 additions and 77 deletions

View File

@ -337,32 +337,7 @@ expressionEnterVisitorHelp : Node Expression -> ModuleContext -> ModuleContext
expressionEnterVisitorHelp node context = expressionEnterVisitorHelp node context =
case Node.value node of case Node.value node of
Expression.IfBlock _ _ else_ -> Expression.IfBlock _ _ else_ ->
if not (List.member (Node.range node) context.elseIfToIgnore) then visitElseExpression (Node.range node) else_ context
{ context
| increases =
{ line = (Node.range node).start
, increase = context.nesting + 1
, nesting = context.nesting
, kind = If
}
:: context.increases
, nesting = context.nesting + 1
, elseIfToIgnore = Node.range else_ :: context.elseIfToIgnore
}
else
-- This if expression is an else if
-- We want to increase the complexity but keep the same nesting as the parent if
{ context
| increases =
{ line = (Node.range node).start
, increase = context.nesting
, nesting = context.nesting - 1
, kind = ElseIf
}
:: context.increases
, elseIfToIgnore = Node.range else_ :: context.elseIfToIgnore
}
Expression.CaseExpression _ -> Expression.CaseExpression _ ->
{ context { context
@ -408,21 +383,62 @@ expressionEnterVisitorHelp node context =
{ context | nesting = context.nesting + 1 } { context | nesting = context.nesting + 1 }
Expression.FunctionOrValue [] name -> Expression.FunctionOrValue [] name ->
{ context if isFunctionReference name then
| references = { context
if Dict.member name context.references then | references =
-- The reference already exists, and we want to keep the first reference if Dict.member name context.references then
-- for a better presentation -- The reference already exists, and we want to keep the first reference
context.references -- for a better presentation
context.references
else else
Dict.insert name (Node.range node).start context.references Dict.insert name (Node.range node).start context.references
} }
else
context
_ -> _ ->
context context
visitElseExpression : Range -> Node a -> ModuleContext -> ModuleContext
visitElseExpression ifExprRange else_ context =
if not (List.member ifExprRange context.elseIfToIgnore) then
{ context
| increases =
{ line = ifExprRange.start
, increase = context.nesting + 1
, nesting = context.nesting
, kind = If
}
:: context.increases
, nesting = context.nesting + 1
, elseIfToIgnore = Node.range else_ :: context.elseIfToIgnore
}
else
-- This if expression is an else if
-- We want to increase the complexity but keep the same nesting as the parent if
{ context
| increases =
{ line = ifExprRange.start
, increase = context.nesting
, nesting = context.nesting - 1
, kind = ElseIf
}
:: context.increases
, elseIfToIgnore = Node.range else_ :: context.elseIfToIgnore
}
isFunctionReference : String -> Bool
isFunctionReference name =
name
|> String.left 1
|> String.all Char.isLower
computeRangesForLetDeclarations : List (Node Expression.LetDeclaration) -> List Range computeRangesForLetDeclarations : List (Node Expression.LetDeclaration) -> List Range
computeRangesForLetDeclarations declarations = computeRangesForLetDeclarations declarations =
List.filterMap List.filterMap
@ -704,19 +720,18 @@ findRecursiveCalls : Dict String (Dict String a) -> RecursiveCalls
findRecursiveCalls graph = findRecursiveCalls graph =
graph graph
|> Dict.foldl |> Dict.foldl
(\vertice _ ( recursiveCalls, visited ) -> (\vertice _ recursiveCalls ->
let let
res : { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String } res : { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String }
res = res =
processDFSTree processDFSTree
graph graph
[ vertice ] [ vertice ]
(Dict.insert vertice InStack visited) (Dict.singleton vertice InStack)
in in
( mergeRecursiveCallsDict res.recursiveCalls recursiveCalls, res.visited ) mergeRecursiveCallsDict res.recursiveCalls recursiveCalls
) )
( Dict.empty, Dict.empty ) Dict.empty
|> Tuple.first
mergeRecursiveCallsDict : RecursiveCalls -> RecursiveCalls -> RecursiveCalls mergeRecursiveCallsDict : RecursiveCalls -> RecursiveCalls -> RecursiveCalls
@ -732,45 +747,53 @@ mergeRecursiveCallsDict left right =
processDFSTree : Dict String (Dict String a) -> List String -> Visited -> { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String } processDFSTree : Dict String (Dict String a) -> List String -> Visited -> { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String }
processDFSTree graph stack visited = processDFSTree graph stack visited =
let case stack of
vertices : List String [] ->
vertices = { recursiveCalls = Dict.empty, visited = visited, stack = [] }
List.head stack
|> Maybe.andThen (\v -> Dict.get v graph)
|> Maybe.withDefault Dict.empty
|> Dict.keys
in
List.foldl
(\vertice acc ->
case Dict.get vertice visited of
Just InStack ->
{ acc | recursiveCalls = insertCycle stack vertice acc.recursiveCalls }
Just Done -> head :: restOfStack ->
acc let
vertices : List String
vertices =
Dict.get head graph
|> Maybe.withDefault Dict.empty
|> Dict.keys
in
List.foldl
(\vertice acc ->
case Dict.get vertice visited of
Just InStack ->
{ acc | recursiveCalls = insertCycle stack vertice acc.recursiveCalls }
Nothing -> Just Done ->
let acc
res : { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String }
res = Nothing ->
processDFSTree let
graph res : { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String }
(vertice :: stack) res =
(Dict.insert vertice InStack visited) processDFSTree
in graph
{ recursiveCalls = mergeRecursiveCallsDict res.recursiveCalls acc.recursiveCalls, visited = res.visited } (vertice :: stack)
) (Dict.insert vertice InStack visited)
{ recursiveCalls = Dict.empty, visited = visited } in
vertices { recursiveCalls = mergeRecursiveCallsDict res.recursiveCalls acc.recursiveCalls, visited = res.visited }
|> (\res -> )
{ recursiveCalls = res.recursiveCalls { recursiveCalls = Dict.empty, visited = visited }
, visited = vertices
List.head stack |> updateStack head restOfStack
|> Maybe.map (\v -> Dict.insert v Done res.visited)
|> Maybe.withDefault res.visited
, stack = List.drop 1 stack updateStack :
} String
) -> List String
-> { recursiveCalls : RecursiveCalls, visited : Visited }
-> { recursiveCalls : RecursiveCalls, visited : Visited, stack : List String }
updateStack head stack res =
{ recursiveCalls = res.recursiveCalls
, visited = Dict.insert head Done res.visited
, stack = stack
}
dataExtractor : ProjectContext -> Encode.Value dataExtractor : ProjectContext -> Encode.Value

View File

@ -611,6 +611,41 @@ fun5 n =
"fun5": 1 "fun5": 1
} }
}""" }"""
, test "recursive call complexity should not depend on alphabetical order" <|
\() ->
"""module A exposing (..)
b () = b ()
a = b ()
c = b ()
"""
|> expectAtExactly
[ { name = "a"
, complexity = 1
, atExactly = { start = { row = 5, column = 1 }, end = { row = 5, column = 2 } }
, details = [ "Line 5: +1 for the indirect recursive call to b" ]
}
, { name = "b"
, complexity = 1
, atExactly = { start = { row = 3, column = 1 }, end = { row = 3, column = 2 } }
, details = [ "Line 3: +1 for the recursive call" ]
}
, { name = "c"
, complexity = 1
, atExactly = { start = { row = 7, column = 1 }, end = { row = 7, column = 2 } }
, details = [ "Line 7: +1 for the indirect recursive call to b" ]
}
]
"""
{
"A": {
"a": 1,
"b": 1,
"c": 1
}
}"""
, test "the complexity of a function should not affect another function's computed complexity" <| , test "the complexity of a function should not affect another function's computed complexity" <|
\() -> \() ->
"""module A exposing (..) """module A exposing (..)