using MarketAlly.AIPlugin; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace MarketAlly.AIPlugin.Analysis.Plugins { [AIPlugin("PerformanceAnalyzer", "Identifies performance bottlenecks and optimization opportunities in code")] public class PerformanceAnalyzerPlugin : IAIPlugin { [AIParameter("Full path to the file or directory to analyze", required: true)] public string Path { get; set; } = string.Empty; [AIParameter("Analyze algorithm complexity", required: false)] public bool AnalyzeComplexity { get; set; } = true; [AIParameter("Check for inefficient loops and iterations", required: false)] public bool CheckLoops { get; set; } = true; [AIParameter("Analyze memory allocation patterns", required: false)] public bool AnalyzeMemory { get; set; } = true; [AIParameter("Check for database query optimization opportunities", required: false)] public bool CheckDatabase { get; set; } = true; [AIParameter("Suggest caching opportunities", required: false)] public bool SuggestCaching { get; set; } = true; [AIParameter("Performance analysis depth: basic, detailed, comprehensive", required: false)] public string AnalysisDepth { get; set; } = "detailed"; public IReadOnlyDictionary SupportedParameters => new Dictionary { ["path"] = typeof(string), ["analyzeComplexity"] = typeof(bool), ["checkLoops"] = typeof(bool), ["analyzeMemory"] = typeof(bool), ["checkDatabase"] = typeof(bool), ["suggestCaching"] = typeof(bool), ["analysisDepth"] = typeof(string) }; public async Task ExecuteAsync(IReadOnlyDictionary parameters) { try { // Extract parameters string path = parameters["path"]?.ToString() ?? string.Empty; bool analyzeComplexity = GetBoolParameter(parameters, "analyzeComplexity", true); bool checkLoops = GetBoolParameter(parameters, "checkLoops", true); bool analyzeMemory = GetBoolParameter(parameters, "analyzeMemory", true); bool checkDatabase = GetBoolParameter(parameters, "checkDatabase", true); bool suggestCaching = GetBoolParameter(parameters, "suggestCaching", true); string analysisDepth = parameters.TryGetValue("analysisDepth", out var depth) ? depth?.ToString() ?? "detailed" : "detailed"; // Validate path if (!File.Exists(path) && !Directory.Exists(path)) { return new AIPluginResult( new FileNotFoundException($"Path not found: {path}"), "Path not found" ); } // Get files to analyze var filesToAnalyze = GetFilesToAnalyze(path); if (!filesToAnalyze.Any()) { return new AIPluginResult( new InvalidOperationException("No C# files found to analyze"), "No files found" ); } // Initialize analyzers based on depth var analyzers = GetAnalyzersForDepth(analysisDepth); // Analyze performance for each file var performanceIssues = new List(); var optimizationOpportunities = new List(); var cachingOpportunities = new List(); var complexityIssues = new List(); var loopOptimizations = new List(); var memoryOptimizations = new List(); var databaseOptimizations = new List(); foreach (string filePath in filesToAnalyze) { var fileResult = await AnalyzeFilePerformance( filePath, analyzeComplexity, checkLoops, analyzeMemory, checkDatabase, suggestCaching, analyzers); performanceIssues.AddRange(fileResult.PerformanceIssues); optimizationOpportunities.AddRange(fileResult.OptimizationOpportunities); cachingOpportunities.AddRange(fileResult.CachingOpportunities); complexityIssues.AddRange(fileResult.ComplexityIssues); loopOptimizations.AddRange(fileResult.LoopOptimizations); memoryOptimizations.AddRange(fileResult.MemoryOptimizations); databaseOptimizations.AddRange(fileResult.DatabaseOptimizations); } // Calculate performance score (0-100, higher is better) int performanceScore = CalculatePerformanceScore( performanceIssues, optimizationOpportunities, filesToAnalyze.Count); // Generate recommendations var recommendations = GeneratePerformanceRecommendations( performanceIssues, optimizationOpportunities, cachingOpportunities); var result = new { Path = path, FilesAnalyzed = filesToAnalyze.Count, AnalysisDepth = analysisDepth, PerformanceScore = performanceScore, ComplexityIssues = analyzeComplexity ? complexityIssues.Select(i => new { i.MethodName, i.ClassName, i.FilePath, i.LineNumber, i.AlgorithmicComplexity, i.Description, i.Severity, i.RecommendedAction }).ToList() : null, LoopOptimizations = checkLoops ? loopOptimizations.Select(l => new { l.MethodName, l.ClassName, l.FilePath, l.LineNumber, l.LoopType, l.IssueType, l.Description, l.Severity, l.Suggestion }).ToList() : null, MemoryOptimizations = analyzeMemory ? memoryOptimizations.Select(m => new { m.MethodName, m.ClassName, m.FilePath, m.LineNumber, m.IssueType, m.Description, m.EstimatedImpact, m.Suggestion }).ToList() : null, DatabaseOptimizations = checkDatabase ? databaseOptimizations.Select(d => new { d.MethodName, d.ClassName, d.FilePath, d.LineNumber, d.QueryType, d.IssueType, d.Description, d.Severity, d.Suggestion }).ToList() : null, CachingOpportunities = suggestCaching ? cachingOpportunities.Select(c => new { c.MethodName, c.ClassName, c.FilePath, c.LineNumber, c.CacheType, c.Rationale, c.EstimatedBenefit, c.Implementation }).ToList() : null, Recommendations = recommendations, Summary = new { TotalIssues = performanceIssues.Count, HighSeverityIssues = performanceIssues.Count(i => i.Severity == "High"), OptimizationOpportunities = optimizationOpportunities.Count, CachingOpportunities = cachingOpportunities.Count, EstimatedPerformanceGain = CalculateEstimatedGain(optimizationOpportunities), PriorityActions = GetPriorityActions(performanceIssues, optimizationOpportunities) } }; return new AIPluginResult(result, $"Performance analysis completed for {filesToAnalyze.Count} files. " + $"Found {performanceIssues.Count} issues and {optimizationOpportunities.Count} optimization opportunities."); } catch (Exception ex) { return new AIPluginResult(ex, "Failed to analyze performance"); } } private async Task AnalyzeFilePerformance( string filePath, bool analyzeComplexity, bool checkLoops, bool analyzeMemory, bool checkDatabase, bool suggestCaching, List analyzers) { var sourceCode = await File.ReadAllTextAsync(filePath); var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode, path: filePath); var root = await syntaxTree.GetRootAsync(); var result = new FilePerformanceResult { FilePath = filePath, PerformanceIssues = new List(), OptimizationOpportunities = new List(), CachingOpportunities = new List(), ComplexityIssues = new List(), LoopOptimizations = new List(), MemoryOptimizations = new List(), DatabaseOptimizations = new List() }; // Run enabled analyzers foreach (var analyzer in analyzers) { var analysis = await analyzer.AnalyzeAsync(root, filePath, sourceCode); result.PerformanceIssues.AddRange(analysis.PerformanceIssues); result.OptimizationOpportunities.AddRange(analysis.OptimizationOpportunities); if (analyzer is ComplexityAnalyzer && analyzeComplexity) result.ComplexityIssues.AddRange(analysis.ComplexityIssues); if (analyzer is LoopAnalyzer && checkLoops) result.LoopOptimizations.AddRange(analysis.LoopOptimizations); if (analyzer is MemoryAnalyzer && analyzeMemory) result.MemoryOptimizations.AddRange(analysis.MemoryOptimizations); if (analyzer is DatabaseAnalyzer && checkDatabase) result.DatabaseOptimizations.AddRange(analysis.DatabaseOptimizations); if (analyzer is CachingAnalyzer && suggestCaching) result.CachingOpportunities.AddRange(analysis.CachingOpportunities); } return result; } private List GetAnalyzersForDepth(string depth) { var analyzers = new List(); switch (depth.ToLowerInvariant()) { case "basic": analyzers.Add(new LoopAnalyzer()); analyzers.Add(new BasicMemoryAnalyzer()); break; case "detailed": analyzers.Add(new ComplexityAnalyzer()); analyzers.Add(new LoopAnalyzer()); analyzers.Add(new MemoryAnalyzer()); analyzers.Add(new DatabaseAnalyzer()); break; case "comprehensive": analyzers.Add(new ComplexityAnalyzer()); analyzers.Add(new LoopAnalyzer()); analyzers.Add(new MemoryAnalyzer()); analyzers.Add(new DatabaseAnalyzer()); analyzers.Add(new CachingAnalyzer()); analyzers.Add(new AsyncAnalyzer()); analyzers.Add(new CollectionAnalyzer()); break; default: goto case "detailed"; } return analyzers; } private List GetFilesToAnalyze(string path) { var files = new List(); if (File.Exists(path)) { if (path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)) { files.Add(path); } } else if (Directory.Exists(path)) { files.AddRange(Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories) .Where(f => !f.Contains("\\bin\\") && !f.Contains("\\obj\\") && !f.EndsWith(".Designer.cs") && !f.EndsWith(".g.cs"))); } return files; } private int CalculatePerformanceScore(List issues, List opportunities, int totalFiles) { if (totalFiles == 0) return 100; // Base score int score = 100; // Deduct points for issues var highSeverityIssues = issues.Count(i => i.Severity == "High"); var mediumSeverityIssues = issues.Count(i => i.Severity == "Medium"); var lowSeverityIssues = issues.Count(i => i.Severity == "Low"); score -= highSeverityIssues * 15; // -15 points per high severity score -= mediumSeverityIssues * 8; // -8 points per medium severity score -= lowSeverityIssues * 3; // -3 points per low severity // Additional deduction for high opportunity count if (opportunities.Count > totalFiles * 5) { score -= 20; // Penalty for many missed opportunities } return Math.Max(0, Math.Min(100, score)); } private List GeneratePerformanceRecommendations( List issues, List opportunities, List cachingOpportunities) { var recommendations = new List(); if (!issues.Any() && !opportunities.Any()) { recommendations.Add("✅ No significant performance issues detected."); return recommendations; } recommendations.Add("🚀 Performance Optimization Recommendations:"); // High priority issues var highPriorityIssues = issues.Where(i => i.Severity == "High").ToList(); if (highPriorityIssues.Any()) { recommendations.Add($"🔥 HIGH PRIORITY - Address {highPriorityIssues.Count} critical performance issue(s):"); foreach (var issue in highPriorityIssues.Take(5)) { recommendations.Add($" • {issue.ClassName}.{issue.MethodName}: {issue.Description}"); } } // Memory optimizations var memoryIssues = issues.Where(i => i.Category == "Memory").ToList(); if (memoryIssues.Any()) { recommendations.Add($"💾 Memory Optimization ({memoryIssues.Count} opportunities):"); recommendations.Add(" • Use StringBuilder for string concatenations"); recommendations.Add(" • Implement IDisposable for resource management"); recommendations.Add(" • Consider object pooling for frequently allocated objects"); } // Loop optimizations var loopIssues = issues.Where(i => i.Category == "Loop").ToList(); if (loopIssues.Any()) { recommendations.Add($"🔄 Loop Optimization ({loopIssues.Count} opportunities):"); recommendations.Add(" • Cache collection.Count in loop variables"); recommendations.Add(" • Use for loops instead of foreach where appropriate"); recommendations.Add(" • Consider LINQ optimizations or alternatives"); } // Database optimizations var dbIssues = issues.Where(i => i.Category == "Database").ToList(); if (dbIssues.Any()) { recommendations.Add($"🗄️ Database Optimization ({dbIssues.Count} opportunities):"); recommendations.Add(" • Use async database operations"); recommendations.Add(" • Implement connection pooling"); recommendations.Add(" • Consider query optimization and indexing"); } // Caching opportunities if (cachingOpportunities.Any()) { var highBenefitCaching = cachingOpportunities.Where(c => c.EstimatedBenefit == "High").ToList(); if (highBenefitCaching.Any()) { recommendations.Add($"⚡ High-Impact Caching ({highBenefitCaching.Count} opportunities):"); foreach (var cache in highBenefitCaching.Take(3)) { recommendations.Add($" • {cache.MethodName}: {cache.Rationale}"); } } } // General best practices recommendations.Add("📋 General Performance Best Practices:"); recommendations.Add(" • Use async/await for I/O operations"); recommendations.Add(" • Minimize allocations in hot paths"); recommendations.Add(" • Profile your application under realistic load"); recommendations.Add(" • Consider using Span and Memory for high-performance scenarios"); return recommendations; } private string CalculateEstimatedGain(List opportunities) { var highImpact = opportunities.Count(o => o.EstimatedImpact == "High"); var mediumImpact = opportunities.Count(o => o.EstimatedImpact == "Medium"); var lowImpact = opportunities.Count(o => o.EstimatedImpact == "Low"); if (highImpact > 0) return $"Significant (>{highImpact} high-impact optimization{(highImpact > 1 ? "s" : "")})"; if (mediumImpact > 0) return $"Moderate ({mediumImpact} medium-impact optimization{(mediumImpact > 1 ? "s" : "")})"; if (lowImpact > 0) return $"Minor ({lowImpact} low-impact optimization{(lowImpact > 1 ? "s" : "")})"; return "Minimal"; } private List GetPriorityActions(List issues, List opportunities) { var actions = new List(); // Top 3 high severity issues var topIssues = issues .Where(i => i.Severity == "High") .OrderBy(i => i.MethodName) .Take(3); foreach (var issue in topIssues) { actions.Add($"Fix {issue.Category.ToLower()} issue in {issue.ClassName}.{issue.MethodName}"); } // Top 2 high impact opportunities var topOpportunities = opportunities .Where(o => o.EstimatedImpact == "High") .Take(2); foreach (var opp in topOpportunities) { actions.Add($"Implement {opp.OptimizationType} in {opp.ClassName}.{opp.MethodName}"); } if (!actions.Any()) { actions.Add("No critical performance issues identified"); } return actions; } private bool GetBoolParameter(IReadOnlyDictionary parameters, string key, bool defaultValue) { return parameters.TryGetValue(key, out var value) ? Convert.ToBoolean(value) : defaultValue; } } // Supporting interfaces and classes public interface IPerformanceAnalyzer { Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode); } public class PerformanceAnalysisResult { public List PerformanceIssues { get; set; } = new(); public List OptimizationOpportunities { get; set; } = new(); public List CachingOpportunities { get; set; } = new(); public List ComplexityIssues { get; set; } = new(); public List LoopOptimizations { get; set; } = new(); public List MemoryOptimizations { get; set; } = new(); public List DatabaseOptimizations { get; set; } = new(); } public class PerformanceIssue { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string Category { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Severity { get; set; } = string.Empty; public string RecommendedAction { get; set; } = string.Empty; } public class OptimizationOpportunity { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string OptimizationType { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string EstimatedImpact { get; set; } = string.Empty; public string Suggestion { get; set; } = string.Empty; } public class ComplexityIssue { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string AlgorithmicComplexity { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Severity { get; set; } = string.Empty; public string RecommendedAction { get; set; } = string.Empty; } public class LoopOptimization { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string LoopType { get; set; } = string.Empty; public string IssueType { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Severity { get; set; } = string.Empty; public string Suggestion { get; set; } = string.Empty; } public class MemoryOptimization { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string IssueType { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string EstimatedImpact { get; set; } = string.Empty; public string Suggestion { get; set; } = string.Empty; } public class DatabaseOptimization { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string QueryType { get; set; } = string.Empty; public string IssueType { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Severity { get; set; } = string.Empty; public string Suggestion { get; set; } = string.Empty; } public class CachingOpportunity { public string MethodName { get; set; } = string.Empty; public string ClassName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } public string CacheType { get; set; } = string.Empty; public string Rationale { get; set; } = string.Empty; public string EstimatedBenefit { get; set; } = string.Empty; public string Implementation { get; set; } = string.Empty; } public class FilePerformanceResult { public string FilePath { get; set; } = string.Empty; public List PerformanceIssues { get; set; } = new(); public List OptimizationOpportunities { get; set; } = new(); public List CachingOpportunities { get; set; } = new(); public List ComplexityIssues { get; set; } = new(); public List LoopOptimizations { get; set; } = new(); public List MemoryOptimizations { get; set; } = new(); public List DatabaseOptimizations { get; set; } = new(); } // Concrete analyzer implementations public class LoopAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; // Analyze for loops var forLoops = method.DescendantNodes().OfType(); foreach (var forLoop in forLoops) { AnalyzeForLoop(forLoop, className, methodName, filePath, result); } // Analyze foreach loops var foreachLoops = method.DescendantNodes().OfType(); foreach (var foreachLoop in foreachLoops) { AnalyzeForeachLoop(foreachLoop, className, methodName, filePath, result); } // Analyze while loops var whileLoops = method.DescendantNodes().OfType(); foreach (var whileLoop in whileLoops) { AnalyzeWhileLoop(whileLoop, className, methodName, filePath, result); } } return Task.FromResult(result); } private void AnalyzeForLoop(ForStatementSyntax forLoop, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = forLoop.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Check for inefficient condition evaluation if (forLoop.Condition != null) { var conditionText = forLoop.Condition.ToString(); if (conditionText.Contains(".Count") || conditionText.Contains(".Length")) { result.LoopOptimizations.Add(new LoopOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = lineNumber, LoopType = "for", IssueType = "Repeated property access", Description = "Loop condition evaluates collection.Count or array.Length on each iteration", Severity = "Medium", Suggestion = "Cache the collection size in a local variable before the loop" }); } } } private void AnalyzeForeachLoop(ForEachStatementSyntax foreachLoop, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = foreachLoop.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Check if foreach is used with indexed access inside var body = foreachLoop.Statement; if (body != null) { var elementAccesses = body.DescendantNodes().OfType(); if (elementAccesses.Any()) { result.LoopOptimizations.Add(new LoopOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = lineNumber, LoopType = "foreach", IssueType = "Indexed access in foreach", Description = "Using indexed access inside foreach loop - consider using for loop instead", Severity = "Low", Suggestion = "Use for loop with index when you need indexed access" }); } } } private void AnalyzeWhileLoop(WhileStatementSyntax whileLoop, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = whileLoop.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Basic analysis - could be expanded var conditionText = whileLoop.Condition.ToString(); if (conditionText.Contains("true")) { result.PerformanceIssues.Add(new PerformanceIssue { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = lineNumber, Category = "Loop", Description = "Infinite loop detected - ensure proper exit condition", Severity = "High", RecommendedAction = "Add proper termination condition or use different loop construct" }); } } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } public class MemoryAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; AnalyzeStringConcatenation(method, className, methodName, filePath, result); AnalyzeDisposablePatterns(method, className, methodName, filePath, result); AnalyzeCollectionUsage(method, className, methodName, filePath, result); } return Task.FromResult(result); } private void AnalyzeStringConcatenation(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for string concatenation in loops var loops = method.DescendantNodes().Where(n => n is ForStatementSyntax || n is ForEachStatementSyntax || n is WhileStatementSyntax); foreach (var loop in loops) { var assignments = loop.DescendantNodes().OfType() .Where(a => a.OperatorToken.IsKind(SyntaxKind.PlusEqualsToken)); foreach (var assignment in assignments) { if (IsStringType(assignment.Left)) { result.MemoryOptimizations.Add(new MemoryOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = assignment.GetLocation().GetLineSpan().StartLinePosition.Line + 1, IssueType = "String concatenation in loop", Description = "String concatenation inside loop creates multiple temporary strings", EstimatedImpact = "High", Suggestion = "Use StringBuilder for multiple string concatenations" }); } } } } private void AnalyzeDisposablePatterns(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for new expressions that might create disposable objects var objectCreations = method.DescendantNodes().OfType(); foreach (var creation in objectCreations) { var typeName = creation.Type.ToString(); // Check for common disposable types not in using statements if (IsDisposableType(typeName) && !IsInUsingStatement(creation)) { result.MemoryOptimizations.Add(new MemoryOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = creation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, IssueType = "Disposable not in using statement", Description = $"Creating {typeName} without proper disposal pattern", EstimatedImpact = "Medium", Suggestion = "Wrap disposable objects in using statements or implement try-finally" }); } } } private void AnalyzeCollectionUsage(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for inefficient collection operations var invocations = method.DescendantNodes().OfType(); foreach (var invocation in invocations) { var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; if (memberAccess != null) { var methodCall = memberAccess.Name.Identifier.ValueText; // Check for inefficient LINQ operations if (methodCall == "Count" && HasLinqWhere(invocation)) { result.MemoryOptimizations.Add(new MemoryOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, IssueType = "Inefficient LINQ usage", Description = "Using Where().Count() instead of Count(predicate)", EstimatedImpact = "Low", Suggestion = "Use Count(predicate) instead of Where(predicate).Count()" }); } if (methodCall == "Any" && HasLinqWhere(invocation)) { result.MemoryOptimizations.Add(new MemoryOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, IssueType = "Inefficient LINQ usage", Description = "Using Where().Any() instead of Any(predicate)", EstimatedImpact = "Low", Suggestion = "Use Any(predicate) instead of Where(predicate).Any()" }); } } } } private bool IsStringType(SyntaxNode node) { // Simple heuristic - could be improved with semantic analysis return node.ToString().Contains("string") || node.ToString().Contains("String"); } private bool IsDisposableType(string typeName) { var disposableTypes = new[] { "FileStream", "StreamReader", "StreamWriter", "HttpClient", "SqlConnection", "SqlCommand", "DbConnection", "DbCommand", "BinaryReader", "BinaryWriter", "StringReader", "StringWriter", "MemoryStream", "NetworkStream" }; return disposableTypes.Any(dt => typeName.Contains(dt)); } private bool IsInUsingStatement(SyntaxNode node) { return node.Ancestors().OfType().Any(); } private bool HasLinqWhere(InvocationExpressionSyntax invocation) { var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; if (memberAccess?.Expression is InvocationExpressionSyntax parentInvocation) { var parentMemberAccess = parentInvocation.Expression as MemberAccessExpressionSyntax; return parentMemberAccess?.Name.Identifier.ValueText == "Where"; } return false; } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } public class BasicMemoryAnalyzer : MemoryAnalyzer { // Simplified version for basic analysis } public class DatabaseAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; AnalyzeDatabaseOperations(method, className, methodName, filePath, result); AnalyzeAsyncPatterns(method, className, methodName, filePath, result); } return Task.FromResult(result); } private void AnalyzeDatabaseOperations(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for database-related method calls var invocations = method.DescendantNodes().OfType(); foreach (var invocation in invocations) { var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; if (memberAccess != null) { var methodCall = memberAccess.Name.Identifier.ValueText; // Check for synchronous database operations if (IsDatabaseMethod(methodCall) && !IsAsyncMethod(method)) { result.DatabaseOptimizations.Add(new DatabaseOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, QueryType = "Database Operation", IssueType = "Synchronous database call", Description = $"Synchronous {methodCall} call can block thread", Severity = "Medium", Suggestion = $"Use {methodCall}Async() and make method async" }); } // Check for potential N+1 queries if (IsInLoop(invocation) && IsDatabaseMethod(methodCall)) { result.DatabaseOptimizations.Add(new DatabaseOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, QueryType = "Database Operation", IssueType = "Potential N+1 query", Description = "Database query inside loop may cause N+1 query problem", Severity = "High", Suggestion = "Consider batching queries or using joins/includes" }); } } } } private void AnalyzeAsyncPatterns(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for .Result or .Wait() calls on async operations var memberAccesses = method.DescendantNodes().OfType(); foreach (var memberAccess in memberAccesses) { if (memberAccess.Name.Identifier.ValueText == "Result" || memberAccess.Name.Identifier.ValueText == "Wait") { result.DatabaseOptimizations.Add(new DatabaseOptimization { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = memberAccess.GetLocation().GetLineSpan().StartLinePosition.Line + 1, QueryType = "Async Pattern", IssueType = "Blocking async operation", Description = "Using .Result or .Wait() can cause deadlocks", Severity = "High", Suggestion = "Use await instead of .Result/.Wait()" }); } } } private bool IsDatabaseMethod(string methodName) { var dbMethods = new[] { "ExecuteScalar", "ExecuteNonQuery", "ExecuteReader", "Query", "QueryFirst", "QuerySingle", "Execute", "SaveChanges", "Add", "Update", "Remove", "Find" }; return dbMethods.Contains(methodName); } private bool IsAsyncMethod(MethodDeclarationSyntax method) { return method.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)); } private bool IsInLoop(SyntaxNode node) { return node.Ancestors().Any(a => a is ForStatementSyntax || a is ForEachStatementSyntax || a is WhileStatementSyntax); } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } public class CachingAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; AnalyzeCachingOpportunities(method, className, methodName, filePath, result); } return Task.FromResult(result); } private void AnalyzeCachingOpportunities(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for expensive operations that could benefit from caching var invocations = method.DescendantNodes().OfType(); foreach (var invocation in invocations) { var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; if (memberAccess != null) { var methodCall = memberAccess.Name.Identifier.ValueText; // Database queries if (IsDatabaseMethod(methodCall) && HasSimpleParameters(invocation)) { result.CachingOpportunities.Add(new CachingOpportunity { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, CacheType = "Query Result Cache", Rationale = "Database query with deterministic parameters", EstimatedBenefit = "High", Implementation = "Consider using IMemoryCache or distributed cache" }); } // HTTP calls if (IsHttpMethod(methodCall)) { result.CachingOpportunities.Add(new CachingOpportunity { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, CacheType = "HTTP Response Cache", Rationale = "External HTTP call that could be cached", EstimatedBenefit = "Medium", Implementation = "Implement HTTP response caching with appropriate TTL" }); } // Expensive computations if (IsExpensiveComputation(methodCall)) { result.CachingOpportunities.Add(new CachingOpportunity { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, CacheType = "Computation Cache", Rationale = "Expensive computation that could be memoized", EstimatedBenefit = "Medium", Implementation = "Consider memoization pattern or result caching" }); } } } } private bool IsDatabaseMethod(string methodName) { return new[] { "Query", "QueryFirst", "QuerySingle", "Find", "Where", "Select" }.Contains(methodName); } private bool IsHttpMethod(string methodName) { return new[] { "GetAsync", "PostAsync", "PutAsync", "DeleteAsync", "SendAsync" }.Contains(methodName); } private bool IsExpensiveComputation(string methodName) { return new[] { "Calculate", "Compute", "Process", "Transform", "Parse", "Serialize" } .Any(keyword => methodName.Contains(keyword)); } private bool HasSimpleParameters(InvocationExpressionSyntax invocation) { // Simple heuristic: check if parameters are likely to be cacheable return invocation.ArgumentList.Arguments.Count <= 3; } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } public class ComplexityAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; AnalyzeAlgorithmicComplexity(method, className, methodName, filePath, result); } return Task.FromResult(result); } private void AnalyzeAlgorithmicComplexity(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; var complexity = EstimateComplexity(method); if (complexity.StartsWith("O(n²)") || complexity.StartsWith("O(n³)")) { result.ComplexityIssues.Add(new ComplexityIssue { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = lineNumber, AlgorithmicComplexity = complexity, Description = $"Method has {complexity} algorithmic complexity", Severity = complexity.StartsWith("O(n³)") ? "High" : "Medium", RecommendedAction = "Consider optimizing algorithm or using more efficient data structures" }); } } private string EstimateComplexity(MethodDeclarationSyntax method) { var nestedLoopDepth = CalculateNestedLoopDepth(method); return nestedLoopDepth switch { 0 => "O(1)", 1 => "O(n)", 2 => "O(n²)", 3 => "O(n³)", _ => $"O(n^{nestedLoopDepth})" }; } private int CalculateNestedLoopDepth(SyntaxNode node) { int maxDepth = 0; CalculateNestedDepth(node, 0, ref maxDepth); return maxDepth; } private void CalculateNestedDepth(SyntaxNode node, int currentDepth, ref int maxDepth) { if (node is ForStatementSyntax || node is ForEachStatementSyntax || node is WhileStatementSyntax) { currentDepth++; maxDepth = Math.Max(maxDepth, currentDepth); } foreach (var child in node.ChildNodes()) { CalculateNestedDepth(child, currentDepth, ref maxDepth); } } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } public class AsyncAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; AnalyzeAsyncUsage(method, className, methodName, filePath, result); } return Task.FromResult(result); } private void AnalyzeAsyncUsage(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Check for missing async/await patterns var awaitExpressions = method.DescendantNodes().OfType(); var isAsync = method.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)); if (awaitExpressions.Any() && !isAsync) { result.PerformanceIssues.Add(new PerformanceIssue { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = lineNumber, Category = "Async", Description = "Method uses await but is not marked as async", Severity = "High", RecommendedAction = "Add async modifier to method signature" }); } } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } public class CollectionAnalyzer : IPerformanceAnalyzer { public Task AnalyzeAsync(SyntaxNode root, string filePath, string sourceCode) { var result = new PerformanceAnalysisResult(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var className = GetClassName(method); var methodName = method.Identifier.ValueText; AnalyzeCollectionUsage(method, className, methodName, filePath, result); } return Task.FromResult(result); } private void AnalyzeCollectionUsage(MethodDeclarationSyntax method, string className, string methodName, string filePath, PerformanceAnalysisResult result) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; // Look for inefficient collection operations var invocations = method.DescendantNodes().OfType(); foreach (var invocation in invocations) { var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; if (memberAccess != null) { var methodCall = memberAccess.Name.Identifier.ValueText; // Multiple enumeration if (methodCall == "ToList" && IsChainedWithLinq(invocation)) { result.OptimizationOpportunities.Add(new OptimizationOpportunity { MethodName = methodName, ClassName = className, FilePath = filePath, LineNumber = invocation.GetLocation().GetLineSpan().StartLinePosition.Line + 1, OptimizationType = "Collection optimization", Description = "Multiple LINQ operations - consider optimizing query", EstimatedImpact = "Medium", Suggestion = "Combine LINQ operations or use more efficient approach" }); } } } } private bool IsChainedWithLinq(InvocationExpressionSyntax invocation) { var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; return memberAccess?.Expression is InvocationExpressionSyntax; } private string GetClassName(SyntaxNode node) { return node.Ancestors().OfType().FirstOrDefault()?.Identifier.ValueText ?? "Unknown"; } } }