patterns

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: AGPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package patterns provides design pattern and anti-pattern detection for Trace.

Description

This package detects design patterns (Singleton, Factory, Builder, etc.), code smells (long functions, god objects), structural issues (circular deps), and coding conventions. Pattern detection is heuristic-based with confidence scores indicating certainty.

Thread Safety

All detector types are safe for concurrent use.

Index

Constants

View Source
const (
	// StructuralMatchBase is confidence when only structure matches.
	StructuralMatchBase = 0.6

	// IdiomaticMatchBase is confidence for idiomatic implementations.
	IdiomaticMatchBase = 0.9

	// HeuristicMatchBase is confidence for threshold-based detection.
	HeuristicMatchBase = 0.5
)

Confidence base values for calibration.

View Source
const (
	// MultipleExamplesBoost increases confidence for repeated patterns.
	MultipleExamplesBoost = 1.2

	// SingleExamplePenalty decreases confidence for single occurrences.
	SingleExamplePenalty = 0.8

	// PartialMatchPenalty decreases confidence for incomplete matches.
	PartialMatchPenalty = 0.7
)

Confidence adjustments.

Variables

View Source
var (
	// ErrInvalidInput indicates invalid input parameters.
	ErrInvalidInput = errors.New("invalid input")

	// ErrGraphNotReady indicates the graph is not frozen.
	ErrGraphNotReady = errors.New("graph not ready")

	// ErrContextCanceled indicates the context was canceled.
	ErrContextCanceled = errors.New("context canceled")

	// ErrPatternNotFound indicates no patterns were detected.
	ErrPatternNotFound = errors.New("pattern not found")

	// ErrUnsupportedLanguage indicates the language isn't supported.
	ErrUnsupportedLanguage = errors.New("unsupported language")
)

Sentinel errors for the patterns package.

View Source
var BuilderMatcher = &PatternMatcher{
	Name:        PatternBuilder,
	Description: "Constructs complex objects step by step",
	Languages:   []string{"go"},

	StructuralCheck: func(ctx context.Context, g *graph.Graph, idx *index.SymbolIndex, scope string) []PatternCandidate {
		candidates := make([]PatternCandidate, 0)

		structs := idx.GetByKind(ast.SymbolKindStruct)

		for _, s := range structs {
			if ctx.Err() != nil {
				break
			}

			if scope != "" && !strings.HasPrefix(s.FilePath, scope) {
				continue
			}

			if !strings.HasSuffix(s.Name, "Builder") && !strings.HasSuffix(s.Name, "Config") {
				continue
			}

			methods := findMethodsForType(idx, s.Name)

			withMethods := make([]string, 0)
			hasBuild := false
			chainable := 0

			for _, m := range methods {

				if strings.HasPrefix(m.Name, "With") || strings.HasPrefix(m.Name, "Set") {
					withMethods = append(withMethods, m.ID)

					if strings.Contains(m.Signature, "*"+s.Name) {
						chainable++
					}
				}

				if m.Name == "Build" {
					hasBuild = true
				}
			}

			if len(withMethods) >= 2 {
				symbolIDs := append([]string{s.ID}, withMethods...)

				candidates = append(candidates, PatternCandidate{
					SymbolIDs: symbolIDs,
					Location:  s.FilePath,
					Metadata: map[string]interface{}{
						"type":         s.ID,
						"type_name":    s.Name,
						"with_count":   len(withMethods),
						"chainable":    chainable,
						"has_build":    hasBuild,
						"with_methods": withMethods,
					},
				})
			}
		}

		return candidates
	},

	IdiomaticCheck: func(candidate PatternCandidate, idx *index.SymbolIndex) (bool, []string) {
		warnings := make([]string, 0)

		hasBuild, ok := candidate.Metadata["has_build"].(bool)
		if ok && !hasBuild {
			warnings = append(warnings, "Builder lacks Build() method - validation point missing")
		}

		chainable, ok := candidate.Metadata["chainable"].(int)
		withCount, ok2 := candidate.Metadata["with_count"].(int)
		if ok && ok2 && chainable < withCount {
			warnings = append(warnings, "Some With* methods aren't chainable (don't return *Builder)")
		}

		return len(warnings) == 0, warnings
	},
}

BuilderMatcher detects builder patterns.

Structural Indicators

- Type with method chaining (methods return same type) - With* or Set* methods - Build() method that returns final type

Idiomatic Check

- Build() validates before returning - With* methods are chainable

View Source
var FactoryMatcher = &PatternMatcher{
	Name:        PatternFactory,
	Description: "Creates objects without exposing instantiation logic",
	Languages:   []string{"go"},

	StructuralCheck: func(ctx context.Context, g *graph.Graph, idx *index.SymbolIndex, scope string) []PatternCandidate {
		candidates := make([]PatternCandidate, 0)

		functions := idx.GetByKind(ast.SymbolKindFunction)

		for _, fn := range functions {
			if ctx.Err() != nil {
				break
			}

			if scope != "" && !strings.HasPrefix(fn.FilePath, scope) {
				continue
			}

			if strings.HasPrefix(fn.Name, "New") || strings.HasPrefix(fn.Name, "Create") {

				if strings.Contains(fn.FilePath, "_test.go") {
					continue
				}

				candidates = append(candidates, PatternCandidate{
					SymbolIDs: []string{fn.ID},
					Location:  fn.FilePath,
					Metadata: map[string]interface{}{
						"function":        fn.ID,
						"function_name":   fn.Name,
						"function_file":   fn.FilePath,
						"signature":       fn.Signature,
						"returns_pointer": strings.Contains(fn.Signature, "*"),
						"returns_error":   strings.Contains(fn.Signature, "error"),
					},
				})
			}
		}

		return candidates
	},

	IdiomaticCheck: func(candidate PatternCandidate, idx *index.SymbolIndex) (bool, []string) {
		warnings := make([]string, 0)

		sig, ok := candidate.Metadata["signature"].(string)
		if !ok {
			return false, []string{"Could not validate signature"}
		}

		returnsError, ok := candidate.Metadata["returns_error"].(bool)
		if !ok {
			returnsError = false
		}

		if !returnsError {
			warnings = append(warnings, "Factory doesn't return error - no validation possible")
		}

		if !strings.Contains(sig, "*") && !strings.Contains(sig, "interface") {
			warnings = append(warnings, "Consider returning pointer or interface for flexibility")
		}

		return len(warnings) == 0, warnings
	},
}

FactoryMatcher detects factory patterns.

Structural Indicators

- Function named New* or Create* - Returns a concrete type or interface - May include validation or configuration

Idiomatic Check

- Better: returns interface, not concrete type - Better: includes validation

View Source
var MiddlewareMatcher = &PatternMatcher{
	Name:        PatternMiddleware,
	Description: "Handler chain pattern for HTTP or similar",
	Languages:   []string{"go"},

	StructuralCheck: func(ctx context.Context, g *graph.Graph, idx *index.SymbolIndex, scope string) []PatternCandidate {
		candidates := make([]PatternCandidate, 0)

		functions := idx.GetByKind(ast.SymbolKindFunction)

		for _, fn := range functions {
			if ctx.Err() != nil {
				break
			}

			if scope != "" && !strings.HasPrefix(fn.FilePath, scope) {
				continue
			}

			sig := fn.Signature

			if (strings.Contains(sig, "Handler") || strings.Contains(sig, "HandlerFunc")) &&
				strings.Count(sig, "Handler") >= 2 {

				candidates = append(candidates, PatternCandidate{
					SymbolIDs: []string{fn.ID},
					Location:  fn.FilePath,
					Metadata: map[string]interface{}{
						"function":  fn.ID,
						"name":      fn.Name,
						"signature": sig,
					},
				})
			}
		}

		return candidates
	},

	IdiomaticCheck: func(candidate PatternCandidate, idx *index.SymbolIndex) (bool, []string) {

		return true, nil
	},
}

