agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
127 lines (118 loc) • 5.89 kB
JavaScript
/**
* @file Calculate multi-dimensional semantic similarity between code blocks
* @description Single responsibility: Compute weighted similarity for WET code detection
*
* This utility calculates comprehensive semantic similarity between code blocks using
* multiple dimensions: structure, parameters, complexity, length, dependencies, and content.
* The weighted approach enables nuanced detection of code duplication that goes beyond
* simple text matching to identify semantically equivalent code patterns.
*
* Design rationale:
* - Multi-dimensional analysis captures various aspects of code similarity
* - Weighted scoring prioritizes structural similarity over surface-level matches
* - Early exit optimizations improve performance for common cases
* - Graceful handling of missing metrics maintains robustness
*/
const calculateStructureSimilarity = require('./calculateStructureSimilarity');
const calculateParameterSimilarity = require('./calculateParameterSimilarity');
const calculateComplexitySimilarity = require('./calculateComplexitySimilarity');
const calculateLengthSimilarity = require('./calculateLengthSimilarity');
const calculateDependencySimilarity = require('./calculateDependencySimilarity');
const calculateContentSimilarity = require('./calculateContentSimilarity');
/**
* Calculate comprehensive semantic similarity score between two code blocks
*
* Technical function: Computes weighted similarity across multiple code dimensions
*
* Implementation rationale:
* - Hash comparison provides O(1) exact match detection before expensive analysis
* - Type checking prevents meaningless comparisons (e.g., function vs class)
* - Weighted scoring reflects relative importance of different similarity aspects
* - Null handling allows graceful degradation when specific metrics unavailable
*
* Similarity weighting strategy:
* - Structure (40%): AST similarity most important for detecting logical equivalence
* - Parameters (20%): Function signatures critical for semantic equivalence
* - Complexity (10%): Similar complexity suggests similar algorithmic approach
* - Length (10%): Code size similarity indicates comparable implementation scale
* - Dependencies (10%): Similar imports/requires suggest related functionality
* - Content (10%): Textual similarity catches remaining edge cases
*
* Performance optimizations:
* - Hash comparison (O(1)) before expensive similarity calculations
* - Type filter prevents unnecessary computation on incompatible blocks
* - Weighted average calculation only for available metrics
* - Early return for trivial cases (exact matches, type mismatches)
*
* Weighted average calculation:
* - Only includes metrics that return non-null values
* - Adjusts total weight to accommodate missing metrics
* - Prevents division by zero when all metrics return null
* - Maintains proportional weighting when some metrics unavailable
*
* Edge case handling:
* - Exact hash matches: Return 1.0 immediately
* - Type mismatches: Return 0.0 (no similarity possible)
* - All null metrics: Return 0.0 (no calculable similarity)
* - Partial metrics: Adjust weights proportionally
*
* Semantic similarity dimensions:
* - Structure: AST node patterns and control flow similarity
* - Parameters: Function argument count, types, and naming patterns
* - Complexity: Cyclomatic complexity and nesting similarity
* - Length: Code size and statement count similarity
* - Dependencies: Import/require statement and API usage similarity
* - Content: Normalized textual content similarity after variable renaming
*
* Alternative approaches considered:
* - Equal weighting: Rejected as structure more important than surface features
* - Binary similarity: Rejected as doesn't capture gradual similarity levels
* - Single-metric comparison: Rejected as misses important similarity dimensions
*
* @param {Object} block1 - First code block with extracted features and metadata
* @param {Object} block2 - Second code block to compare against first block
* @returns {number} Similarity score from 0.0 (no similarity) to 1.0 (identical)
* @example
* // Identical functions with different variable names:
* // function add(a, b) { return a + b; }
* // function sum(x, y) { return x + y; }
* // Returns: ~0.85 (high structure/parameter similarity, lower content similarity)
*
* // Completely different block types:
* // function vs class declaration
* // Returns: 0.0 (immediate type mismatch)
*/
function calculateSemanticSimilarity(block1, block2) {
// Quick checks for exact matches
if (block1.hash === block2.hash) return 1.0;
// Don't compare different types of blocks
if (block1.type !== block2.type) return 0.0;
// Calculate weighted similarity score
const weights = {
structure: 0.4, // AST structure similarity
parameters: 0.2, // Parameter similarity
complexity: 0.1, // Complexity similarity
length: 0.1, // Length similarity
dependencies: 0.1, // Dependency similarity
content: 0.1 // Normalized content similarity
};
const scores = {
structure: calculateStructureSimilarity(block1, block2),
parameters: calculateParameterSimilarity(block1, block2),
complexity: calculateComplexitySimilarity(block1, block2),
length: calculateLengthSimilarity(block1, block2),
dependencies: calculateDependencySimilarity(block1, block2),
content: calculateContentSimilarity(block1, block2)
};
// Calculate weighted average
let totalScore = 0;
let totalWeight = 0;
for (const [metric, weight] of Object.entries(weights)) {
if (scores[metric] !== null) {
totalScore += scores[metric] * weight;
totalWeight += weight;
}
}
return totalWeight > 0 ? totalScore / totalWeight : 0;
}
module.exports = calculateSemanticSimilarity;