cfg

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 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 decision point is a block that has at least one outgoing edge of type EdgeCondTrue, EdgeCondFalse, or EdgeException. Each such block counts as exactly one decision point regardless of how many decision edges it has (e.g. an if-else has both EdgeCondTrue and EdgeCondFalse but is one decision point). 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