MiddlewareMatcher detects middleware patterns.

Structural Indicators

- Function signature: func(Handler) Handler - http.Handler or similar handler types

View Source
var OptionsMatcher = &PatternMatcher{
	Name:        PatternOptions,
	Description: "Functional options for configuration",
	Languages:   []string{"go"},

	StructuralCheck: func(ctx context.Context, g *graph.Graph, idx *index.SymbolIndex, scope string) []PatternCandidate {
		candidates := make([]PatternCandidate, 0)

		types := idx.GetByKind(ast.SymbolKindType)

		for _, t := range types {
			if ctx.Err() != nil {
				break
			}

			if scope != "" && !strings.HasPrefix(t.FilePath, scope) {
				continue
			}

			if !strings.Contains(t.Name, "Option") && !strings.Contains(t.Name, "Opt") {
				continue
			}

			if !strings.Contains(t.Signature, "func") {
				continue
			}

			functions := idx.GetByKind(ast.SymbolKindFunction)
			withFuncs := make([]string, 0)

			for _, fn := range functions {
				if strings.HasPrefix(fn.Name, "With") && strings.Contains(fn.Signature, t.Name) {
					withFuncs = append(withFuncs, fn.ID)
				}
			}

			if len(withFuncs) >= 2 {
				symbolIDs := append([]string{t.ID}, withFuncs...)

				candidates = append(candidates, PatternCandidate{
					SymbolIDs: symbolIDs,
					Location:  t.FilePath,
					Metadata: map[string]interface{}{
						"option_type": t.ID,
						"option_name": t.Name,
						"with_count":  len(withFuncs),
						"with_funcs":  withFuncs,
					},
				})
			}
		}

		return candidates
	},

	IdiomaticCheck: func(candidate PatternCandidate, idx *index.SymbolIndex) (bool, []string) {

		withCount, ok := candidate.Metadata["with_count"].(int)
		if ok && withCount < 3 {
			return true, []string{"Options pattern with few options - might be over-engineering"}
		}
		return true, nil
	},
}

OptionsMatcher detects functional options pattern.

Structural Indicators

- Type alias: type Option func(*Config) - Multiple With* functions returning Option - Constructor accepting ...Option

View Source
var SingletonMatcher = &PatternMatcher{
	Name:        PatternSingleton,
	Description: "Single instance pattern with thread-safe lazy initialization",
	Languages:   []string{"go"},

	StructuralCheck: func(ctx context.Context, g *graph.Graph, idx *index.SymbolIndex, scope string) []PatternCandidate {
		candidates := make([]PatternCandidate, 0)

		variables := idx.GetByKind(ast.SymbolKindVariable)

		for _, v := range variables {
			if ctx.Err() != nil {
				break
			}

			if scope != "" && !strings.HasPrefix(v.FilePath, scope) {
				continue
			}

			accessorNames := []string{
				"Get" + v.Name,
				v.Name + "Instance",
				"Instance",
				"Get" + strings.TrimPrefix(v.Name, "default"),
			}

			for _, name := range accessorNames {
				functions := idx.GetByName(name)
				for _, fn := range functions {
					if fn.Kind == ast.SymbolKindFunction &&
						strings.HasPrefix(fn.FilePath, strings.TrimSuffix(v.FilePath, ".go")) {

						candidates = append(candidates, PatternCandidate{
							SymbolIDs: []string{v.ID, fn.ID},
							Location:  v.FilePath,
							Metadata: map[string]interface{}{
								"variable":       v.ID,
								"accessor":       fn.ID,
								"variable_name":  v.Name,
								"variable_file":  v.FilePath,
								"accessor_name":  fn.Name,
								"accessor_file":  fn.FilePath,
								"accessor_start": fn.StartLine,
								"accessor_end":   fn.EndLine,
							},
						})
					}
				}
			}
		}

		return candidates
	},

	IdiomaticCheck: func(candidate PatternCandidate, idx *index.SymbolIndex) (bool, []string) {
		warnings := make([]string, 0)

		accessorID, ok := candidate.Metadata["accessor"].(string)
		if !ok {
			return false, []string{"Could not validate accessor function"}
		}

		sym, found := idx.GetByID(accessorID)
		if !found {
			return false, []string{"Accessor function not found"}
		}

		hasSyncOnce := strings.Contains(sym.Signature, "sync.Once") ||
			strings.Contains(sym.Signature, "sync.Mutex")

		if !hasSyncOnce {
			warnings = append(warnings, "Singleton without sync.Once is not thread-safe")
			return false, warnings
		}

		return true, warnings
	},
}

SingletonMatcher detects singleton patterns.

Structural Indicators

- Package-level variable of a type - sync.Once or mutex for initialization - Accessor function (GetInstance, Instance, etc.)

Idiomatic Check

- MUST use sync.Once or mutex for thread-safety - Non-idiomatic: no synchronization mechanism

Functions

func DefaultMatchers

func DefaultMatchers() map[PatternType]*PatternMatcher

DefaultMatchers returns the standard set of pattern matchers.

Types

type CRSBridge

type CRSBridge struct {
	// contains filtered or unexported fields
}

CRSBridge implements CRSRecorder using a real CRS instance.

Description

CRSBridge wraps a CRS instance to record tool executions and generate CDCL clauses on error or degraded results. It uses ActorSystem since tool executions are system-level actions.

Thread Safety

This type is safe for concurrent use (delegates to thread-safe CRS).

func NewCRSBridge

func NewCRSBridge(crsInstance crs.CRS, sessionID string) *CRSBridge

NewCRSBridge creates a CRS bridge for tool step recording.

Inputs

  • crsInstance: The CRS to record steps in.
  • sessionID: Session ID for step attribution.

Outputs

  • *CRSBridge: Configured bridge.

func (*CRSBridge) RecordToolStep

func (b *CRSBridge) RecordToolStep(ctx context.Context, toolName string, resultCount int, duration time.Duration, err error)

RecordToolStep records a tool execution step and generates clauses as needed.

Description

Records the step in the CRS step history. On error, generates a session-scoped CDCL clause with FailureType=ToolError. On empty results, generates a soft signal clause indicating the parameters may be wrong.

Inputs

  • ctx: Context for cancellation.
  • toolName: Name of the tool that executed.
  • resultCount: Number of results returned.
  • duration: Execution duration.
  • err: Error from tool execution (nil on success).

type CRSRecorder

type CRSRecorder interface {
	// RecordToolStep records a tool execution step in the CRS.
	RecordToolStep(ctx context.Context, toolName string, resultCount int, duration time.Duration, err error)
}

CRSRecorder abstracts CRS step recording for tool implementations.

Description

CRSRecorder provides a simplified interface for pattern analysis tools to record their executions in the CRS. Tool executions are recorded as system-level actions using ActorSystem.

Thread Safety

Implementations must be safe for concurrent use.

type CircularDep

