em 03: extract-first scan + full suggestion report
- ExtractFirstScanner: reads-only scan of non-local/param read expressions (member/field/property access, element access, invocation, cast), deduped by symbol+text with occurrence counts; invocations flagged (hoisting changes eval count); own-class field/property reads marked optional. Assignment LHS skipped whole (decision 5) — surfaced as a report note. - SignatureBuilder: promotes bucket overlaps into one coherent signature (declared-inside vars drop from params; declared-inside return candidates become the return; outside candidates already covered by their ref write-back stay ref params). Trailing composite returns stay void with a note suggesting an extract-variable first. - ExtractionReporter + ReportFormatter: single composition shared by CLI and tests; report = header, params (ref), returns, locals, extract-first (count + flags), notes, suggested-signature line last. - DataFlowClassifier: Returns now carry types (ReturnSuggestion) — the signature line needs them. - Displays: compact symbol/type formatting for the report. - Fixture: RepeatReads (dedupe ×2 + skipped field write), Casts (cast candidate) appended at the end so all pinned line numbers stay put.
This commit is contained in:
@@ -38,25 +38,22 @@ if (compilation.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error
|
||||
}
|
||||
|
||||
// 3. snap the range to whole statements, report cleanly otherwise
|
||||
var report = SelectionResolver.Resolve(tree, startLine, endLine);
|
||||
if (!report.Succeeded)
|
||||
var resolved = SelectionResolver.Resolve(tree, startLine, endLine);
|
||||
if (!resolved.Succeeded)
|
||||
{
|
||||
Console.Error.WriteLine($"error: {report.Error}");
|
||||
Console.Error.WriteLine($"error: {resolved.Error}");
|
||||
return SelectionResolver.ExitError;
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
$"{report.Count} statement(s) selected, lines {report.StartLine}..{report.EndLine} " +
|
||||
$"in {report.Method?.Identifier.ValueText}(): " +
|
||||
string.Join(", ", report.Kinds));
|
||||
|
||||
// 4. classify the data flow into the raw buckets (em 02: no pretty report yet)
|
||||
// A classification failure is a semantic resolution error: same exit code as
|
||||
// the resolver, message on stderr, but never a stack trace.
|
||||
ExtractionSuggestion suggestion;
|
||||
// 4. compose the full suggestion (em 02 buckets + em 03 extract-first and
|
||||
// signature) and print the report. A composition failure is a semantic
|
||||
// resolution error: same exit code as the resolver, message on stderr,
|
||||
// but never a stack trace.
|
||||
var model = compilation.GetSemanticModel(tree);
|
||||
try
|
||||
{
|
||||
suggestion = DataFlowClassifier.Classify(compilation.GetSemanticModel(tree), report);
|
||||
var extraction = ExtractionReporter.Compose(model, resolved);
|
||||
Console.Write(ReportFormatter.Format(extraction));
|
||||
}
|
||||
catch (Exception e) when (e is InvalidOperationException or ArgumentException)
|
||||
{
|
||||
@@ -64,31 +61,4 @@ catch (Exception e) when (e is InvalidOperationException or ArgumentException)
|
||||
return SelectionResolver.ExitError;
|
||||
}
|
||||
|
||||
foreach (var param in suggestion.Params)
|
||||
{
|
||||
Console.WriteLine($"params: {param.Name} ({param.Type}){(param.ByRef ? " [ref]" : " [in]")}");
|
||||
}
|
||||
if (suggestion.Params.Count == 0)
|
||||
{
|
||||
Console.WriteLine("params: (none)");
|
||||
}
|
||||
|
||||
foreach (var name in suggestion.Returns)
|
||||
{
|
||||
Console.WriteLine($"returns: {name}");
|
||||
}
|
||||
if (suggestion.Returns.Count == 0)
|
||||
{
|
||||
Console.WriteLine("returns: (none)");
|
||||
}
|
||||
|
||||
foreach (var name in suggestion.Locals)
|
||||
{
|
||||
Console.WriteLine($"locals: {name}");
|
||||
}
|
||||
if (suggestion.Locals.Count == 0)
|
||||
{
|
||||
Console.WriteLine("locals: (none)");
|
||||
}
|
||||
|
||||
return 0;
|
||||
Reference in New Issue
Block a user