Added code to detect execution flows with long straight-line code flow graph segments. This is not captured in the cyclical code complexity calculations that we normally use to determine if code is too complex to analyze.

This commit is contained in:
Eric Traut 2022-05-13 20:40:39 -07:00
parent 3c6585971e
commit 063a205133
2 changed files with 28 additions and 2 deletions

View File

@ -93,6 +93,7 @@ import {
FlowLabel,
FlowNarrowForPattern,
FlowNode,
FlowNodeCounter,
FlowPostContextManagerLabel,
FlowPostFinally,
FlowPreFinallyGate,
@ -142,6 +143,12 @@ interface ClassVarInfo {
classVarTypeNode: ExpressionNode | undefined;
}
// For each flow node within an execution context, we'll add a small
// amount to the complexity factor. Without this, the complexity
// calculation fails to take into account large numbers of non-cyclical
// flow nodes. This number is somewhat arbitrary and is tuned empirically.
const flowNodeComplexityFactor = 0.05;
export class Binder extends ParseTreeWalker {
private readonly _fileInfo: AnalyzerFileInfo;
@ -248,6 +255,8 @@ export class Binder extends ParseTreeWalker {
isBuiltInModule ? ScopeType.Builtin : ScopeType.Module,
this._fileInfo.builtinsScope,
() => {
const flowNodeCounter = new FlowNodeCounter();
AnalyzerNodeInfo.setScope(node, this._currentScope);
AnalyzerNodeInfo.setFlowNode(node, this._currentFlowNode!);
@ -275,7 +284,10 @@ export class Binder extends ParseTreeWalker {
AnalyzerNodeInfo.setAfterFlowNode(node, this._currentFlowNode);
AnalyzerNodeInfo.setCodeFlowExpressions(node, this._currentScopeCodeFlowExpressions!);
AnalyzerNodeInfo.setCodeFlowComplexity(node, this._codeFlowComplexity);
AnalyzerNodeInfo.setCodeFlowComplexity(
node,
this._codeFlowComplexity + flowNodeCounter.getCount() * flowNodeComplexityFactor
);
}
);
@ -480,6 +492,7 @@ export class Binder extends ParseTreeWalker {
// Create a start node for the function.
this._currentFlowNode = this._createStartFlowNode();
this._codeFlowComplexity = 0;
const flowNodeCounter = new FlowNodeCounter();
node.parameters.forEach((paramNode) => {
if (paramNode.name) {
@ -525,7 +538,10 @@ export class Binder extends ParseTreeWalker {
AnalyzerNodeInfo.setAfterFlowNode(node, returnFlowNode);
AnalyzerNodeInfo.setCodeFlowExpressions(node, this._currentScopeCodeFlowExpressions!);
AnalyzerNodeInfo.setCodeFlowComplexity(node, this._codeFlowComplexity);
AnalyzerNodeInfo.setCodeFlowComplexity(
node,
this._codeFlowComplexity + flowNodeCounter.getCount() * flowNodeComplexityFactor
);
});
});

View File

@ -60,6 +60,16 @@ export function getUniqueFlowNodeId() {
return _nextFlowNodeId++;
}
export class FlowNodeCounter {
private _startingNodeId = _nextFlowNodeId;
// Return the number of flow nodes that have been allocated
// since the counter was allocated.
getCount() {
return _nextFlowNodeId - this._startingNodeId;
}
}
export interface FlowNode {
flags: FlowFlags;
id: number;