type CircularDep struct {
	// Cycle is the dependency chain [A, B, C, A].
	Cycle []string `json:"cycle"`

	// Type is package, type, or function.
	Type string `json:"type"`

	// Severity indicates importance.
	Severity Severity `json:"severity"`

	// Suggestion explains how to fix.
	Suggestion string `json:"suggestion"`

	// BreakPoints are recommended places to break the cycle.
	BreakPoints []string `json:"break_points"`
}

CircularDep represents a circular dependency.

type CircularDepFinder

type CircularDepFinder struct {
	// contains filtered or unexported fields
}

CircularDepFinder finds circular dependencies in code.

Description

CircularDepFinder detects circular dependencies at package, type, and function levels. It uses Tarjan's algorithm for efficient strongly connected component detection.

Thread Safety

This type is safe for concurrent use.

func NewCircularDepFinder

func NewCircularDepFinder(g *graph.Graph, projectModule string) *CircularDepFinder

NewCircularDepFinder creates a new circular dependency finder.

Description

Creates a finder that analyzes the code graph for circular dependencies. The package graph is built lazily on first query.

Inputs

  • g: The code graph to analyze.
  • projectModule: The module path (e.g., "github.com/user/repo").

Outputs

  • *CircularDepFinder: Configured finder.

func (*CircularDepFinder) BuildPackageGraph

func (c *CircularDepFinder) BuildPackageGraph(ctx context.Context) error

BuildPackageGraph builds the package-level dependency graph.

Description

Derives package dependencies from file imports. Called automatically on first query but can be called explicitly for better control.

Inputs

  • ctx: Context for cancellation.

Outputs

  • error: Non-nil on failure.

func (*CircularDepFinder) FindCircularDeps

func (c *CircularDepFinder) FindCircularDeps(
	ctx context.Context,
	scope string,
	depType CircularDepType,
) ([]CircularDep, error)

FindCircularDeps finds circular dependencies.

Description

Finds all circular dependencies in the specified scope. Supports package, type, and function level detection.

Inputs

  • ctx: Context for cancellation.
  • scope: Package path prefix to filter (empty = all).
  • depType: Type of circular dependency to find.

Outputs

  • []CircularDep: Found circular dependencies.
  • error: Non-nil on failure.

Example

finder := NewCircularDepFinder(graph, "github.com/user/repo")
deps, err := finder.FindCircularDeps(ctx, "pkg/", CircularDepPackage)

func (*CircularDepFinder) GetPackageGraph

func (c *CircularDepFinder) GetPackageGraph() *PackageGraph

GetPackageGraph returns the underlying package graph.

func (*CircularDepFinder) SetCRS

func (c *CircularDepFinder) SetCRS(recorder CRSRecorder)

SetCRS configures CRS recording for this finder.

func (*CircularDepFinder) Summary

func (c *CircularDepFinder) Summary(deps []CircularDep) string

Summary generates a summary of circular dependency findings.

type CircularDepType

type CircularDepType string

CircularDepType categorizes circular dependencies.

const (
	// CircularDepPackage is a package-level circular dependency.
	CircularDepPackage CircularDepType = "package"

	// CircularDepType represents a type-level circular dependency.
	CircularDepTypeLevel CircularDepType = "type"

	// CircularDepFunction is a function-level circular dependency.
	CircularDepFunction CircularDepType = "function"
)

type CodeFingerprint

type CodeFingerprint struct {
	// SymbolID is the identifier of the source symbol.
	SymbolID string

	// FilePath is the source file path.
	FilePath string

	// LineStart is the starting line of the code.
	LineStart int

	// LineEnd is the ending line of the code.
	LineEnd int

	// TokenHashes contains k-gram hashes of normalized tokens.
	TokenHashes []uint64

	// MinHashSig is the MinHash signature for LSH.
	MinHashSig []uint64

	// ASTStructure is an abstracted AST pattern string.
	ASTStructure string

	// TokenCount is the number of tokens in the code.
	TokenCount int

	// LineCount is the number of lines in the code.
	LineCount int

	// Complexity is a cyclomatic complexity estimate.
	Complexity int
}

CodeFingerprint represents a fingerprint for duplication detection.

Description

CodeFingerprint captures the structural essence of code for efficient similarity comparison. It uses k-gram hashing for exact matching and MinHash signatures for locality-sensitive hashing.

Thread Safety

This type is immutable after creation.

func (*CodeFingerprint) EstimatedJaccard

func (fp *CodeFingerprint) EstimatedJaccard(other *CodeFingerprint) float64

EstimatedJaccard estimates Jaccard similarity using MinHash signatures.

Description

Uses MinHash signatures for O(1) similarity estimation. Less accurate than exact Jaccard but much faster for large codebases.

Inputs

  • other: The fingerprint to compare against.

Outputs

  • float64: Estimated similarity score (0.0 - 1.0).

func (*CodeFingerprint) JaccardSimilarity

func (fp *CodeFingerprint) JaccardSimilarity(other *CodeFingerprint) float64

JaccardSimilarity computes the Jaccard similarity between two fingerprints.

Description

Computes set intersection / set union of token hashes. Returns a value between 0.0 and 1.0.

Inputs

  • other: The fingerprint to compare against.

Outputs

  • float64: Similarity score (0.0 - 1.0).

type CodeSmell

type CodeSmell struct {
	// Type categorizes the smell.
	Type SmellType `json:"type"`

	// Severity indicates importance.
	Severity Severity `json:"severity"`

	// Location is the file and symbol.
	Location string `json:"location"`

	// Description explains the issue.
	Description string `json:"description"`

	// Suggestion provides a fix recommendation.
	Suggestion string `json:"suggestion"`

	// Code is the relevant code snippet.
	Code string `json:"code,omitempty"`

	// Value is the measured value (e.g., line count).
	Value int `json:"value,omitempty"`

	// Threshold is what triggered the smell.
	Threshold int `json:"threshold,omitempty"`
}

CodeSmell represents a potential code quality issue.

type Convention

type Convention struct {
	// Type categorizes the convention (naming, error_handling, etc.).
	Type string `json:"type"`

	// Pattern describes the convention pattern.
	Pattern string `json:"pattern"`

	// Examples shows instances of the convention.
	Examples []string `json:"examples"`

	// Frequency is how often the convention is followed (0.0 - 1.0).
	Frequency float64 `json:"frequency"`

	// Description explains the convention.
	Description string `json:"description"`
}

Convention represents an observed coding convention.

type ConventionExtractor

type ConventionExtractor struct {
	// contains filtered or unexported fields
}

ConventionExtractor extracts coding conventions from a codebase.

Description

ConventionExtractor analyzes the codebase to infer coding conventions. This helps new code follow existing patterns and maintains consistency.

Thread Safety

This type is safe for concurrent use.

func NewConventionExtractor

func NewConventionExtractor(idx *index.SymbolIndex, projectRoot string) *ConventionExtractor

NewConventionExtractor creates a new convention extractor.

Inputs

  • idx: Symbol index for lookups.
  • projectRoot: Project root for reading source files.

Outputs

  • *ConventionExtractor: Configured extractor.

func NewConventionExtractorWithReader

func NewConventionExtractorWithReader(idx *index.SymbolIndex, reader *FileReader) *ConventionExtractor

NewConventionExtractorWithReader creates a convention extractor with a shared FileReader.

