// CompilationValidator.cs and supporting classes using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace MarketAlly.AIPlugin.Analysis.Plugins { public class CompilationValidator { public async Task ValidateCompilationAsync(string solutionPath) { var result = new CompilationResult { StartTime = DateTime.UtcNow, SolutionPath = solutionPath }; try { // Use dotnet build to validate compilation var processInfo = new ProcessStartInfo { FileName = "dotnet", Arguments = $"build \"{solutionPath}\" --verbosity quiet --nologo", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using var process = Process.Start(processInfo) ?? throw new InvalidOperationException("Failed to start build process"); var output = await process.StandardOutput.ReadToEndAsync(); var error = await process.StandardError.ReadToEndAsync(); await process.WaitForExitAsync(); result.ExitCode = process.ExitCode; result.BuildOutput = output; result.BuildErrors = error; // Parse compilation results result.Status = process.ExitCode == 0 ? CompilationStatus.Success : CompilationStatus.Failed; result.ErrorCount = CountErrors(output + error); result.WarningCount = CountWarnings(output + error); // Extract specific errors and warnings result.Errors = ExtractDiagnostics(output + error, "error"); result.Warnings = ExtractDiagnostics(output + error, "warning"); } catch (Exception ex) { result.Status = CompilationStatus.Failed; result.ErrorMessage = ex.Message; } finally { result.EndTime = DateTime.UtcNow; result.Duration = result.EndTime - result.StartTime; } return result; } private int CountErrors(string buildOutput) { return Regex.Matches(buildOutput, @"error\s+[A-Z]+\d+:", RegexOptions.IgnoreCase).Count; } private int CountWarnings(string buildOutput) { return Regex.Matches(buildOutput, @"warning\s+[A-Z]+\d+:", RegexOptions.IgnoreCase).Count; } private List ExtractDiagnostics(string buildOutput, string type) { var diagnostics = new List(); var pattern = $@"{type}\s+([A-Z]+\d+):\s*(.+?)\s+\[(.+?)\]"; var matches = Regex.Matches(buildOutput, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline); foreach (Match match in matches.Take(10)) // Limit to 10 diagnostics { var diagnostic = new CompilationDiagnostic { Code = match.Groups[1].Value, Message = match.Groups[2].Value.Trim(), File = ExtractFileName(match.Groups[3].Value), Type = type }; // Try to extract line/column from message ExtractLineColumn(diagnostic); diagnostics.Add(diagnostic); } return diagnostics; } private string ExtractFileName(string filePath) { try { return Path.GetFileName(filePath); } catch { return filePath; } } private void ExtractLineColumn(CompilationDiagnostic diagnostic) { // Try to extract line/column from message like "Program.cs(10,5)" var lineColMatch = Regex.Match(diagnostic.Message, @"\((\d+),(\d+)\)"); if (lineColMatch.Success) { if (int.TryParse(lineColMatch.Groups[1].Value, out var line)) diagnostic.Line = line; if (int.TryParse(lineColMatch.Groups[2].Value, out var column)) diagnostic.Column = column; } } } public class CompilationDiagnostic { public string Code { get; set; } = string.Empty; public string Message { get; set; } = string.Empty; public string File { get; set; } = string.Empty; public int Line { get; set; } public int Column { get; set; } public string Type { get; set; } = string.Empty; public string Severity { get; set; } = string.Empty; public string Category { get; set; } = string.Empty; public string HelpLink { get; set; } = string.Empty; public bool IsWarningAsError { get; set; } } // Alternative implementation using MSBuild APIs for more detailed analysis public class AdvancedCompilationValidator { public async Task ValidateWithMSBuildAsync(string solutionPath) { var result = new CompilationResult { StartTime = DateTime.UtcNow, SolutionPath = solutionPath }; try { // This would require Microsoft.Build packages // Keeping the simpler dotnet build approach for now // but providing structure for future enhancement result = await new CompilationValidator().ValidateCompilationAsync(solutionPath); } catch (Exception ex) { result.Status = CompilationStatus.Failed; result.ErrorMessage = ex.Message; } finally { result.EndTime = DateTime.UtcNow; result.Duration = result.EndTime - result.StartTime; } return result; } } // Compilation analysis extensions public static class CompilationResultExtensions { public static CompilationResult WithPreviousErrorCount(this CompilationResult result, int previousCount) { result.PreviousErrorCount = previousCount; return result; } public static bool ImprovedFrom(this CompilationResult result, CompilationResult previous) { return result.ErrorCount < previous.ErrorCount || (result.ErrorCount == previous.ErrorCount && result.WarningCount < previous.WarningCount); } public static bool IsSuccessful(this CompilationResult result) { return result.Status == CompilationStatus.Success || result.Status == CompilationStatus.Warning; } public static bool HasErrors(this CompilationResult result) { return result.ErrorCount > 0; } public static bool HasWarnings(this CompilationResult result) { return result.WarningCount > 0; } public static double GetErrorReduction(this CompilationResult result) { if (!result.PreviousErrorCount.HasValue || result.PreviousErrorCount.Value == 0) return 0.0; return (double)(result.PreviousErrorCount.Value - result.ErrorCount) / result.PreviousErrorCount.Value; } public static string GetSummary(this CompilationResult result) { return $"{result.Status} - {result.ErrorCount} errors, {result.WarningCount} warnings ({result.Duration.TotalSeconds:F1}s)"; } public static List GetCriticalIssues(this CompilationResult result) { return result.Errors.Where(e => IsCriticalError(e.Code)).ToList(); } private static bool IsCriticalError(string errorCode) { // Define critical error codes that should be prioritized var criticalCodes = new[] { "CS0103", // Name does not exist "CS0246", // Type or namespace not found "CS0029", // Cannot implicitly convert "CS1002", // Syntax error "CS1513", // } expected }; return criticalCodes.Contains(errorCode); } } // Compilation metrics for learning analysis public class CompilationMetrics { public DateTime Timestamp { get; set; } = DateTime.UtcNow; public string SolutionPath { get; set; } = string.Empty; public CompilationStatus Status { get; set; } public int ErrorCount { get; set; } public int WarningCount { get; set; } public TimeSpan BuildDuration { get; set; } public Dictionary ErrorsByType { get; set; } = new(); public Dictionary WarningsByType { get; set; } = new(); public List ModifiedFiles { get; set; } = new(); public string BuildConfiguration { get; set; } = "Debug"; public string TargetFramework { get; set; } = string.Empty; public static CompilationMetrics FromResult(CompilationResult result, List? modifiedFiles = null) { var metrics = new CompilationMetrics { SolutionPath = result.SolutionPath, Status = result.Status, ErrorCount = result.ErrorCount, WarningCount = result.WarningCount, BuildDuration = result.Duration, ModifiedFiles = modifiedFiles ?? new List() }; // Group errors and warnings by type metrics.ErrorsByType = result.Errors .GroupBy(e => e.Code) .ToDictionary(g => g.Key, g => g.Count()); metrics.WarningsByType = result.Warnings .GroupBy(w => w.Code) .ToDictionary(g => g.Key, g => g.Count()); return metrics; } } // Build output parser for enhanced diagnostics public class BuildOutputParser { public static List ParseMSBuildOutput(string buildOutput) { var diagnostics = new List(); var lines = buildOutput.Split('\n', StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var diagnostic = TryParseDiagnosticLine(line.Trim()); if (diagnostic != null) { diagnostics.Add(diagnostic); } } return diagnostics; } private static CompilationDiagnostic? TryParseDiagnosticLine(string line) { // MSBuild diagnostic format: File(line,column): error/warning CODE: Message var pattern = @"^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+([A-Z]+\d+):\s+(.+)$"; var match = Regex.Match(line, pattern, RegexOptions.IgnoreCase); if (!match.Success) return null; return new CompilationDiagnostic { File = Path.GetFileName(match.Groups[1].Value), Line = int.Parse(match.Groups[2].Value), Column = int.Parse(match.Groups[3].Value), Type = match.Groups[4].Value.ToLower(), Code = match.Groups[5].Value, Message = match.Groups[6].Value.Trim(), Severity = DetermineSeverity(match.Groups[4].Value, match.Groups[5].Value) }; } private static string DetermineSeverity(string type, string code) { if (type.Equals("error", StringComparison.OrdinalIgnoreCase)) return "Error"; // Some warnings are more critical than others var highPriorityWarnings = new[] { "CS0162", "CS0219", "CS0414" }; if (highPriorityWarnings.Contains(code)) return "High"; return "Normal"; } } }