Documentation
¶
Overview ¶
Package rules provides the core types and interfaces for goperf's static analysis rules. Each rule detects specific performance anti-patterns in Go source code.
Rules are organized by category (algorithm, allocation, database, etc.) and registered via the RegisterRule function during package initialization.
Example usage:
analyzer := rules.NewAnalyzer(rules.AnalyzerConfig{
Rules: []string{"algorithm", "database"},
Context: 3,
})
issues, _ := analyzer.Analyze("./...")
Index ¶
- Constants
- Variables
- func ExtractContext(src []byte, pos token.Position, contextLines int) []string
- func FindMutexHotspots(file *ast.File, fset *token.FileSet) map[string]int
- func FindNestedRangeLoops(file *ast.File, fset *token.FileSet) []ast.Node
- func FindStringConcatInLoop(file *ast.File, fset *token.FileSet) []token.Position
- func FindUnbufferedChannels(file *ast.File, fset *token.FileSet) []token.Position
- func RegisterRule(category string, rule Rule)
- func ValidateASTComplexity(file *ast.File)
- type ASTComplexityValidator
- type Analyzer
- type AnalyzerConfig
- type AppendInLoopInfo
- type BenchmarkSuggestionRule
- type ContextBackgroundInHandlerRule
- type ContextLeakRule
- type ErrorWrapInLoopRule
- type EscapeToHeapRule
- type FmtErrorfInLoopRule
- type GoroutineLeakRule
- type HTTPClientCreationRule
- type IgnoreSet
- type IndirectSQLInLoopRule
- type InterfaceBoxingInLoopRule
- type Issue
- type JSONInLoopInfo
- type JSONInLoopRule
- type JSONSchemaValidationRule
- type LargeStructCopyRule
- type LinearSearchInLoopRule
- type MapWithoutSizeRule
- type MissingBodyCloseRule
- type MissingConnectionPoolRule
- type MissingContextTimeoutRule
- type MissingMaxBytesReaderRule
- type MutexInLoopRule
- type NestedRangeRule
- type PprofInHotPathRule
- type ReadAllRule
- type ReflectionInLoopRule
- type RegexpMatchStringRule
- type RepeatedRegexpCompileRule
- type RepeatedTemplateParseRule
- type ResponseWriterBufferingRule
- type Rule
- type SQLInLoopInfo
- type SQLInLoopRule
- type Severity
- type StringConcatInLoopRule
- type SyncPoolOpportunityRule
- type TimeFormatInLoopRule
- type TimeLocationInLoopRule
- type TimeParseInLoopRule
- type TypeAssertionInLoopRule
- type UnbatchedInsertRule
- type UnbufferedChannelRule
- type UnlimitedConnectionPoolRule
- type UnpreallocatedSliceRule
- type VariadicInterfaceRule
Constants ¶
const ( ParseTimeout = 5 * time.Second MaxASTNodes = 100000 MaxASTDepth = 1000 )
Variables ¶
var RuleRegistry = make(map[string][]Rule)
RuleRegistry maps category names to the rules in that category. Rules are added to this registry via RegisterRule during init().
Functions ¶
func ExtractContext ¶
ExtractContext returns lines of code surrounding the given position. It returns up to contextLines lines before and after the specified position.
func FindMutexHotspots ¶
FindMutexHotspots counts mutex lock calls per function
func FindNestedRangeLoops ¶
FindNestedRangeLoops finds nested for-range loops
func FindStringConcatInLoop ¶
FindStringConcatInLoop finds string concatenation in loops
func FindUnbufferedChannels ¶
FindUnbufferedChannels finds unbuffered channel creation
func RegisterRule ¶
RegisterRule adds a rule to the registry under the specified category. This should be called from init() functions in rule implementation files.
func ValidateASTComplexity ¶
Types ¶
type ASTComplexityValidator ¶
type ASTComplexityValidator struct {
// contains filtered or unexported fields
}
ASTComplexityValidator enforces node count and depth limits during AST walks.
type Analyzer ¶
type Analyzer struct {
Errors []string
// contains filtered or unexported fields
}
Analyzer runs rules against Go source files
func NewAnalyzer ¶
func NewAnalyzer(config AnalyzerConfig) *Analyzer
NewAnalyzer creates a new analyzer with the given config
type AnalyzerConfig ¶
type AnalyzerConfig struct {
// Rules is a list of rule categories to run.
// An empty list means all rules. Example: []string{"algorithm", "database"}
Rules []string
// IgnorePaths is a list of path patterns to skip during analysis.
// Supports glob patterns. Example: []string{"vendor/**", "**/*_test.go"}
IgnorePaths []string
// Context is the number of lines of code to include around each issue.
Context int
// Verbose enables detailed output during analysis.
Verbose bool
}
AnalyzerConfig configures the behavior of the analyzer.
type AppendInLoopInfo ¶
func FindAppendInLoop ¶
func FindAppendInLoop(file *ast.File, fset *token.FileSet) []AppendInLoopInfo
FindAppendInLoop finds append calls inside loops without preallocation
type BenchmarkSuggestionRule ¶
type BenchmarkSuggestionRule struct{}
BenchmarkSuggestionRule suggests benchmarks for functions with detected issues
func (*BenchmarkSuggestionRule) Category ¶
func (r *BenchmarkSuggestionRule) Category() string
func (*BenchmarkSuggestionRule) Name ¶
func (r *BenchmarkSuggestionRule) Name() string
type ContextBackgroundInHandlerRule ¶
type ContextBackgroundInHandlerRule struct{}
ContextBackgroundInHandlerRule detects context.Background() in HTTP handlers
func (*ContextBackgroundInHandlerRule) Category ¶
func (r *ContextBackgroundInHandlerRule) Category() string
func (*ContextBackgroundInHandlerRule) Name ¶
func (r *ContextBackgroundInHandlerRule) Name() string
type ContextLeakRule ¶
type ContextLeakRule struct{}
ContextLeakRule detects context cancel functions that aren't called
func (*ContextLeakRule) Category ¶
func (r *ContextLeakRule) Category() string
func (*ContextLeakRule) Name ¶
func (r *ContextLeakRule) Name() string
type ErrorWrapInLoopRule ¶
type ErrorWrapInLoopRule struct{}
ErrorWrapInLoopRule detects error wrapping in hot paths
func (*ErrorWrapInLoopRule) Category ¶
func (r *ErrorWrapInLoopRule) Category() string
func (*ErrorWrapInLoopRule) Name ¶
func (r *ErrorWrapInLoopRule) Name() string
type EscapeToHeapRule ¶
type EscapeToHeapRule struct{}
EscapeToHeapRule detects patterns that likely cause heap escapes
func (*EscapeToHeapRule) Category ¶
func (r *EscapeToHeapRule) Category() string
func (*EscapeToHeapRule) Name ¶
func (r *EscapeToHeapRule) Name() string
type FmtErrorfInLoopRule ¶
type FmtErrorfInLoopRule struct{}
FmtErrorfInLoopRule specifically detects fmt.Errorf with %w verb (error wrapping)
func (*FmtErrorfInLoopRule) Category ¶
func (r *FmtErrorfInLoopRule) Category() string
func (*FmtErrorfInLoopRule) Name ¶
func (r *FmtErrorfInLoopRule) Name() string
type GoroutineLeakRule ¶
type GoroutineLeakRule struct{}
GoroutineLeakRule detects goroutines started without clear termination
func (*GoroutineLeakRule) Category ¶
func (r *GoroutineLeakRule) Category() string
func (*GoroutineLeakRule) Name ¶
func (r *GoroutineLeakRule) Name() string
type HTTPClientCreationRule ¶
type HTTPClientCreationRule struct{}
HTTPClientCreationRule detects http.Client{} created inside functions (not reused)
func (*HTTPClientCreationRule) Category ¶
func (r *HTTPClientCreationRule) Category() string
func (*HTTPClientCreationRule) Name ¶
func (r *HTTPClientCreationRule) Name() string
type IgnoreSet ¶
type IgnoreSet struct {
// contains filtered or unexported fields
}
IgnoreSet tracks which lines should be ignored based on perf:ignore comments. This allows developers to suppress specific warnings when they've verified the code is intentional or when there's a false positive.
func NewIgnoreSet ¶
NewIgnoreSet parses source code to find perf:ignore comments. It supports several ignore patterns:
Line-level ignore (ignores the current and next line):
// perf:ignore
for _, item := range items {
db.Exec(query, item) // This line is ignored
}
Same-line ignore:
db.Exec(query, item) // perf:ignore
Rule-specific ignore:
// perf:ignore sql-in-loop
for _, item := range items {
db.Exec(query, item) // Only sql-in-loop is ignored
}
Block ignore (ignores all lines between start and end):
// perf:ignore-start
for _, item := range items {
db.Exec(query, item)
}
// perf:ignore-end
type IndirectSQLInLoopRule ¶
type IndirectSQLInLoopRule struct{}
IndirectSQLInLoopRule detects when functions containing SQL are called in loops This catches the N+1 pattern even when SQL is wrapped in helper functions
func (*IndirectSQLInLoopRule) Category ¶
func (r *IndirectSQLInLoopRule) Category() string
func (*IndirectSQLInLoopRule) Name ¶
func (r *IndirectSQLInLoopRule) Name() string
type InterfaceBoxingInLoopRule ¶
type InterfaceBoxingInLoopRule struct{}
InterfaceBoxingInLoopRule detects interface{} assignments in loops
func (*InterfaceBoxingInLoopRule) Category ¶
func (r *InterfaceBoxingInLoopRule) Category() string
func (*InterfaceBoxingInLoopRule) Name ¶
func (r *InterfaceBoxingInLoopRule) Name() string
type Issue ¶
type Issue struct {
// Rule is the identifier of the rule that detected this issue
// (e.g., "nested-loop", "append-in-loop", "sql-in-loop").
Rule string `json:"rule"`
// Category groups related rules together
// (e.g., "algorithm", "allocation", "database").
Category string `json:"category"`
// Severity indicates the impact level of this issue.
Severity Severity `json:"severity"`
// File is the path to the source file containing the issue.
File string `json:"file"`
// Line is the 1-indexed line number where the issue occurs.
Line int `json:"line"`
// Column is the 1-indexed column number where the issue occurs.
Column int `json:"column"`
// Message is a brief description of the detected issue.
Message string `json:"message"`
// Why explains the performance impact of this pattern.
Why string `json:"why"`
// Fix suggests how to resolve the issue.
Fix string `json:"fix"`
// CodeSnippet contains the problematic line of code.
CodeSnippet string `json:"code_snippet,omitempty"`
// Context contains surrounding lines of code for display.
Context []string `json:"context,omitempty"`
}
Issue represents a detected performance anti-pattern in source code. Each issue includes location information, a description, an explanation of why it's a problem, and a suggested fix.
type JSONInLoopInfo ¶
func FindJSONInLoop ¶
func FindJSONInLoop(file *ast.File, fset *token.FileSet) []JSONInLoopInfo
FindJSONInLoop finds JSON marshal/unmarshal calls in loops
type JSONInLoopRule ¶
type JSONInLoopRule struct{}
JSONInLoopRule detects JSON marshal/unmarshal in loops Now smarter: recognizes json.Encoder which reuses reflection cache
func (*JSONInLoopRule) Category ¶
func (r *JSONInLoopRule) Category() string
func (*JSONInLoopRule) Name ¶
func (r *JSONInLoopRule) Name() string
type JSONSchemaValidationRule ¶
type JSONSchemaValidationRule struct{}
JSONSchemaValidationRule detects JSON schema validation in loops
func (*JSONSchemaValidationRule) Category ¶
func (r *JSONSchemaValidationRule) Category() string
func (*JSONSchemaValidationRule) Name ¶
func (r *JSONSchemaValidationRule) Name() string
type LargeStructCopyRule ¶
type LargeStructCopyRule struct{}
LargeStructCopyRule detects passing large structs by value
func (*LargeStructCopyRule) Category ¶
func (r *LargeStructCopyRule) Category() string
func (*LargeStructCopyRule) Name ¶
func (r *LargeStructCopyRule) Name() string
type LinearSearchInLoopRule ¶
type LinearSearchInLoopRule struct{}
LinearSearchInLoopRule detects repeated linear searches that should use maps
func (*LinearSearchInLoopRule) Category ¶
func (r *LinearSearchInLoopRule) Category() string
func (*LinearSearchInLoopRule) Name ¶
func (r *LinearSearchInLoopRule) Name() string
type MapWithoutSizeRule ¶
type MapWithoutSizeRule struct{}
MapWithoutSizeRule detects map creation without size hint when populated in a loop
func (*MapWithoutSizeRule) Category ¶
func (r *MapWithoutSizeRule) Category() string
func (*MapWithoutSizeRule) Name ¶
func (r *MapWithoutSizeRule) Name() string
type MissingBodyCloseRule ¶
type MissingBodyCloseRule struct{}
MissingBodyCloseRule detects HTTP response bodies that aren't closed
func (*MissingBodyCloseRule) Category ¶
func (r *MissingBodyCloseRule) Category() string
func (*MissingBodyCloseRule) Name ¶
func (r *MissingBodyCloseRule) Name() string
type MissingConnectionPoolRule ¶
type MissingConnectionPoolRule struct{}
MissingConnectionPoolRule detects sql.Open without pool configuration
func (*MissingConnectionPoolRule) Category ¶
func (r *MissingConnectionPoolRule) Category() string
func (*MissingConnectionPoolRule) Name ¶
func (r *MissingConnectionPoolRule) Name() string
type MissingContextTimeoutRule ¶
type MissingContextTimeoutRule struct{}
MissingContextTimeoutRule detects external calls without context timeout
func (*MissingContextTimeoutRule) Category ¶
func (r *MissingContextTimeoutRule) Category() string
func (*MissingContextTimeoutRule) Name ¶
func (r *MissingContextTimeoutRule) Name() string
type MissingMaxBytesReaderRule ¶
type MissingMaxBytesReaderRule struct{}
MissingMaxBytesReaderRule detects reading request body without size limit
func (*MissingMaxBytesReaderRule) Category ¶
func (r *MissingMaxBytesReaderRule) Category() string
func (*MissingMaxBytesReaderRule) Name ¶
func (r *MissingMaxBytesReaderRule) Name() string
type MutexInLoopRule ¶
type MutexInLoopRule struct{}
MutexInLoopRule detects mutex Lock() calls inside loops
func (*MutexInLoopRule) Category ¶
func (r *MutexInLoopRule) Category() string
func (*MutexInLoopRule) Name ¶
func (r *MutexInLoopRule) Name() string
type NestedRangeRule ¶
type NestedRangeRule struct{}
NestedRangeRule detects O(n²) nested range loops Now smarter: recognizes map-based optimizations
func (*NestedRangeRule) Category ¶
func (r *NestedRangeRule) Category() string
func (*NestedRangeRule) Name ¶
func (r *NestedRangeRule) Name() string
type PprofInHotPathRule ¶
type PprofInHotPathRule struct{}
PprofInHotPathRule detects pprof calls in hot paths
func (*PprofInHotPathRule) Category ¶
func (r *PprofInHotPathRule) Category() string
func (*PprofInHotPathRule) Name ¶
func (r *PprofInHotPathRule) Name() string
type ReadAllRule ¶
type ReadAllRule struct{}
ReadAllRule detects ioutil.ReadAll/io.ReadAll that could use streaming
func (*ReadAllRule) Category ¶
func (r *ReadAllRule) Category() string
func (*ReadAllRule) Name ¶
func (r *ReadAllRule) Name() string
type ReflectionInLoopRule ¶
type ReflectionInLoopRule struct{}
ReflectionInLoopRule detects reflection usage in loops (advanced)
func (*ReflectionInLoopRule) Category ¶
func (r *ReflectionInLoopRule) Category() string
func (*ReflectionInLoopRule) Name ¶
func (r *ReflectionInLoopRule) Name() string
type RegexpMatchStringRule ¶
type RegexpMatchStringRule struct{}
RegexpMatchStringRule detects regexp.MatchString in loops (compiles each time)
func (*RegexpMatchStringRule) Category ¶
func (r *RegexpMatchStringRule) Category() string
func (*RegexpMatchStringRule) Name ¶
func (r *RegexpMatchStringRule) Name() string
type RepeatedRegexpCompileRule ¶
type RepeatedRegexpCompileRule struct{}
RepeatedRegexpCompileRule detects regexp.Compile inside functions (should be package-level)
func (*RepeatedRegexpCompileRule) Category ¶
func (r *RepeatedRegexpCompileRule) Category() string
func (*RepeatedRegexpCompileRule) Name ¶
func (r *RepeatedRegexpCompileRule) Name() string
type RepeatedTemplateParseRule ¶
type RepeatedTemplateParseRule struct{}
RepeatedTemplateParseRule detects template.Parse inside functions
func (*RepeatedTemplateParseRule) Category ¶
func (r *RepeatedTemplateParseRule) Category() string
func (*RepeatedTemplateParseRule) Name ¶
func (r *RepeatedTemplateParseRule) Name() string
type ResponseWriterBufferingRule ¶
type ResponseWriterBufferingRule struct{}
ResponseWriterBufferingRule detects large writes to ResponseWriter without Flush
func (*ResponseWriterBufferingRule) Category ¶
func (r *ResponseWriterBufferingRule) Category() string
func (*ResponseWriterBufferingRule) Name ¶
func (r *ResponseWriterBufferingRule) Name() string
type Rule ¶
type Rule interface {
// Name returns a unique identifier for this rule (e.g., "append-in-loop").
Name() string
// Category returns the rule's category (e.g., "allocation", "database").
Category() string
// Check analyzes a Go source file and returns any detected issues.
// The file parameter is the parsed AST, fset provides position info,
// and src is the original source code for context extraction.
Check(file *ast.File, fset *token.FileSet, src []byte) []Issue
}
Rule is the interface that all detection rules must implement. Rules are the core building blocks of goperf's analysis engine.
To create a custom rule:
- Implement the Rule interface
- Register it using RegisterRule in an init() function
- The analyzer will automatically pick it up
Example:
type MyRule struct{}
func (r *MyRule) Name() string { return "my-pattern" }
func (r *MyRule) Category() string { return "custom" }
func (r *MyRule) Check(file *ast.File, fset *token.FileSet, src []byte) []Issue {
// Analyze the AST and return issues
return nil
}
func init() {
RegisterRule("custom", &MyRule{})
}
type SQLInLoopInfo ¶
func FindSQLInLoop ¶
func FindSQLInLoop(file *ast.File, fset *token.FileSet) []SQLInLoopInfo
FindSQLInLoop finds database query patterns inside loops
type SQLInLoopRule ¶
type SQLInLoopRule struct{}
SQLInLoopRule detects N+1 query patterns with smart prepared statement detection
func (*SQLInLoopRule) Category ¶
func (r *SQLInLoopRule) Category() string
func (*SQLInLoopRule) Name ¶
func (r *SQLInLoopRule) Name() string
type Severity ¶
type Severity int
Severity represents the impact level of a detected performance issue. Higher severity issues have greater performance impact and should be addressed with higher priority.
const ( // SeverityLow indicates a minor optimization opportunity. // These are "nice to have" improvements that may not have // measurable impact in most applications. SeverityLow Severity = iota // SeverityMedium indicates a moderate performance concern. // These issues should be addressed but may not be critical. SeverityMedium // SeverityHigh indicates a significant performance problem. // These issues will likely cause noticeable slowdowns and // should be fixed before release. SeverityHigh // SeverityCritical indicates a severe performance issue // that will cause production problems. Fix immediately. SeverityCritical )
type StringConcatInLoopRule ¶
type StringConcatInLoopRule struct{}
StringConcatInLoopRule detects string += concatenation in loops
func (*StringConcatInLoopRule) Category ¶
func (r *StringConcatInLoopRule) Category() string
func (*StringConcatInLoopRule) Name ¶
func (r *StringConcatInLoopRule) Name() string
type SyncPoolOpportunityRule ¶
type SyncPoolOpportunityRule struct{}
SyncPoolOpportunityRule detects repeated allocations that could use sync.Pool
func (*SyncPoolOpportunityRule) Category ¶
func (r *SyncPoolOpportunityRule) Category() string
func (*SyncPoolOpportunityRule) Name ¶
func (r *SyncPoolOpportunityRule) Name() string
type TimeFormatInLoopRule ¶
type TimeFormatInLoopRule struct{}
TimeFormatInLoopRule detects time.Time.Format with complex layouts in loops
func (*TimeFormatInLoopRule) Category ¶
func (r *TimeFormatInLoopRule) Category() string
func (*TimeFormatInLoopRule) Name ¶
func (r *TimeFormatInLoopRule) Name() string
type TimeLocationInLoopRule ¶
type TimeLocationInLoopRule struct{}
TimeLocationInLoopRule detects time.LoadLocation in loops
func (*TimeLocationInLoopRule) Category ¶
func (r *TimeLocationInLoopRule) Category() string
func (*TimeLocationInLoopRule) Name ¶
func (r *TimeLocationInLoopRule) Name() string
type TimeParseInLoopRule ¶
type TimeParseInLoopRule struct{}
TimeParseInLoopRule detects time.Parse in loops
func (*TimeParseInLoopRule) Category ¶
func (r *TimeParseInLoopRule) Category() string
func (*TimeParseInLoopRule) Name ¶
func (r *TimeParseInLoopRule) Name() string
type TypeAssertionInLoopRule ¶
type TypeAssertionInLoopRule struct{}
TypeAssertionInLoopRule detects type assertions in loops
func (*TypeAssertionInLoopRule) Category ¶
func (r *TypeAssertionInLoopRule) Category() string
func (*TypeAssertionInLoopRule) Name ¶
func (r *TypeAssertionInLoopRule) Name() string
type UnbatchedInsertRule ¶
type UnbatchedInsertRule struct{}
UnbatchedInsertRule detects single-row inserts that could be batched
func (*UnbatchedInsertRule) Category ¶
func (r *UnbatchedInsertRule) Category() string
func (*UnbatchedInsertRule) Name ¶
func (r *UnbatchedInsertRule) Name() string
type UnbufferedChannelRule ¶
type UnbufferedChannelRule struct{}
UnbufferedChannelRule detects unbuffered channel creation Now smarter: checks for intentional synchronization patterns
func (*UnbufferedChannelRule) Category ¶
func (r *UnbufferedChannelRule) Category() string
func (*UnbufferedChannelRule) Name ¶
func (r *UnbufferedChannelRule) Name() string
type UnlimitedConnectionPoolRule ¶
type UnlimitedConnectionPoolRule struct{}
UnlimitedConnectionPoolRule detects sql.DB with SetMaxOpenConns(0)
func (*UnlimitedConnectionPoolRule) Category ¶
func (r *UnlimitedConnectionPoolRule) Category() string
func (*UnlimitedConnectionPoolRule) Name ¶
func (r *UnlimitedConnectionPoolRule) Name() string
type UnpreallocatedSliceRule ¶
type UnpreallocatedSliceRule struct{}
UnpreallocatedSliceRule detects slice append in loops without preallocation Now smarter: tracks make() calls with capacity before loops
func (*UnpreallocatedSliceRule) Category ¶
func (r *UnpreallocatedSliceRule) Category() string
func (*UnpreallocatedSliceRule) Name ¶
func (r *UnpreallocatedSliceRule) Name() string
type VariadicInterfaceRule ¶
type VariadicInterfaceRule struct{}
VariadicInterfaceRule detects slice passed to ...interface{} causing per-element allocation
func (*VariadicInterfaceRule) Category ¶
func (r *VariadicInterfaceRule) Category() string
func (*VariadicInterfaceRule) Name ¶
func (r *VariadicInterfaceRule) Name() string