Inputs

  • idx: Symbol index for lookups.
  • reader: Shared cached file reader.

Outputs

  • *ConventionExtractor: Configured extractor.

func (*ConventionExtractor) ExtractConventions

func (c *ConventionExtractor) ExtractConventions(
	ctx context.Context,
	scope string,
	opts *ConventionOptions,
) ([]Convention, error)

ExtractConventions extracts coding conventions from the specified scope.

Description

Analyzes the codebase to identify common patterns in: - Naming (camelCase, snake_case, prefixes, suffixes) - Error handling (return patterns, wrapping) - File organization (one type per file, grouping) - Testing (table-driven, subtests) - Documentation (format, coverage)

Inputs

  • ctx: Context for cancellation.
  • scope: Package or file path prefix (empty = all).
  • opts: Extraction options.

Outputs

  • []Convention: Extracted conventions.
  • error: Non-nil on failure.

func (*ConventionExtractor) SetCRS

func (c *ConventionExtractor) SetCRS(recorder CRSRecorder)

SetCRS configures CRS recording for this extractor.

func (*ConventionExtractor) Summary

func (c *ConventionExtractor) Summary(conventions []Convention) string

Summary generates a summary of extracted conventions.

type ConventionOptions

type ConventionOptions struct {
	// SampleSize is the max symbols to sample per type (0 = all).
	SampleSize int

	// MinFrequency is the minimum frequency to report (0.0 - 1.0).
	MinFrequency float64

	// Types filters which convention categories to extract.
	// Valid values: "naming", "error_handling", "file_organization", "testing", "documentation", "imports".
	// Empty slice means all types.
	Types []string

	// IncludeTests includes test files in analysis.
	IncludeTests bool
}

ConventionOptions configures convention extraction.

func DefaultConventionOptions

func DefaultConventionOptions() ConventionOptions

DefaultConventionOptions returns sensible defaults.

type ConventionType

type ConventionType string

ConventionType categorizes conventions.

const (
	// ConventionNaming is a naming convention.
	ConventionNaming ConventionType = "naming"

	// ConventionErrorHandling is an error handling convention.
	ConventionErrorHandling ConventionType = "error_handling"

	// ConventionFileOrganization is a file organization convention.
	ConventionFileOrganization ConventionType = "file_organization"

	// ConventionTesting is a testing convention.
	ConventionTesting ConventionType = "testing"

	// ConventionDocumentation is a documentation convention.
	ConventionDocumentation ConventionType = "documentation"

	// ConventionImports is an import organization convention.
	ConventionImports ConventionType = "imports"
)

type DeadCode

type DeadCode struct {
	// Type is function, type, variable, or constant.
	Type string `json:"type"`

	// Name is the symbol name.
	Name string `json:"name"`

	// FilePath is the source file.
	FilePath string `json:"file_path"`

	// Line is the 1-indexed line number.
	Line int `json:"line"`

	// Reason explains why it's considered dead.
	Reason string `json:"reason"`

	// Confidence indicates certainty (lower for edge cases).
	Confidence float64 `json:"confidence"`
}

DeadCode represents unreferenced code.

type DeadCodeExclusions

type DeadCodeExclusions struct {
	// EntryPoints lists function patterns that are always excluded.
	// Supports wildcards: "main", "init", "Test*", "Benchmark*", "Example*"
	EntryPoints []string

	// ExportedSymbols excludes exported symbols (may be external API).
	ExportedSymbols bool

	// InterfaceImpls excludes methods that implement interfaces.
	InterfaceImpls bool

	// ReflectionPatterns lists patterns suggesting reflection usage.
	ReflectionPatterns []string

	// BuildTaggedFiles excludes files with build tags.
	BuildTaggedFiles bool

	// AnnotationPatterns lists comment annotations that exclude symbols.
	// Examples: "@used-by", "@implements", "nolint:deadcode"
	AnnotationPatterns []string
}

DeadCodeExclusions configures what to exclude from dead code detection.

Description

Dead code detection is inherently imprecise due to reflection, build tags, and external API contracts. These exclusions help reduce false positives.

func DefaultExclusions

func DefaultExclusions() *DeadCodeExclusions

DefaultExclusions returns conservative default exclusions.

IT-08 H-1: Extended from Go-only to cross-language entry point patterns.

func (*DeadCodeExclusions) IsExcluded

func (e *DeadCodeExclusions) IsExcluded(symbol *ast.Symbol, g *graph.Graph) (bool, string)

IsExcluded is a public wrapper for exclusion checking.

Inputs

  • symbol: The symbol to check.
  • g: The code graph.

Outputs

  • bool: True if the symbol should be excluded.
  • string: The reason for exclusion.

type DeadCodeFinder

type DeadCodeFinder struct {
	// contains filtered or unexported fields
}

DeadCodeFinder finds unreferenced code.

Description

DeadCodeFinder detects code that appears to be unused. It is conservative by default, preferring false negatives over false positives because: - Reflection can invoke code without static references - Build tags may exclude code from normal builds - Exported symbols may be part of a public API

Thread Safety

This type is safe for concurrent use.

func NewDeadCodeFinder

func NewDeadCodeFinder(g *graph.Graph, idx *index.SymbolIndex, projectRoot string) *DeadCodeFinder

NewDeadCodeFinder creates a new dead code finder.

Inputs

  • g: Code graph for reference analysis.
  • idx: Symbol index for lookups.
  • projectRoot: Project root for reading source files.

Outputs

  • *DeadCodeFinder: Configured finder.

func (*DeadCodeFinder) FindDeadCode

func (d *DeadCodeFinder) FindDeadCode(
	ctx context.Context,
	scope string,
	opts *DeadCodeOptions,
) ([]DeadCode, error)

FindDeadCode finds unreferenced code in the specified scope.

Description

Analyzes the code graph to find symbols with no incoming references. Uses a conservative approach to avoid false positives.

Inputs

  • ctx: Context for cancellation.
  • scope: Package or file path prefix (empty = all).
  • opts: Detection options.

Outputs

  • []DeadCode: Found dead code.
  • error: Non-nil on failure.

Example

finder := NewDeadCodeFinder(graph, index, "/project")
dead, err := finder.FindDeadCode(ctx, "pkg/internal", nil)

func (*DeadCodeFinder) SetCRS

func (d *DeadCodeFinder) SetCRS(recorder CRSRecorder)

SetCRS configures CRS recording for this finder.

func (*DeadCodeFinder) Summary

func (d *DeadCodeFinder) Summary(deadCode []DeadCode) string

Summary generates a summary of dead code findings.

type DeadCodeOptions

type DeadCodeOptions struct {
	// Exclusions configures what to exclude.
	Exclusions *DeadCodeExclusions

	// IncludeExported includes exported symbols in results.
	IncludeExported bool

	// IncludeTests includes test files in analysis.
	IncludeTests bool

	// MaxResults limits the number of results (0 = unlimited).
	MaxResults int
}

DeadCodeOptions configures dead code detection.

func DefaultDeadCodeOptions

func DefaultDeadCodeOptions() DeadCodeOptions

DefaultDeadCodeOptions returns sensible defaults.

type DetectedPattern

