Files
db-subclass-to-dto/tools/ExtractMethod/Program.cs
T
mostalive b012d0aaa0 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.
2026-09-12 15:28:14 +01:00

51 lines
1.7 KiB
C#

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;