superaugment
Version:
Enterprise-grade MCP server with world-class C++ analysis, robust error handling, and production-ready architecture for VS Code Augment
555 lines • 22.4 kB
JavaScript
/**
* SuperAugment C++ Rule Engine
*
* Provides rule-based code quality analysis for C++ code,
* supporting different C++ standards and custom rule sets.
*/
import { logger } from '../utils/logger.js';
import { readFile } from 'fs/promises';
/**
* Rule category definitions
*/
export var RuleCategory;
(function (RuleCategory) {
RuleCategory["STYLE"] = "style";
RuleCategory["PERFORMANCE"] = "performance";
RuleCategory["MEMORY"] = "memory";
RuleCategory["SECURITY"] = "security";
RuleCategory["MODERN_CPP"] = "modern-cpp";
RuleCategory["BEST_PRACTICES"] = "best-practices";
RuleCategory["NAMING"] = "naming";
RuleCategory["COMPLEXITY"] = "complexity";
})(RuleCategory || (RuleCategory = {}));
/**
* C++ Rule Engine
*/
export class CppRuleEngine {
rules = new Map();
rulesByCategory = new Map();
constructor() {
this.initializeBuiltInRules();
}
/**
* Load rules for specific C++ standard and custom rules
*/
async loadRules(cppStandard, customRuleNames = []) {
const applicableRules = [];
// Get built-in rules for the C++ standard
for (const rule of this.rules.values()) {
if (rule.cppStandards.includes(cppStandard) || rule.cppStandards.includes('all')) {
applicableRules.push(rule);
}
}
// Add custom rules if specified
for (const ruleName of customRuleNames) {
const rule = this.rules.get(ruleName);
if (rule && !applicableRules.includes(rule)) {
applicableRules.push(rule);
}
}
logger.info(`Loaded ${applicableRules.length} rules for C++ standard ${cppStandard}`, {
cppStandard,
customRules: customRuleNames.length,
totalRules: applicableRules.length,
});
return applicableRules;
}
/**
* Analyze file against loaded rules
*/
async analyzeFile(filePath, content, rules) {
const violations = [];
const lines = content.split('\n');
for (const rule of rules) {
try {
// Check each line against the rule
lines.forEach((line, index) => {
const violation = rule.check(line, index + 1, content, filePath);
if (violation) {
violations.push(violation);
}
});
}
catch (error) {
logger.warn(`Error applying rule ${rule.id} to ${filePath}`, { error });
}
}
return violations;
}
/**
* Get rules by category
*/
getRulesByCategory(category) {
return this.rulesByCategory.get(category) || [];
}
/**
* Get all available rules
*/
getAllRules() {
return Array.from(this.rules.values());
}
/**
* Initialize built-in rules
*/
initializeBuiltInRules() {
// Style rules
this.addRule({
id: 'cpp-style-001',
name: 'Brace Style',
description: 'Enforce consistent brace style',
severity: 'warning',
category: RuleCategory.STYLE,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
if (line.trim().endsWith('{') && line.includes('if ') && !line.includes('\n')) {
return {
rule: 'cpp-style-001',
file: filePath,
line: lineNumber,
column: line.indexOf('{'),
severity: 'warning',
message: 'Opening brace should be on next line',
suggestion: 'Move opening brace to next line for better readability',
};
}
return null;
},
});
this.addRule({
id: 'cpp-style-002',
name: 'Line Length',
description: 'Enforce maximum line length',
severity: 'warning',
category: RuleCategory.STYLE,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
if (line.length > 120) {
return {
rule: 'cpp-style-002',
file: filePath,
line: lineNumber,
column: 120,
severity: 'warning',
message: 'Line too long (>120 characters)',
suggestion: 'Break long lines for better readability',
};
}
return null;
},
});
// Performance rules
this.addRule({
id: 'cpp-perf-001',
name: 'Pass by Reference',
description: 'Large objects should be passed by const reference',
severity: 'warning',
category: RuleCategory.PERFORMANCE,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
const match = line.match(/(\w+)\s+(\w+)\s*\([^)]*std::(string|vector|map|set)\s+(\w+)[^)]*\)/);
if (match && match[1] && !line.includes('const') && !line.includes('&')) {
return {
rule: 'cpp-perf-001',
file: filePath,
line: lineNumber,
column: match[3] ? line.indexOf(match[3]) : 0,
severity: 'warning',
message: 'Large object passed by value',
suggestion: 'Pass large objects by const reference to avoid unnecessary copying',
};
}
return null;
},
});
this.addRule({
id: 'cpp-perf-002',
name: 'String Concatenation',
description: 'Avoid inefficient string concatenation',
severity: 'warning',
category: RuleCategory.PERFORMANCE,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
if (line.includes('string') && line.includes('+') && !line.includes('append')) {
return {
rule: 'cpp-perf-002',
file: filePath,
line: lineNumber,
column: line.indexOf('+'),
severity: 'warning',
message: 'Inefficient string concatenation',
suggestion: 'Use std::string::append() or std::stringstream for better performance',
};
}
return null;
},
});
// Memory rules
this.addRule({
id: 'cpp-mem-001',
name: 'Raw Pointer Usage',
description: 'Avoid raw pointers in favor of smart pointers',
severity: 'warning',
category: RuleCategory.MEMORY,
cppStandards: ['cpp11', 'cpp14', 'cpp17', 'cpp20', 'cpp23'],
check: (line, lineNumber, _content, filePath) => {
if (line.includes('new ') && !line.includes('std::') && !line.includes('//')) {
return {
rule: 'cpp-mem-001',
file: filePath,
line: lineNumber,
column: line.indexOf('new'),
severity: 'warning',
message: 'Raw pointer allocation detected',
suggestion: 'Use std::unique_ptr or std::shared_ptr for automatic memory management',
};
}
return null;
},
});
this.addRule({
id: 'cpp-mem-002',
name: 'Memory Leak Prevention',
description: 'Ensure proper memory deallocation',
severity: 'error',
category: RuleCategory.MEMORY,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
if (line.includes('new ') && !_content.includes('delete') && !line.includes('std::')) {
return {
rule: 'cpp-mem-002',
file: filePath,
line: lineNumber,
column: line.indexOf('new'),
severity: 'error',
message: 'Potential memory leak - new without delete',
suggestion: 'Ensure proper memory deallocation or use RAII',
};
}
return null;
},
});
// Security rules
this.addRule({
id: 'cpp-sec-001',
name: 'Unsafe String Functions',
description: 'Avoid unsafe C string functions',
severity: 'error',
category: RuleCategory.SECURITY,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
const unsafeFunctions = ['strcpy', 'strcat', 'sprintf', 'gets'];
for (const func of unsafeFunctions) {
if (line.includes(`${func}(`)) {
return {
rule: 'cpp-sec-001',
file: filePath,
line: lineNumber,
column: line.indexOf(func),
severity: 'error',
message: `Unsafe function ${func} detected`,
suggestion: `Use safe alternatives like strncpy, strncat, snprintf, or std::string`,
};
}
}
return null;
},
});
this.addRule({
id: 'cpp-sec-002',
name: 'Buffer Overflow Prevention',
description: 'Prevent potential buffer overflows',
severity: 'error',
category: RuleCategory.SECURITY,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
if (line.includes('char ') && line.includes('[') && !line.includes('const')) {
const match = line.match(/char\s+\w+\[(\d+)\]/);
if (match && match[1] && match[1] && parseInt(match[1]) > 1024) {
return {
rule: 'cpp-sec-002',
file: filePath,
line: lineNumber,
column: line.indexOf('char'),
severity: 'error',
message: 'Large fixed-size buffer detected',
suggestion: 'Use std::string or std::vector for dynamic sizing',
};
}
}
return null;
},
});
// Modern C++ rules
this.addRule({
id: 'cpp-modern-001',
name: 'Use nullptr',
description: 'Use nullptr instead of NULL',
severity: 'warning',
category: RuleCategory.MODERN_CPP,
cppStandards: ['cpp11', 'cpp14', 'cpp17', 'cpp20', 'cpp23'],
check: (line, lineNumber, _content, filePath) => {
if (line.includes('NULL') && !line.includes('//') && !line.includes('/*')) {
return {
rule: 'cpp-modern-001',
file: filePath,
line: lineNumber,
column: line.indexOf('NULL'),
severity: 'warning',
message: 'Use nullptr instead of NULL',
suggestion: 'Replace NULL with nullptr for type safety',
};
}
return null;
},
});
this.addRule({
id: 'cpp-modern-002',
name: 'Use auto keyword',
description: 'Consider using auto for type deduction',
severity: 'info',
category: RuleCategory.MODERN_CPP,
cppStandards: ['cpp11', 'cpp14', 'cpp17', 'cpp20', 'cpp23'],
check: (line, lineNumber, _content, filePath) => {
const match = line.match(/^\s*(std::\w+(?:<[^>]+>)?)\s+(\w+)\s*=/);
if (match && match[1] && match[1] && !line.includes('auto')) {
return {
rule: 'cpp-modern-002',
file: filePath,
line: lineNumber,
column: match[1] ? line.indexOf(match[1]) : 0,
severity: 'info',
message: 'Consider using auto for type deduction',
suggestion: 'Use auto to improve code readability and maintainability',
};
}
return null;
},
});
this.addRule({
id: 'cpp-modern-003',
name: 'Range-based for loops',
description: 'Use range-based for loops when possible',
severity: 'info',
category: RuleCategory.MODERN_CPP,
cppStandards: ['cpp11', 'cpp14', 'cpp17', 'cpp20', 'cpp23'],
check: (line, lineNumber, _content, filePath) => {
const match = line.match(/for\s*\(\s*\w+\s+\w+\s*=\s*\w+\.begin\(\)/);
if (match) {
return {
rule: 'cpp-modern-003',
file: filePath,
line: lineNumber,
column: line.indexOf('for'),
severity: 'info',
message: 'Consider using range-based for loop',
suggestion: 'Use range-based for loop for cleaner iteration',
};
}
return null;
},
});
// Best practices rules
this.addRule({
id: 'cpp-best-001',
name: 'Include Guards',
description: 'Header files should have include guards',
severity: 'warning',
category: RuleCategory.BEST_PRACTICES,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
if (filePath.endsWith('.h') || filePath.endsWith('.hpp')) {
if (lineNumber === 1 && !line.includes('#ifndef') && !line.includes('#pragma once')) {
return {
rule: 'cpp-best-001',
file: filePath,
line: lineNumber,
column: 0,
severity: 'warning',
message: 'Header file missing include guard',
suggestion: 'Add #pragma once or #ifndef include guard',
};
}
}
return null;
},
});
this.addRule({
id: 'cpp-best-002',
name: 'Const Correctness',
description: 'Use const where appropriate',
severity: 'warning',
category: RuleCategory.BEST_PRACTICES,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
const match = line.match(/(\w+)\s*\*\s*(\w+)\s*=/);
if (match && match[1] && !line.includes('const') && !line.includes('//')) {
return {
rule: 'cpp-best-002',
file: filePath,
line: lineNumber,
column: match[1] ? line.indexOf(match[1]) : 0,
severity: 'warning',
message: 'Consider using const for immutable data',
suggestion: 'Add const qualifier for better code safety',
};
}
return null;
},
});
// Naming rules
this.addRule({
id: 'cpp-naming-001',
name: 'Class Naming Convention',
description: 'Class names should use PascalCase',
severity: 'warning',
category: RuleCategory.NAMING,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
const match = line.match(/class\s+([a-z]\w*)/);
if (match) {
return {
rule: 'cpp-naming-001',
file: filePath,
line: lineNumber,
column: match[1] ? line.indexOf(match[1]) : 0,
severity: 'warning',
message: 'Class name should use PascalCase',
suggestion: 'Use PascalCase for class names (e.g., MyClass)',
};
}
return null;
},
});
this.addRule({
id: 'cpp-naming-002',
name: 'Constant Naming Convention',
description: 'Constants should use UPPER_CASE',
severity: 'info',
category: RuleCategory.NAMING,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
const match = line.match(/const\s+\w+\s+([a-z]\w*)\s*=/);
if (match && match[1] && !match[1].includes('_')) {
return {
rule: 'cpp-naming-002',
file: filePath,
line: lineNumber,
column: match[1] ? line.indexOf(match[1]) : 0,
severity: 'info',
message: 'Constant should use UPPER_CASE naming',
suggestion: 'Use UPPER_CASE for constant names (e.g., MAX_SIZE)',
};
}
return null;
},
});
// Complexity rules
this.addRule({
id: 'cpp-complex-001',
name: 'Function Length',
description: 'Functions should not be too long',
severity: 'warning',
category: RuleCategory.COMPLEXITY,
cppStandards: ['all'],
check: (line, lineNumber, _content, filePath) => {
// This is a simplified check - would need more sophisticated analysis
if (line.includes('{') && _content.split('\n').length > 100) {
return {
rule: 'cpp-complex-001',
file: filePath,
line: lineNumber,
column: 0,
severity: 'warning',
message: 'Function may be too long',
suggestion: 'Consider breaking down large functions into smaller ones',
};
}
return null;
},
});
// Organize rules by category
this.organizeRulesByCategory();
}
/**
* Add a rule to the engine
*/
addRule(rule) {
this.rules.set(rule.id, rule);
}
/**
* Organize rules by category for easy access
*/
organizeRulesByCategory() {
this.rulesByCategory.clear();
for (const rule of this.rules.values()) {
if (!this.rulesByCategory.has(rule.category)) {
this.rulesByCategory.set(rule.category, []);
}
this.rulesByCategory.get(rule.category).push(rule);
}
}
/**
* Load custom rules from configuration
*/
async loadCustomRules(configPath) {
try {
const config = await readFile(configPath, 'utf-8');
const customRules = JSON.parse(config);
for (const ruleConfig of customRules.rules || []) {
const rule = {
id: ruleConfig.id,
name: ruleConfig.name,
description: ruleConfig.description,
severity: ruleConfig.severity,
category: ruleConfig.category,
cppStandards: ruleConfig.cppStandards || ['all'],
check: this.createRuleFromConfig(ruleConfig),
};
this.addRule(rule);
}
this.organizeRulesByCategory();
logger.info(`Loaded ${customRules.rules?.length || 0} custom rules from ${configPath}`);
}
catch (error) {
logger.warn(`Failed to load custom rules from ${configPath}`, { error });
}
}
/**
* Create a rule check function from configuration
*/
createRuleFromConfig(ruleConfig) {
return (line, lineNumber, _content, filePath) => {
// Simplified rule creation from config
// In a real implementation, this would support more sophisticated rule definitions
if (ruleConfig.pattern && new RegExp(ruleConfig.pattern).test(line)) {
return {
rule: ruleConfig.id,
file: filePath,
line: lineNumber,
column: 0,
severity: ruleConfig.severity,
message: ruleConfig.message || 'Rule violation detected',
suggestion: ruleConfig.suggestion || 'Fix the violation',
};
}
return null;
};
}
/**
* Get rule statistics
*/
getRuleStatistics() {
const rulesByCategory = {};
const rulesBySeverity = {};
for (const rule of this.rules.values()) {
rulesByCategory[rule.category] = (rulesByCategory[rule.category] || 0) + 1;
rulesBySeverity[rule.severity] = (rulesBySeverity[rule.severity] || 0) + 1;
}
return {
totalRules: this.rules.size,
rulesByCategory,
rulesBySeverity,
};
}
}
//# sourceMappingURL=CppRuleEngine.js.map