type DetectedPattern struct {
	// Type identifies the pattern (singleton, factory, etc.).
	Type PatternType `json:"type"`

	// Location is the package or file where the pattern is found.
	Location string `json:"location"`

	// Components lists the symbols that form the pattern.
	Components []string `json:"components"`

	// Confidence is how certain we are (0.0 - 1.0).
	Confidence float64 `json:"confidence"`

	// Example shows a code snippet illustrating the pattern.
	Example string `json:"example,omitempty"`

	// Idiomatic indicates if this is the recommended implementation.
	Idiomatic bool `json:"idiomatic"`

	// Warnings lists issues with the implementation.
	Warnings []string `json:"warnings,omitempty"`
}

DetectedPattern represents a design pattern found in code.

type DetectionOptions

type DetectionOptions struct {
	// Patterns limits detection to specific patterns (empty = all).
	Patterns []PatternType

	// MinConfidence filters results below this threshold.
	MinConfidence float64

	// IncludeNonIdiomatic includes non-idiomatic implementations.
	IncludeNonIdiomatic bool
}

DetectionOptions configures pattern detection.

type DupLocation

type DupLocation struct {
	// FilePath is the source file.
	FilePath string `json:"file_path"`

	// LineStart is the 1-indexed starting line.
	LineStart int `json:"line_start"`

	// LineEnd is the 1-indexed ending line.
	LineEnd int `json:"line_end"`

	// SymbolID is the containing symbol (if applicable).
	SymbolID string `json:"symbol_id,omitempty"`
}

DupLocation is a location of duplicated code.

type DuplicatePair

type DuplicatePair struct {
	// SymbolID1 is the first symbol's ID.
	SymbolID1 string

	// SymbolID2 is the second symbol's ID.
	SymbolID2 string

	// Similarity is the estimated Jaccard similarity.
	Similarity float64
}

DuplicatePair represents a pair of similar fingerprints.

type Duplication

type Duplication struct {
	// Type is exact, near, or structural.
	Type string `json:"type"`

	// Similarity is the match percentage (0.0 - 1.0).
	Similarity float64 `json:"similarity"`

	// Locations are where the duplicates exist.
	Locations []DupLocation `json:"locations"`

	// Code is a representative sample.
	Code string `json:"code,omitempty"`

	// Suggestion recommends extraction or refactoring.
	Suggestion string `json:"suggestion"`

	// Confidence in the duplication detection.
	Confidence float64 `json:"confidence"`
}

Duplication represents duplicate or similar code.

type DuplicationFinder

type DuplicationFinder struct {
	// contains filtered or unexported fields
}

DuplicationFinder finds duplicate code blocks.

Description

DuplicationFinder uses LSH-based fingerprinting to efficiently find duplicate and near-duplicate code. It operates in O(n log n) time instead of O(n²) for large codebases.

Thread Safety

This type is safe for concurrent use.

func NewDuplicationFinder

func NewDuplicationFinder(idx *index.SymbolIndex, projectRoot string) *DuplicationFinder

NewDuplicationFinder creates a new duplication finder.

Description

Creates a finder with pre-configured fingerprinting and LSH index. The index is built lazily on first query.

Inputs

  • idx: Symbol index for lookups.
  • projectRoot: Project root directory for reading source files.

Outputs

  • *DuplicationFinder: Configured finder.

func NewDuplicationFinderWithReader

func NewDuplicationFinderWithReader(idx *index.SymbolIndex, reader *FileReader) *DuplicationFinder

NewDuplicationFinderWithReader creates a duplication finder with a shared FileReader.

Inputs

  • idx: Symbol index for lookups.
  • reader: Shared cached file reader.

Outputs

  • *DuplicationFinder: Configured finder.

func (*DuplicationFinder) BuildIndex

func (d *DuplicationFinder) BuildIndex(ctx context.Context, opts *DuplicationOptions) (int, error)

BuildIndex builds the fingerprint index for the codebase.

Description

Scans all functions/methods in the index, computes fingerprints, and adds them to the LSH index. This is called automatically on first query but can be called explicitly for better control.

Inputs

  • ctx: Context for cancellation.
  • opts: Options controlling what to index.

Outputs

  • int: Number of symbols indexed.
  • error: Non-nil on failure.

func (*DuplicationFinder) FindDuplicatesOf

func (d *DuplicationFinder) FindDuplicatesOf(
	ctx context.Context,
	symbolID string,
	opts *DuplicationOptions,
) ([]Duplication, error)

FindDuplicatesOf finds code similar to a specific symbol.

Description

Finds all code blocks similar to the specified symbol.

Inputs

  • ctx: Context for cancellation.
  • symbolID: The symbol to find duplicates of.
  • opts: Detection options.

Outputs

  • []Duplication: Found duplications.
  • error: Non-nil on failure.

func (*DuplicationFinder) FindDuplication

func (d *DuplicationFinder) FindDuplication(
	ctx context.Context,
	scope string,
	opts *DuplicationOptions,
) ([]Duplication, error)

FindDuplication finds duplicate code blocks.

Description

Finds all pairs of duplicate code blocks in the specified scope. Uses LSH for efficient near-duplicate detection.

Inputs

  • ctx: Context for cancellation.
  • scope: Package or file path prefix (empty = all).
  • opts: Detection options.

Outputs

  • []Duplication: Found duplications.
  • error: Non-nil on failure.

Example

finder := NewDuplicationFinder(index, "/project")
dups, err := finder.FindDuplication(ctx, "pkg/auth", nil)

func (*DuplicationFinder) SetCRS

func (d *DuplicationFinder) SetCRS(recorder CRSRecorder)

SetCRS configures CRS recording for this finder.

func (*DuplicationFinder) Stats

Stats returns statistics about the duplication finder.

func (*DuplicationFinder) Summary

func (d *DuplicationFinder) Summary(duplications []Duplication) string

Summary generates a summary of duplication findings.

type DuplicationOptions

type DuplicationOptions struct {
	// MinLines is the minimum lines to consider (default: 5).
	MinLines int

	// SimilarityThreshold is the minimum similarity (default: 0.8).
	SimilarityThreshold float64

	// Type filters results by duplication type ("exact", "near", "structural").
	// Empty string means all types.
	Type string

	// IncludeTests includes test files in analysis.
	IncludeTests bool

	// MaxResults limits the number of results (0 = unlimited).
	MaxResults int
}

DuplicationOptions configures duplication detection.

func DefaultDuplicationOptions

func DefaultDuplicationOptions() DuplicationOptions

DefaultDuplicationOptions returns sensible defaults.

type DuplicationStats

type DuplicationStats struct {
	// Indexed indicates if the index has been built.
	Indexed bool

	// IndexedSymbols is the number of indexed symbols.
	IndexedSymbols int

	// NumBands is the LSH band count.
	NumBands int

	// RowsPerBand is the LSH rows per band.
	RowsPerBand int

	// TotalBuckets is the total non-empty buckets.
	TotalBuckets int

	// MaxBucketSize is the largest bucket size.
	MaxBucketSize int
}

DuplicationStats contains statistics about duplication detection.

type DuplicationType

type DuplicationType string

DuplicationType categorizes duplicates.

const (
	// DuplicationExact is byte-for-byte identical code.
	DuplicationExact DuplicationType = "exact"

	// DuplicationNear is structurally similar code with minor differences.
	DuplicationNear DuplicationType = "near"

	// DuplicationStructural is same control flow with different names.
	DuplicationStructural DuplicationType = "structural"
)

type FileReader

type FileReader struct {
	// contains filtered or unexported fields
}

FileReader provides cached file reading for pattern analysis tools.

Description

