cfg

package
v0.2.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SortLineFindings

func SortLineFindings(findings []*LineFinding)

SortLineFindings sorts findings by start line, then end line, for consistent output and as the precondition of MergeContiguousFindings.

Types

type BasicBlock

type BasicBlock struct {
	ID    string
	Label string

	// Statements contains the AST nodes in this block.
	// Each element is a language-specific AST node stored as any.
	// Language adapters recover the concrete type via type assertion:
	//
	//   for _, stmt := range block.Statements {
	//       node := stmt.(*parser.Node) // pyscn or jscan
	//   }
	Statements []any

	Predecessors []*Edge
	Successors   []*Edge

	IsEntry bool
	IsExit  bool
}

BasicBlock represents a basic block in the control flow graph.

func NewBasicBlock

func NewBasicBlock(id string) *BasicBlock

NewBasicBlock creates a new basic block with the given ID.

func (*BasicBlock) AddStatement

func (bb *BasicBlock) AddStatement(stmt any)

AddStatement adds a statement to this block.

func (*BasicBlock) AddSuccessor

func (bb *BasicBlock) AddSuccessor(to *BasicBlock, edgeType EdgeType) *Edge

AddSuccessor adds an outgoing edge to another block.

func (*BasicBlock) IsEmpty

func (bb *BasicBlock) IsEmpty() bool

IsEmpty returns true if the block has no statements.

func (*BasicBlock) RemoveSuccessor

func (bb *BasicBlock) RemoveSuccessor(to *BasicBlock)

RemoveSuccessor removes an edge to the specified block.

func (*BasicBlock) String

func (bb *BasicBlock) String() string

String returns a string representation of the basic block.

type CFG

type CFG struct {
	Entry  *BasicBlock
	Exit   *BasicBlock
	Blocks map[string]*BasicBlock
	Name   string

	// FunctionNode is the original AST node for the function.
	// This field is opaque to polyscan core; language adapters store their
	// own function node type here and recover it via type assertion:
	//
	//   fnNode := cfg.FunctionNode.(*parser.Node)
	FunctionNode any
	// contains filtered or unexported fields
}

CFG represents a control flow graph.

func NewCFG

func NewCFG(name string) *CFG

NewCFG creates a new control flow graph.

func (*CFG) AddBlock

func (c *CFG) AddBlock(block *BasicBlock)

AddBlock adds an existing block to the graph.

func (*CFG) BreadthFirstWalk

func (c *CFG) BreadthFirstWalk(visitor Visitor)

BreadthFirstWalk performs a breadth-first traversal of the CFG.

func (*CFG) ConnectBlocks

func (c *CFG) ConnectBlocks(from, to *BasicBlock, edgeType EdgeType) *Edge

ConnectBlocks creates an edge between two blocks.

func (*CFG) CreateBlock

func (c *CFG) CreateBlock(label string) *BasicBlock

CreateBlock creates a new basic block and adds it to the graph.

func (*CFG) GetBlock

func (c *CFG) GetBlock(id string) *BasicBlock

GetBlock retrieves a block by its ID.

func (*CFG) RemoveBlock

func (c *CFG) RemoveBlock(block *BasicBlock)

RemoveBlock removes a block from the graph.

func (*CFG) Size

func (c *CFG) Size() int

Size returns the number of blocks in the graph.

func (*CFG) String

func (c *CFG) String() string

String returns a string representation of the CFG.

func (*CFG) Walk

func (c *CFG) Walk(visitor Visitor)

Walk performs a depth-first traversal of the CFG.

type ComplexityConfig

type ComplexityConfig struct {
	// Contributor provides language-specific extra complexity contributions.
	// If nil, no extra contributions are added.
	Contributor ComplexityContributor
}

ComplexityConfig configures complexity computation.

type ComplexityContribution

type ComplexityContribution struct {
	Count       int
	Description string // e.g. "logical_and", "ternary", "null_coalescing"
}

ComplexityContribution represents a single language-specific complexity contribution.

type ComplexityContributor

type ComplexityContributor interface {
	ContributeComplexity(block *BasicBlock) ([]ComplexityContribution, error)
}

ComplexityContributor provides language-specific additional complexity counts. For example, jscan counts logical operators (&&, ||, ??) and ternary expressions.

type ComplexityResult

type ComplexityResult struct {
	McCabe             int
	DecisionPoints     int
	ExtraContributions int
	Contributions      []ComplexityContribution
	EdgeBreakdown      map[EdgeType]int
}

ComplexityResult holds McCabe cyclomatic complexity analysis results.

func ComputeComplexity

func ComputeComplexity(c *CFG, config ComplexityConfig) (*ComplexityResult, error)

ComputeComplexity computes McCabe cyclomatic complexity for a CFG. A block whose outgoing EdgeCondTrue/EdgeCondFalse edges lead to k distinct branch targets is a k-way branch and contributes k-1 decision points: an if-else (true + false) or a loop header (body + exit) contributes one, while a switch/match that emits one edge per case plus a no-match edge contributes one per case. A block with any EdgeException successor contributes one more, for the implicit raise-or-not branch. EdgeLoop is a back-edge and does not count as a decision point; loop headers should use EdgeCondTrue/EdgeCondFalse for the loop-body vs exit branch. McCabe = DecisionPoints + ExtraContributions + 1.

