Documentation
¶
Index ¶
- func SortLineFindings(findings []*LineFinding)
- type BasicBlock
- type CFG
- func (c *CFG) AddBlock(block *BasicBlock)
- func (c *CFG) BreadthFirstWalk(visitor Visitor)
- func (c *CFG) ConnectBlocks(from, to *BasicBlock, edgeType EdgeType) *Edge
- func (c *CFG) CreateBlock(label string) *BasicBlock
- func (c *CFG) GetBlock(id string) *BasicBlock
- func (c *CFG) RemoveBlock(block *BasicBlock)
- func (c *CFG) Size() int
- func (c *CFG) String() string
- func (c *CFG) Walk(visitor Visitor)
- type ComplexityConfig
- type ComplexityContribution
- type ComplexityContributor
- type ComplexityResult
- type DeadCodeConfig
- type DeadCodeFinding
- type DeadCodeResult
- type DeadCodeSeverity
- type Edge
- type EdgeType
- type LineFinding
- type NoOpClassifier
- type ReachabilityConfig
- type ReachabilityResult
- type StatementClassifier
- type Visitor
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 (*CFG) AddBlock ¶
func (c *CFG) AddBlock(block *BasicBlock)
AddBlock adds an existing block to the graph.
func (*CFG) BreadthFirstWalk ¶
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.
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 )
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 ¶
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.