FileReader caches file contents (split into lines) to avoid repeated I/O when multiple tools analyze the same files. This replaces the three duplicate readSymbolCode implementations in smells.go, duplication.go, and conventions.go.

Thread Safety

This type is safe for concurrent use via sync.RWMutex.

func NewFileReader

func NewFileReader(projectRoot string) *FileReader

NewFileReader creates a new cached file reader.

Inputs

  • projectRoot: Base directory for resolving relative file paths.

Outputs

  • *FileReader: Configured reader with empty cache.

func (*FileReader) CacheSize

func (r *FileReader) CacheSize() int

CacheSize returns the number of cached files.

func (*FileReader) ReadLines

func (r *FileReader) ReadLines(filePath string) ([]string, error)

ReadLines reads file contents as lines, using cache on subsequent calls.

Inputs

  • filePath: Relative file path (joined with projectRoot).

Outputs

  • []string: File lines.
  • error: Non-nil on I/O failure.

func (*FileReader) ReadSymbolCode

func (r *FileReader) ReadSymbolCode(sym *ast.Symbol) (string, error)

ReadSymbolCode reads the source code for a symbol, using cache.

Description

Replaces the 3 duplicate readSymbolCode implementations that existed in SmellFinder, DuplicationFinder, and ConventionExtractor.

Inputs

  • sym: The symbol to read code for.

Outputs

  • string: Source code of the symbol.
  • error: Non-nil if symbol is nil, lines are out of bounds, or I/O fails.

type FingerprintConfig

type FingerprintConfig struct {
	// KGramSize is the size of k-grams for token hashing.
	KGramSize int

	// NumHashFuncs is the number of hash functions for MinHash.
	NumHashFuncs int

	// NormalizeIdentifiers removes identifier differences.
	NormalizeIdentifiers bool
}

FingerprintConfig configures code fingerprinting behavior.

func DefaultFingerprintConfig

func DefaultFingerprintConfig() FingerprintConfig

DefaultFingerprintConfig returns sensible defaults.

type Fingerprinter

type Fingerprinter struct {
	// contains filtered or unexported fields
}

Fingerprinter creates code fingerprints from symbols.

Description

Fingerprinter processes code symbols to create fingerprints suitable for duplication detection. It normalizes code, extracts k-grams, and computes MinHash signatures.

Thread Safety

This type is safe for concurrent use.

func NewFingerprinter

func NewFingerprinter(config FingerprintConfig) *Fingerprinter

NewFingerprinter creates a new fingerprinter with the given config.

Description

Creates a fingerprinter with pre-computed hash seeds for MinHash.

Inputs

  • config: Fingerprinting configuration.

Outputs

  • *Fingerprinter: Configured fingerprinter.

func (*Fingerprinter) Fingerprint

func (f *Fingerprinter) Fingerprint(symbol *ast.Symbol, code string) *CodeFingerprint

Fingerprint creates a fingerprint for a symbol's code.

Description

Extracts code from the symbol, normalizes it, and computes k-gram hashes and MinHash signature.

Inputs

  • symbol: The symbol to fingerprint.
  • code: The source code content for the symbol.

Outputs

  • *CodeFingerprint: The computed fingerprint.

type LSHIndex

type LSHIndex struct {
	// contains filtered or unexported fields
}

LSHIndex provides O(n log n) near-duplicate detection using locality-sensitive hashing.

Description

LSHIndex uses banded MinHash for approximate nearest neighbor search. Instead of comparing every pair of fingerprints (O(n²)), it hashes fingerprints into buckets where similar items are likely to collide.

The index divides MinHash signatures into bands. Two fingerprints are candidates if they share at least one band. More bands mean higher recall but more false positives.

Thread Safety

This type is safe for concurrent use.

func NewLSHIndex

func NewLSHIndex(numBands, rowsPerBand int) *LSHIndex

NewLSHIndex creates an LSH index with the specified parameters.

Description

Creates an index configured for the desired similarity threshold. Higher numBands increases recall (finds more similar pairs) but also increases false positives. A good rule of thumb:

  • For 80% similarity threshold: numBands=20, rowsPerBand=5
  • For 90% similarity threshold: numBands=50, rowsPerBand=2

The signature length must be numBands * rowsPerBand.

Inputs

  • numBands: Number of hash bands.
  • rowsPerBand: Rows per band.

Outputs

  • *LSHIndex: The configured index.

Example

index := NewLSHIndex(20, 5) // For 100-element MinHash signatures
index.Add(fingerprint)
candidates := index.Query(queryFingerprint)

func (*LSHIndex) Add

func (l *LSHIndex) Add(fp *CodeFingerprint)

Add adds a fingerprint to the index.

Description

Hashes the fingerprint's MinHash signature into band buckets. If a fingerprint with the same SymbolID already exists, it is replaced.

Inputs

  • fp: The fingerprint to add.

Errors

Silently ignores nil fingerprints or those with empty signatures.

func (*LSHIndex) FindAllDuplicates

func (l *LSHIndex) FindAllDuplicates(threshold float64) []DuplicatePair

FindAllDuplicates finds all pairs of similar fingerprints.

Description

Efficiently finds all pairs of fingerprints with similarity above the threshold. Uses LSH to avoid O(n²) comparisons.

Inputs

  • threshold: Minimum similarity (0.0 - 1.0).

Outputs

  • []DuplicatePair: All pairs above the threshold.

func (*LSHIndex) GetFingerprint

func (l *LSHIndex) GetFingerprint(symbolID string) (*CodeFingerprint, bool)

GetFingerprint retrieves a fingerprint by symbol ID.

Outputs

  • *CodeFingerprint: The fingerprint, or nil if not found.
  • bool: True if found.

func (*LSHIndex) Query

func (l *LSHIndex) Query(fp *CodeFingerprint) []string

Query finds candidate matches for a fingerprint.

Description

Returns symbol IDs of fingerprints that share at least one band with the query fingerprint. These are candidates that should be verified with actual similarity computation.

Inputs

  • fp: The query fingerprint.

Outputs

  • []string: Symbol IDs of candidate matches.

func (*LSHIndex) QueryWithThreshold

func (l *LSHIndex) QueryWithThreshold(fp *CodeFingerprint, threshold float64) []LSHMatch

QueryWithThreshold finds matches above a similarity threshold.

Description

Uses LSH to find candidates, then verifies each candidate using estimated Jaccard similarity. Returns only matches above the threshold.

Inputs

  • fp: The query fingerprint.
  • threshold: Minimum similarity (0.0 - 1.0).

Outputs

  • []LSHMatch: Matches above the threshold.

func (*LSHIndex) Remove

func (l *LSHIndex) Remove(symbolID string)

Remove removes a fingerprint from the index.

Inputs

  • symbolID: The ID of the fingerprint to remove.

func (*LSHIndex) Size

func (l *LSHIndex) Size() int

Size returns the number of fingerprints in the index.

func (*LSHIndex) Stats

func (l *LSHIndex) Stats() LSHStats

Stats returns statistics about the index.

type LSHMatch

type LSHMatch struct {
	// SymbolID is the matched symbol's ID.
	SymbolID string

	// Similarity is the estimated Jaccard similarity.
	Similarity float64
}

LSHMatch represents a similarity match from the LSH index.

type LSHStats

type LSHStats struct {
	// NumFingerprints is the number of indexed fingerprints.
	NumFingerprints int

	// NumBands is the number of bands.
	NumBands int

	// RowsPerBand is the number of rows per band.
	RowsPerBand int

	// TotalBuckets is the total number of non-empty buckets.
	TotalBuckets int

	// MaxBucketSize is the size of the largest bucket.
	MaxBucketSize int
}

LSHStats contains statistics about the LSH index.

type NopCRSRecorder

type NopCRSRecorder struct{}

NopCRSRecorder is a no-op implementation for tests and standalone usage.

Description

NopCRSRecorder discards all step recordings without error. Use this when CRS integration is not needed (tests, standalone tool usage).

Thread Safety

This type is safe for concurrent use (stateless).

func (*NopCRSRecorder) RecordToolStep

func (n *NopCRSRecorder) RecordToolStep(_ context.Context, _ string, _ int, _ time.Duration, _ error)

RecordToolStep is a no-op.

type PackageGraph

type PackageGraph struct {
	// contains filtered or unexported fields
}

PackageGraph provides package-level dependency tracking.

Description

Our code graph has file and symbol nodes, but not package nodes. PackageGraph aggregates file imports into package-level dependencies, enabling circular dependency detection at the package level.

Thread Safety

This type is NOT safe for concurrent modification. It is designed to be built once and then queried concurrently.

func BuildPackageGraph

func BuildPackageGraph(g *graph.Graph, projectModule string) *PackageGraph

BuildPackageGraph derives package dependencies from file imports.

Description

Scans all file nodes in the code graph and aggregates their imports into package-level dependencies. Internal packages are identified by the "internal" path segment.

Inputs

  • g: The code graph to analyze.
  • projectModule: The module path (e.g., "github.com/user/repo").

Outputs

  • *PackageGraph: The constructed package dependency graph.

func NewPackageGraph

func NewPackageGraph() *PackageGraph

NewPackageGraph creates an empty package graph.

func (*PackageGraph) Dependencies

func (pg *PackageGraph) Dependencies(path string) []string

Dependencies returns the direct dependencies of a package.

func (*PackageGraph) Dependents

func (pg *PackageGraph) Dependents(path string) []string

Dependents returns packages that depend on the given package.

func (*PackageGraph) FindCycles

func (pg *PackageGraph) FindCycles() [][]string

FindCycles finds all strongly connected components (cycles) using Tarjan's algorithm.

Description

Implements Tarjan's algorithm for finding strongly connected components. Returns all cycles (SCCs with more than one node).

Outputs

  • [][]string: Each element is a cycle of package paths.

func (*PackageGraph) FindShortestCycle

func (pg *PackageGraph) FindShortestCycle(pkgPath string) []string

FindShortestCycle finds the shortest cycle containing a package.

Description

Uses BFS to find the shortest cycle that includes the specified package. Returns nil if no cycle exists.

Inputs

  • pkgPath: The package to find cycles for.

Outputs

  • []string: The cycle path, or nil if no cycle exists.

func (*PackageGraph) GetPackage

func (pg *PackageGraph) GetPackage(path string) (*PackageNode, bool)

GetPackage retrieves a package node by path.

func (*PackageGraph) Packages

func (pg *PackageGraph) Packages() []string

Packages returns all package paths.

func (*PackageGraph) Stats

func (pg *PackageGraph) Stats() PackageGraphStats

Stats returns statistics about the package graph.

func (*PackageGraph) TopologicalSort

func (pg *PackageGraph) TopologicalSort() []string

TopologicalSort returns packages in topological order.

Description

Returns packages such that if A depends on B, B comes before A. Returns nil if cycles exist (no valid topological order).

Outputs

  • []string: Packages in topological order, or nil if cycles exist.

type PackageGraphStats

type PackageGraphStats struct {
	// PackageCount is the number of packages.
	PackageCount int

	// EdgeCount is the number of import edges.
	EdgeCount int

	// MaxDeps is the maximum dependencies for any package.
	MaxDeps int

	// MaxDependents is the maximum dependents for any package.
	MaxDependents int

	// InternalCount is the number of internal packages.
	InternalCount int

	// StdLibCount is the number of std lib packages.
	StdLibCount int
}

PackageGraphStats contains statistics about the package graph.

type PackageNode

type PackageNode struct {
	// Path is the package import path.
	Path string

	// Files lists the source files in this package.
	Files []string

	// Imports lists packages this package imports.
	Imports []string

	// ImportedBy lists packages that import this package.
	ImportedBy []string

	// IsInternal indicates if this is an internal package.
	IsInternal bool

	// IsStdLib indicates if this is a standard library package.
	IsStdLib bool
}

PackageNode represents a package in the dependency graph.

type PatternCandidate

type PatternCandidate struct {
	// SymbolIDs are the symbols that may form the pattern.
	SymbolIDs []string

	// Location is where the pattern was found.
	Location string

	// Metadata contains pattern-specific detection data.
	Metadata map[string]interface{}
}

PatternCandidate is a potential pattern match before validation.

type PatternDetector

type PatternDetector struct {
	// contains filtered or unexported fields
}

PatternDetector finds design patterns in code.

Description

