268 lines
8.0 KiB
C#
Executable File
268 lines
8.0 KiB
C#
Executable File
using MarketAlly.AIPlugin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MarketAlly.AIPlugin.Analysis.Plugins
|
|
{
|
|
[AIPlugin("WarningsAnalysis", "Analyzes and attempts to fix compiler warnings in .NET solutions")]
|
|
public class WarningsAnalysisPlugin : IAIPlugin
|
|
{
|
|
[AIParameter("Solution path to analyze", required: true)]
|
|
public string SolutionPath { get; set; } = string.Empty;
|
|
|
|
[AIParameter("Analysis phase (initial/final)", required: false)]
|
|
public string Phase { get; set; } = "initial";
|
|
|
|
[AIParameter("Maximum attempts per warning", required: false)]
|
|
public int MaxAttempts { get; set; } = 3;
|
|
|
|
[AIParameter("Apply fixes automatically", required: false)]
|
|
public bool ApplyFixes { get; set; } = false;
|
|
|
|
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
|
|
{
|
|
["solutionPath"] = typeof(string),
|
|
["phase"] = typeof(string),
|
|
["maxAttempts"] = typeof(int),
|
|
["applyFixes"] = typeof(bool)
|
|
};
|
|
|
|
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
var solutionPath = parameters["solutionPath"].ToString();
|
|
var phase = parameters.GetValueOrDefault("phase", "initial").ToString();
|
|
var maxAttempts = Convert.ToInt32(parameters.GetValueOrDefault("maxAttempts", 3));
|
|
var applyFixes = Convert.ToBoolean(parameters.GetValueOrDefault("applyFixes", false));
|
|
|
|
var result = await AnalyzeWarningsAsync(solutionPath ?? string.Empty, phase ?? "initial", maxAttempts, applyFixes);
|
|
return new AIPluginResult(result, $"Warnings analysis completed for {phase} phase");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AIPluginResult(ex, $"Warnings analysis failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private async Task<object> AnalyzeWarningsAsync(string solutionPath, string phase, int maxAttempts, bool applyFixes)
|
|
{
|
|
var analysisResult = new WarningAnalysisResult
|
|
{
|
|
Phase = phase,
|
|
SolutionPath = solutionPath,
|
|
Timestamp = DateTime.UtcNow,
|
|
};
|
|
|
|
try
|
|
{
|
|
var buildResult = await RunBuildAnalysis(solutionPath);
|
|
|
|
analysisResult.TotalWarnings = buildResult.WarningCount;
|
|
analysisResult.WarningsByType = GroupWarningsByType(buildResult.Warnings);
|
|
analysisResult.ProcessedFiles = GetFilesWithWarnings(buildResult.Warnings);
|
|
analysisResult.FixesApplied = applyFixes ? await AttemptWarningFixes(buildResult.Warnings, maxAttempts) : 0;
|
|
analysisResult.Summary = $"{phase} warnings analysis: {buildResult.WarningCount} warnings found";
|
|
analysisResult.Details = buildResult.Warnings.Take(10).Select(w => new
|
|
{
|
|
w.Code,
|
|
w.Message,
|
|
w.File,
|
|
w.Line,
|
|
Type = w.Type
|
|
}).Cast<object>().ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Warning: Warnings analysis failed: {ex.Message}");
|
|
}
|
|
|
|
return analysisResult;
|
|
}
|
|
|
|
|
|
private async Task<CompilationResult> RunBuildAnalysis(string solutionPath)
|
|
{
|
|
var processInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "dotnet",
|
|
Arguments = $"build \"{solutionPath}\" --verbosity normal --nologo",
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
using var process = Process.Start(processInfo);
|
|
if (process == null)
|
|
{
|
|
throw new InvalidOperationException("Failed to start dotnet build process");
|
|
}
|
|
|
|
var output = await process.StandardOutput.ReadToEndAsync();
|
|
var error = await process.StandardError.ReadToEndAsync();
|
|
await process.WaitForExitAsync();
|
|
|
|
var result = new CompilationResult
|
|
{
|
|
ExitCode = process.ExitCode,
|
|
BuildOutput = output,
|
|
BuildErrors = error,
|
|
Status = process.ExitCode == 0 ? CompilationStatus.Success : CompilationStatus.Failed
|
|
};
|
|
|
|
// Parse warnings from output
|
|
result.Warnings = ParseWarningsFromOutput(output + error);
|
|
result.WarningCount = result.Warnings.Count;
|
|
|
|
return result;
|
|
}
|
|
|
|
private List<CompilationDiagnostic> ParseWarningsFromOutput(string output)
|
|
{
|
|
var warnings = new List<CompilationDiagnostic>();
|
|
var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
if (line.Contains("warning", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var warning = TryParseWarningLine(line.Trim());
|
|
if (warning != null)
|
|
{
|
|
warnings.Add(warning);
|
|
}
|
|
}
|
|
}
|
|
|
|
return warnings;
|
|
}
|
|
|
|
private CompilationDiagnostic? TryParseWarningLine(string line)
|
|
{
|
|
var patterns = new[]
|
|
{
|
|
@"(.+?)\((\d+),(\d+)\):\s*warning\s+([A-Z]+\d+):\s*(.+)",
|
|
@"warning\s+([A-Z]+\d+):\s*(.+)\s*\[(.+?)\]"
|
|
};
|
|
|
|
foreach (var pattern in patterns)
|
|
{
|
|
var match = System.Text.RegularExpressions.Regex.Match(line, pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
|
if (match.Success)
|
|
{
|
|
if (match.Groups.Count >= 5 && !string.IsNullOrEmpty(match.Groups[1].Value))
|
|
{
|
|
return new CompilationDiagnostic
|
|
{
|
|
File = Path.GetFileName(match.Groups[1].Value),
|
|
Line = int.TryParse(match.Groups[2].Value, out var parsedLine) ? parsedLine : 0,
|
|
Column = int.TryParse(match.Groups[3].Value, out var parsedCol) ? parsedCol : 0,
|
|
Code = match.Groups[4].Value,
|
|
Message = match.Groups[5].Value.Trim(),
|
|
Type = "warning"
|
|
};
|
|
}
|
|
else if (match.Groups.Count >= 3)
|
|
{
|
|
return new CompilationDiagnostic
|
|
{
|
|
Code = match.Groups[1].Value,
|
|
Message = match.Groups[2].Value.Trim(),
|
|
File = match.Groups.Count > 3 ? Path.GetFileName(match.Groups[3].Value) : "Unknown",
|
|
Type = "warning"
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
private Dictionary<string, int> GroupWarningsByType(List<CompilationDiagnostic> warnings)
|
|
{
|
|
return warnings.GroupBy(w => w.Code)
|
|
.ToDictionary(g => g.Key, g => g.Count());
|
|
}
|
|
|
|
private List<string> GetFilesWithWarnings(List<CompilationDiagnostic> warnings)
|
|
{
|
|
return warnings.Select(w => w.File)
|
|
.Where(f => !string.IsNullOrEmpty(f))
|
|
.Distinct()
|
|
.ToList();
|
|
}
|
|
|
|
private async Task<int> AttemptWarningFixes(List<CompilationDiagnostic> warnings, int maxAttempts)
|
|
{
|
|
var fixesApplied = 0;
|
|
|
|
// Group warnings by file for efficient processing
|
|
var warningsByFile = warnings.GroupBy(w => w.File);
|
|
|
|
foreach (var fileGroup in warningsByFile.Take(5)) // Limit to 5 files
|
|
{
|
|
var fileName = fileGroup.Key;
|
|
if (string.IsNullOrEmpty(fileName) || fileName == "Unknown") continue;
|
|
|
|
// Apply simple fixes for common warnings
|
|
foreach (var warning in fileGroup.Take(3)) // Limit warnings per file
|
|
{
|
|
if (await TryApplySimpleFix(warning))
|
|
{
|
|
fixesApplied++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fixesApplied;
|
|
}
|
|
|
|
private async Task<bool> TryApplySimpleFix(CompilationDiagnostic warning)
|
|
{
|
|
// This is a simplified implementation
|
|
// In practice, you'd implement specific fixes for different warning types
|
|
|
|
switch (warning.Code?.ToUpper())
|
|
{
|
|
case "CS0168": // Variable declared but never used
|
|
case "CS0219": // Variable assigned but never used
|
|
// Could remove unused variables
|
|
break;
|
|
|
|
case "CS0162": // Unreachable code
|
|
// Could remove unreachable code
|
|
break;
|
|
|
|
case "CS1998": // Async method lacks 'await'
|
|
// Could remove async modifier if appropriate
|
|
break;
|
|
}
|
|
|
|
// For demo purposes, just return true occasionally
|
|
await Task.Delay(10);
|
|
return new Random().Next(100) < 30; // 30% success rate
|
|
}
|
|
}
|
|
|
|
class WarningAnalysisResult
|
|
{
|
|
public string Phase { get; set; } = string.Empty;
|
|
public string SolutionPath { get; set; } = string.Empty;
|
|
public DateTime Timestamp { get; set; }
|
|
public int TotalWarnings { get; set; }
|
|
public Dictionary<string, int> WarningsByType { get; set; } = new();
|
|
public List<string> ProcessedFiles { get; set; } = new();
|
|
public int FixesApplied { get; set; }
|
|
public int FailedFixes { get; set; }
|
|
public string Summary { get; set; } = string.Empty;
|
|
public List<object> Details { get; set; } = new();
|
|
}
|
|
|
|
} |