feat: Add the worker that lets compile-check check only changed files
Merged
HA
2d ago+2202+2202+0123456789012345678901234567890123456789-1-1-0123456789
Summary
- Adds the worker that will let
uloop compile-checkcheck only the changed files of a large assembly in a few seconds, instead of recompiling the whole assembly with csc. Nothing calls it yet; the Go side that builds and runs it comes in the next pull requests. - Records the decision, what the fast check guarantees, and what it does not, in ADR 0015.
User Impact
- No change in behavior yet. This lays the groundwork for faster compile-check runs after a method-body edit in a large assembly (a one-line edit in an assembly of about 3,000 files took 17–22 seconds).
Changes
docs/adr/0015-...md: the changed-files check runs only when the last full compile succeeded and the edit keeps every declaration. It reports the changed files' compiler and analyzer diagnostics (withDiagnosticSuppressors applied) and carries the unchanged files' diagnostics over from the last full compile. It names what it cannot check: CS0067/CS0169/CS0414/CS0649, analyzers with Compilation/CompilationEnd/SemanticModel/SymbolStart actions, errors the compiler reports only while emitting (such as CS8178 and CS0656), and source generators.- Emit-only errors are left to
--fullrather than caught by emitting in the worker: emitting would cost a second or two per check, and the output already asks for--fullbefore finishing.
- Emit-only errors are left to
cli/dispatcher/internal/compilecheck/fastcheck/worker/: a C# worker the dispatcher will build on the user's machine with the Editor's dotnet and Roslyn. It uses only API that Roslyn 4.3.1 and .NET 6 already have.fingerprint <in> <out>: for each source, a hash of its declarations (bodies and non-constant initializers replaced by placeholders) and the sha256 of the exact bytes it parsed.check <in> <out>: returnsDeclarationChangedwhen a changed file's fingerprint differs from the baseline; otherwise builds the compilation from the Bee response file (reusing the saved generator output) and reports the changed files' diagnostics, plus the rule IDs it could not run.- Compiler diagnostics reach the suppressors through a relay analyzer on the public API, which works on Roslyn 4.3, 4.10, and 5.6.
- A
#nullabledirective inside a body stays in the fingerprint at the position of that body, so moving it between bodies counts as a declaration change. - Field and property initializers of partial types stay in the fingerprint, because the nullable analysis of a constructor in another file of the type reads them.
- An analyzer that throws while a changed file is checked makes the check return
Unsupported, so the assembly is compiled in full and csc reports AD0001. - The changed files are compiled from the same bytes their fingerprints were computed from.
tests/CompileCheckWorker.UnitTests/: NUnit tests pinned to net6.0,Microsoft.CodeAnalysis.CSharp4.3.1, and C# 10, so newer API fails the build. Analyzers, suppressors, and a generator used by the tests are built from source at test time.- CI: the unit test step runs the new project, and changes to the worker sources trigger it.
Verification
DOTNET_SYSTEM_NET_DISABLEIPV6=1 dotnet test tests/CompileCheckWorker.UnitTests/CompileCheckWorker.UnitTests.csproj --configuration Release→ Passed 40/40.- Mutation checks, each restored afterwards and each failing the test that pins it: the relay-ID filter, the positional
#nullable,-nowarnin the unchecked list, symbol start / semantic model / compilation actions in the unchecked list,GeneratorsNotRerun, partial-type initializers, the AD0001 fallback, and reusing the fingerprinted bytes. - Built the worker with the csc and Roslyn bundled with Unity 2022.3 (Roslyn 4.3.1) and Unity 6000.7 (Roslyn 5.6), then ran
fingerprintandcheckon a small project:checkreported CS0029 for a body edit on both; a bad command returned exit code 2. The final sources also build with the Roslyn 4.3.1 csc. - Repository CI that triggers only for
mainmay not run on this pull request into the integration branch; the commands above are the local equivalent for this change.