type DeadCodeConfig

type DeadCodeConfig struct {
	// Classifier provides language-specific statement classification.
	// If nil, only structural analysis (unreachable blocks) is performed.
	Classifier StatementClassifier
}

DeadCodeConfig configures dead code detection.

type DeadCodeFinding

type DeadCodeFinding struct {
	BlockID  string
	Severity DeadCodeSeverity
	Reason   string // "after_return", "after_break", "after_continue", "after_throw", "unreachable"
}

DeadCodeFinding represents a single dead code detection result.

type DeadCodeResult

type DeadCodeResult struct {
	Findings    []*DeadCodeFinding
	TotalBlocks int
	DeadBlocks  int
}

DeadCodeResult holds all dead code findings for a CFG.

func DetectDeadCode

func DetectDeadCode(c *CFG, config DeadCodeConfig) *DeadCodeResult

DetectDeadCode identifies dead code in a CFG. It uses AnalyzeReachability to find unreachable blocks, then examines reachable blocks for code after terminators (return/break/continue/throw).

type DeadCodeSeverity

type DeadCodeSeverity int

DeadCodeSeverity indicates the severity of a dead code finding.

const (
	SeverityInfo     DeadCodeSeverity = iota // after_break, after_continue
	SeverityWarning                          // after_return, after_throw
	SeverityCritical                         // unreachable
)

type Edge

type Edge struct {
	From  *BasicBlock
	To    *BasicBlock
	Type  EdgeType
	Label string // Language-specific flow description (e.g. switch case value, yield, await).
	Data  any    // Optional language-specific edge metadata.
}

Edge represents a directed edge between two basic blocks.

type EdgeType

type EdgeType int

EdgeType represents the type of edge between basic blocks.

const (
	EdgeNormal    EdgeType = iota // Normal sequential flow
	EdgeCondTrue                  // Conditional true branch
	EdgeCondFalse                 // Conditional false branch
	EdgeException                 // Exception flow
	EdgeLoop                      // Loop back edge
	EdgeBreak                     // Break statement flow
	EdgeContinue                  // Continue statement flow
	EdgeReturn                    // Return statement flow
)

func (EdgeType) String

func (e EdgeType) String() string

String returns string representation of EdgeType.

type LineFinding

type LineFinding struct {
	StartLine   int
	EndLine     int
	Reason      string
	Severity    DeadCodeSeverity
	Description string
	Code        string
}

LineFinding is the language-independent, line-level slice of a dead code finding used by post-processing passes. Language adapters convert their richer finding types to and from this representation (or embed it).

func MergeContiguousFindings

func MergeContiguousFindings(findings []*LineFinding) []*LineFinding

MergeContiguousFindings collapses findings whose line ranges overlap or are directly adjacent (no reachable line between them) and that share the same reason into a single finding. Findings must be pre-sorted by StartLine (then EndLine); use SortLineFindings. This removes the overlapping/duplicate ranges that arise because a compound statement's finding spans its body while the body's block emits its own nested finding.

type NoOpClassifier

type NoOpClassifier interface {
	IsNoOp(stmt any) bool
}

NoOpClassifier is an optional StatementClassifier extension reporting statements with no actionable content (e.g. a bare `;` in JS/TS or `pass` in Python). Unreachable blocks consisting solely of no-op statements are technically dead but carry no signal for the user, so they are not reported as findings.

type ReachabilityConfig

type ReachabilityConfig struct {
	// Classifier provides language-specific statement classification.
	// If nil, only structural reachability (DFS from entry) is computed.
	Classifier StatementClassifier
}

ReachabilityConfig configures reachability analysis.

type ReachabilityResult

type ReachabilityResult struct {
	Reachable        map[string]bool // blockID -> reachable from entry
	ReachableCount   int
	UnreachableCount int
}

ReachabilityResult holds the result of reachability analysis.

func AnalyzeReachability

func AnalyzeReachability(c *CFG, config ReachabilityConfig) *ReachabilityResult

AnalyzeReachability performs reachability analysis on a CFG. If config.Classifier is nil, only structural reachability (DFS from entry) is computed, following all edges. If config.Classifier is non-nil, normal fallthrough edges from blocks containing a terminator (return/break/continue/throw) are not followed. Explicit control-transfer edges such as exception, return, break, and continue remain traversable. Pruned successors are still reachable if another path leads to them.

type StatementClassifier

type StatementClassifier interface {
	IsReturn(stmt any) bool
	IsBreak(stmt any) bool
	IsContinue(stmt any) bool
	IsThrow(stmt any) bool
}

StatementClassifier classifies statements in a BasicBlock. Language-specific implementations check for terminator statements: e.g. pyscn checks parser.NodeReturn, jscan checks parser.NodeReturnStatement, etc.

type Visitor

type Visitor interface {
	VisitBlock(block *BasicBlock) bool
	VisitEdge(edge *Edge) bool
}

Visitor defines the interface for visiting CFG nodes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL