/** * Universal DRY (Don't Repeat Yourself) Analyzer — Spec 17 R3 * * R3.1: Self-reference fix — span-overlap check prevents a block from citing itself. * R3.2: Minimum block size 5 → 15. * R3.3: Rule-id split — dry/duplicate (exact token match) + dry/structural-similarity * (identical token-kind sequence with different identifiers/literals). * R7: dry/duplicate → warning, dry/structural-similarity → suggestion. * * Spec 13 R5 — Diverging Clones: Exports DryPairSeed during analysis for * two-phase tracking (seed + re-measure pass in auditRunner). */ import { UniversalAnalyzer } from '../../languages/UniversalAnalyzer.js'; import type { Violation, FunctionMetadata } from '../../types.js'; import type { AST, LanguageAdapter } from '../../languages/types.js'; /** * Pair seed emitted during DRY analysis for diverging-clone tracking. * The identity is fingerprint-based (file + enclosing symbol), not content-hash-based. */ export interface DryPairSeed { /** Order-normalized pair identity: SHA256(sorted(fp1, fp2).join('||')) */ pairFingerprint: string; file1: string; symbol1: string; line1: number; contentHash1: string; file2: string; symbol2: string; line2: number; contentHash2: string; /** Jaccard similarity [0,1] — 1.0 for exact, ~Jaccard for structural. */ similarity: number; /** Rule: 'dry/duplicate' or 'dry/structural-similarity' */ rule: string; } /** * Configuration for DRY analyzer */ export interface DRYAnalyzerConfig { minLineThreshold?: number; similarityThreshold?: number; excludePatterns?: string[]; checkImports?: boolean; checkStrings?: boolean; /** R4.2: Enables dry/structural-similarity analysis. Default false. */ checkStructuralSimilarity?: boolean; ignoreComments?: boolean; ignoreWhitespace?: boolean; /** Full function index (all functions in codebase) for cross-file duplicate detection in scoped audits */ fullFunctionIndex?: FunctionMetadata[]; } export declare const DEFAULT_DRY_CONFIG: DRYAnalyzerConfig; export declare class UniversalDRYAnalyzer extends UniversalAnalyzer { readonly name = "dry"; readonly description = "Detects code duplication across the codebase"; readonly category = "maintainability"; /** Per-file accumulator for pairs seeded during analyzeAST. */ private _dryPairsForFile; /** Pairs collected during the current analysis run for diverging-clone seeding. */ private _dryPairs; /** Expose collected pairs for the auditRunner to persist. */ get dryPairs(): DryPairSeed[]; protected analyzeAST(ast: AST, adapter: LanguageAdapter, config: DRYAnalyzerConfig, sourceCode: string): Promise; /** * Returns true if the two blocks share code spans (same file + overlapping lines). */ private spansOverlap; /** * Sort comparator: earliest file+line first. */ private byFileAndLine; /** * R3.1: Deduplicate overlapping blocks. Prefers the innermost block when * one block fully contains another (nesting), and the earliest block when * blocks only partially overlap. * * This ensures that blocks nested inside functions/classes (e.g. for-loops * inside a function body) surface for duplicate detection instead of being * silently deduplicated by their outer container. */ private deduplicateBlocks; /** * Group blocks by a key field into a map of key→blocks[]. */ private groupByHash; /** * R3.3: Normalize code to its token-kind sequence. * Identifiers → ID, string/number/regex literals → LIT. */ private normalizeStructure; /** * Extract code blocks from AST */ private extractCodeBlocks; /** * Create a code block from an AST node */ private createCodeBlock; /** * R3.3: Normalize code for structural comparison. * First applies standard normalization (whitespace/comments), then * replaces identifiers and literals with placeholders. */ private normalizeCodeForStructure; /** * Normalize code for comparison */ private normalizeCode; /** * Hash code for comparison */ private hashCode; /** * Count lines in text */ private countLines; /** * Check if block is large enough to be considered */ private isBlockLargeEnough; /** * Build local index for duplicate detection */ private buildLocalIndex; /** * Check for duplicate string literals */ private checkDuplicateStrings; /** * Check for duplicate imports */ private checkDuplicateImports; /** * Helper methods */ private isExcluded; private findNodeByLocation; private walkAST; private isSignificantBlock; private isStringLiteral; /** * Compute the Jaccard similarity index between two tokenized strings. * Jaccard = |intersection| / |union|. Range [0, 1]. */ private computeJaccardSimilarity; /** * Compute an order-independent pair fingerprint from file + line + nodeType. * Uses SHA256(sorted(a, b).join('||')) so the same pair has the same * fingerprint regardless of argument order. */ private computePairFingerprint; /** * Seed a pair into the per-file accumulator for diverging-clone tracking. * Called during analyzeAST when a duplicate or structural-similarity pair * is detected. */ private seedPair; /** * Override analyze() to attach seeded pairs to the AnalyzerResult. */ analyze(files: string[], config?: any, options?: any): Promise; } //# sourceMappingURL=UniversalDRYAnalyzer.d.ts.map