PatternDetector scans code using configurable matchers to find design patterns. It supports both structural detection (finding code that looks like a pattern) and idiomatic validation (checking if it's implemented correctly).

Thread Safety

This type is safe for concurrent use.

func NewPatternDetector

func NewPatternDetector(g *graph.Graph, idx *index.SymbolIndex) *PatternDetector

NewPatternDetector creates a new detector with default matchers.

Description

Creates a detector that uses the provided graph and index with the standard set of pattern matchers.

Inputs

  • g: Code graph. Must be frozen before DetectPatterns().
  • idx: Symbol index for lookups.

Outputs

  • *PatternDetector: Configured detector.

Example

detector := NewPatternDetector(graph, index)
patterns, err := detector.DetectPatterns(ctx, "pkg/service", nil)

func (*PatternDetector) DetectPattern

func (d *PatternDetector) DetectPattern(
	ctx context.Context,
	scope string,
	patternType PatternType,
) ([]DetectedPattern, error)

DetectPattern detects a specific pattern type.

Description

Shorthand for detecting a single pattern type.

Inputs

  • ctx: Context for cancellation.
  • scope: Package or file path prefix to scan.
  • patternType: The specific pattern to look for.

Outputs

  • []DetectedPattern: Found patterns.
  • error: Non-nil on failure.

func (*PatternDetector) DetectPatterns

func (d *PatternDetector) DetectPatterns(
	ctx context.Context,
	scope string,
	opts *DetectionOptions,
) ([]DetectedPattern, error)

DetectPatterns finds design patterns in the specified scope.

Description

Scans the specified scope (package or file path prefix) for design patterns. Returns all detected patterns with confidence scores and idiomatic validation results.

Inputs

  • ctx: Context for cancellation.
  • scope: Package or file path prefix to scan (empty = all).
  • opts: Optional detection options.

Outputs

  • []DetectedPattern: Found patterns with confidence and warnings.
  • error: Non-nil on failure.

Example

// Detect all patterns in a package
patterns, err := detector.DetectPatterns(ctx, "pkg/auth", nil)

// Detect only factory patterns
patterns, err := detector.DetectPatterns(ctx, "pkg/auth", &DetectionOptions{
    Patterns: []PatternType{PatternFactory},
})

func (*PatternDetector) GetMatcher

func (d *PatternDetector) GetMatcher(patternType PatternType) (*PatternMatcher, bool)

GetMatcher returns a registered matcher by pattern type.

func (*PatternDetector) ListPatterns

func (d *PatternDetector) ListPatterns() []PatternType

ListPatterns returns all registered pattern types.

func (*PatternDetector) RegisterMatcher

func (d *PatternDetector) RegisterMatcher(matcher *PatternMatcher)

RegisterMatcher adds a custom pattern matcher.

Description

Registers a custom pattern matcher. If a matcher for the pattern type already exists, it is replaced.

Inputs

  • matcher: The pattern matcher to register.

Example

detector.RegisterMatcher(&PatternMatcher{
    Name: "custom_pattern",
    StructuralCheck: func(...) []PatternCandidate { ... },
})

func (*PatternDetector) Summary

func (d *PatternDetector) Summary(patterns []DetectedPattern) string

Summary generates a summary of detected patterns.

type PatternMatcher

type PatternMatcher struct {
	// Name is the pattern name (singleton, factory, etc.).
	Name PatternType

	// Description explains the pattern.
	Description string

	// Languages supported (empty = all).
	Languages []string

	// StructuralCheck finds candidates based on code structure.
	StructuralCheck func(ctx context.Context, g *graph.Graph, idx *index.SymbolIndex, scope string) []PatternCandidate

	// IdiomaticCheck validates if a candidate is idiomatically implemented.
	// Returns (isIdiomatic, warnings).
	IdiomaticCheck func(candidate PatternCandidate, idx *index.SymbolIndex) (bool, []string)
}

PatternMatcher defines how to detect a specific design pattern.

Description

PatternMatcher contains the logic for detecting one design pattern. It has two phases: structural detection (find candidates based on shape) and idiomatic validation (check if the implementation follows best practices).

func (*PatternMatcher) Match

func (m *PatternMatcher) Match(
	ctx context.Context,
	g *graph.Graph,
	idx *index.SymbolIndex,
	scope string,
) []DetectedPattern

Match runs the pattern matcher and returns detected patterns.

type PatternType

type PatternType string

PatternType identifies the design pattern.

const (
	// PatternSingleton is a single instance pattern with thread-safety.
	PatternSingleton PatternType = "singleton"

	// PatternFactory creates objects without exposing instantiation logic.
	PatternFactory PatternType = "factory"

	// PatternBuilder constructs complex objects step by step.
	PatternBuilder PatternType = "builder"

	// PatternOptions uses functional options for configuration.
	PatternOptions PatternType = "options"

	// PatternMiddleware chains handlers.
	PatternMiddleware PatternType = "middleware"

	// PatternStrategy defines interchangeable algorithms.
	PatternStrategy PatternType = "strategy"

	// PatternObserver notifies observers of state changes.
	PatternObserver PatternType = "observer"

	// PatternRepository abstracts data access.
	PatternRepository PatternType = "repository"
)

type Severity

type Severity string

Severity indicates the importance of an issue.

const (
	SeverityInfo    Severity = "INFO"
	SeverityWarning Severity = "WARNING"
	SeverityError   Severity = "ERROR"
)

type SmellFinder

type SmellFinder struct {
	// contains filtered or unexported fields
}

SmellFinder finds code smells in the codebase.

Description

SmellFinder detects potential code quality issues like long functions, god objects, error swallowing, and more. All detections are configurable via thresholds.

Thread Safety

This type is safe for concurrent use.

func NewSmellFinder

func NewSmellFinder(idx *index.SymbolIndex, projectRoot string) *SmellFinder

NewSmellFinder creates a new code smell finder.

Inputs

  • idx: Symbol index for lookups.
  • projectRoot: Project root for reading source files.

Outputs

  • *SmellFinder: Configured finder.

func NewSmellFinderWithReader

func NewSmellFinderWithReader(idx *index.SymbolIndex, reader *FileReader) *SmellFinder

NewSmellFinderWithReader creates a smell finder with a shared FileReader.

Inputs

  • idx: Symbol index for lookups.
  • reader: Shared cached file reader.

Outputs

  • *SmellFinder: Configured finder.

func (*SmellFinder) FindCodeSmells

func (s *SmellFinder) FindCodeSmells(
	ctx context.Context,
	scope string,
	opts *SmellOptions,
) ([]CodeSmell, error)

FindCodeSmells finds code smells in the specified scope.

Description

Scans the codebase for various code smells including: - Long functions (exceeds line threshold) - Long parameter lists (exceeds param threshold) - God objects (types with too many methods) - Error swallowing (empty error handling) - Magic numbers (unexplained numeric literals) - Deep nesting (excessive indentation)

Inputs

  • ctx: Context for cancellation.
  • scope: Package or file path prefix (empty = all).
  • opts: Detection options.

Outputs

  • []CodeSmell: Found code smells.
  • error: Non-nil on failure.

func (*SmellFinder) SetCRS

func (s *SmellFinder) SetCRS(recorder CRSRecorder)

SetCRS configures CRS recording for this finder.

Inputs

  • recorder: CRS recorder for step tracking.

func (*SmellFinder) Summary

func (s *SmellFinder) Summary(smells []CodeSmell) string

Summary generates a summary of code smell findings.

type SmellOptions

type SmellOptions struct {
	// Thresholds configures detection thresholds.
	Thresholds SmellThresholds

	// MinSeverity filters results by minimum severity.
	MinSeverity Severity

	// ContextLines controls how many lines of surrounding code to include
	// in the Code field of each CodeSmell result (0 = no code context).
	ContextLines int

	// IncludeTests includes test files in analysis.
	IncludeTests bool

	// MaxResults limits the number of results (0 = unlimited).
	MaxResults int
}

SmellOptions configures code smell detection.

func DefaultSmellOptions

func DefaultSmellOptions() SmellOptions

DefaultSmellOptions returns sensible defaults.

type SmellThresholds

type SmellThresholds struct {
	// MaxFunctionLines triggers long_function smell.
	MaxFunctionLines int

	// MaxParameters triggers long_parameter_list smell.
	MaxParameters int

	// MaxMethodCount triggers god_object smell.
	MaxMethodCount int

	// MaxNestingDepth triggers deep_nesting smell.
	MaxNestingDepth int
}

SmellThresholds configures code smell detection.

func DefaultSmellThresholds

func DefaultSmellThresholds() SmellThresholds

DefaultSmellThresholds returns standard thresholds.

type SmellType

type SmellType string

SmellType identifies the code smell category.

const (
	// SmellLongFunction is a function with too many lines.
	SmellLongFunction SmellType = "long_function"

	// SmellLongParameterList is a function with too many parameters.
	SmellLongParameterList SmellType = "long_parameter_list"

	// SmellGodObject is a type with too many methods.
	SmellGodObject SmellType = "god_object"

	// SmellFeatureEnvy is excessive cross-package dependencies.
	SmellFeatureEnvy SmellType = "feature_envy"

	// SmellEmptyInterface is overuse of interface{}.
	SmellEmptyInterface SmellType = "empty_interface"

	// SmellErrorSwallowing is ignoring errors.
	SmellErrorSwallowing SmellType = "error_swallowing"

	// SmellMagicNumber is unexplained numeric literals.
	SmellMagicNumber SmellType = "magic_number"

	// SmellDeepNesting is excessive nesting depth.
	SmellDeepNesting SmellType = "deep_nesting"
)

Jump to

Keyboard shortcuts

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