feat: add FeatureClassifier — heuristic-based feature classification
This commit is contained in:
91
tools/NatsNet.PortTracker/Audit/FeatureClassifier.cs
Normal file
91
tools/NatsNet.PortTracker/Audit/FeatureClassifier.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
namespace NatsNet.PortTracker.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Classifies features by inspecting the SourceIndexer for their .NET implementation status.
|
||||
/// Priority: n_a lookup -> method-not-found -> stub detection -> verified.
|
||||
/// </summary>
|
||||
public sealed class FeatureClassifier
|
||||
{
|
||||
public record ClassificationResult(string Status, string Reason);
|
||||
|
||||
public record FeatureRecord(
|
||||
long Id,
|
||||
string DotnetClass,
|
||||
string DotnetMethod,
|
||||
string GoFile,
|
||||
string GoMethod);
|
||||
|
||||
private readonly SourceIndexer _indexer;
|
||||
|
||||
// N/A lookup: (goMethod pattern) -> reason
|
||||
// Checked case-insensitively against go_method
|
||||
private static readonly Dictionary<string, string> NaByGoMethod = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["Noticef"] = ".NET uses Microsoft.Extensions.Logging",
|
||||
["Debugf"] = ".NET uses Microsoft.Extensions.Logging",
|
||||
["Tracef"] = ".NET uses Microsoft.Extensions.Logging",
|
||||
["Warnf"] = ".NET uses Microsoft.Extensions.Logging",
|
||||
["Errorf"] = ".NET uses Microsoft.Extensions.Logging",
|
||||
["Fatalf"] = ".NET uses Microsoft.Extensions.Logging",
|
||||
};
|
||||
|
||||
// N/A lookup: go_file + go_method patterns
|
||||
private static readonly List<(Func<FeatureRecord, bool> Match, string Reason)> NaPatterns =
|
||||
[
|
||||
// Signal handling — .NET uses IHostApplicationLifetime
|
||||
(f => f.GoMethod.Equals("handleSignals", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"),
|
||||
(f => f.GoMethod.Equals("processSignal", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"),
|
||||
];
|
||||
|
||||
public FeatureClassifier(SourceIndexer indexer)
|
||||
{
|
||||
_indexer = indexer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Classify a single feature. Returns status and reason.
|
||||
/// </summary>
|
||||
public ClassificationResult Classify(FeatureRecord feature)
|
||||
{
|
||||
// 1. N/A lookup — check go_method against known patterns
|
||||
if (NaByGoMethod.TryGetValue(feature.GoMethod, out var naReason))
|
||||
return new ClassificationResult("n_a", naReason);
|
||||
|
||||
foreach (var (match, reason) in NaPatterns)
|
||||
{
|
||||
if (match(feature))
|
||||
return new ClassificationResult("n_a", reason);
|
||||
}
|
||||
|
||||
// 2. Handle comma-separated dotnet_class (e.g. "ClosedRingBuffer,ClosedClient")
|
||||
var classNames = feature.DotnetClass.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var methodName = feature.DotnetMethod;
|
||||
|
||||
// Try each class name
|
||||
foreach (var className in classNames)
|
||||
{
|
||||
var methods = _indexer.Lookup(className, methodName);
|
||||
if (methods.Count > 0)
|
||||
{
|
||||
// Found the method — classify based on body analysis
|
||||
// Use the "best" match: prefer non-stub over stub
|
||||
var best = methods.OrderByDescending(m => m.StatementCount).First();
|
||||
|
||||
if (best.IsStub)
|
||||
return new ClassificationResult("stub", $"Body is throw NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}");
|
||||
|
||||
if (best.IsPartial)
|
||||
return new ClassificationResult("stub", $"Partial implementation with NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}");
|
||||
|
||||
return new ClassificationResult("verified", $"Method found with {best.StatementCount} statement(s) at {Path.GetFileName(best.FilePath)}:{best.LineNumber}");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Method not found — check if any class exists
|
||||
var anyClassFound = classNames.Any(c => _indexer.HasClass(c));
|
||||
if (anyClassFound)
|
||||
return new ClassificationResult("deferred", "Class exists but method not found");
|
||||
|
||||
return new ClassificationResult("deferred", "Class not found in .NET source");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user