em 01: scaffold ExtractMethod tool + Demo fixture
- tools/ExtractMethod: net10.0 console app (Microsoft.CodeAnalysis.CSharp 5.9.0, pinned latest stable). CLI: <file.cs> <startLine> <endLine>. Parses the file, builds a scratch compilation with refs from TRUSTED_PLATFORM_ASSEMBLIES, and reports how many whole statements the line range covers (clean error otherwise; exit codes: 0 ok, 1 resolution error, 2 usage). - Tooling/CompilationLoader: shared parse + compilation path for CLI and tests (tests exercise the exact loading path the CLI uses). - Tooling/SelectionResolver: snaps a 1-based inclusive line range to whole statements in the enclosing method body block; boundary checks never split a statement; nested/blank-line ranges handled cleanly. - tests/: ExtractMethod/Fixtures/Demo.cs checked-in fixture exercising every bucket of the parent spec (read-only local, written+read-later return, scratch local, param read, field+property access, indexer + method invocation, multi-statement range incl. a for-loop); excluded from project compilation, copied to output as data. - Tests: Demo.cs compiles with no diagnostics; range 68..72 resolves to 2 statements (LocalDeclarationStatement, ForStatement); AnalyzeDataFlow succeeds on the fixture's for-loop node; mid-statement range fails cleanly. 30/30 green.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using ExtractMethod.Tooling;
|
||||
|
||||
// CLIs are boring on purpose: argument parsing and printing live here, all
|
||||
// Roslyn logic lives in Tooling/ so the tests can drive it directly.
|
||||
|
||||
string usage = "usage: ExtractMethod <file.cs> <startLine> <endLine> (1-based, inclusive)";
|
||||
|
||||
if (args.Length != 3)
|
||||
{
|
||||
Console.Error.WriteLine(usage);
|
||||
return 2;
|
||||
}
|
||||
|
||||
string file = Path.GetFullPath(args[0]);
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
Console.Error.WriteLine($"error: file not found: {file}");
|
||||
Console.Error.WriteLine(usage);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[1], out int startLine) || !int.TryParse(args[2], out int endLine))
|
||||
{
|
||||
Console.Error.WriteLine($"error: line numbers must be integers");
|
||||
Console.Error.WriteLine(usage);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 1. parse the file
|
||||
var tree = CompilationLoader.ParseFile(file);
|
||||
|
||||
// 2. build the scratch compilation (refs from TRUSTED_PLATFORM_ASSEMBLIES)
|
||||
var compilation = CompilationLoader.CreateCompilation(tree, Path.GetFileNameWithoutExtension(file));
|
||||
if (compilation.GetDiagnostics().Any(d => d.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error))
|
||||
{
|
||||
Console.Error.WriteLine("warning: the file does not compile cleanly under a plain Roslyn compilation; reporting syntax-level resolution only");
|
||||
}
|
||||
|
||||
// 3. snap the range to whole statements, report cleanly otherwise
|
||||
var report = SelectionResolver.Resolve(tree, startLine, endLine);
|
||||
if (!report.Succeeded)
|
||||
{
|
||||
Console.Error.WriteLine($"error: {report.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));
|
||||
return 0;
|
||||
Reference in New Issue
Block a user