Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cli/dispatcher/internal/compilecheck/fast_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,9 @@ func TestSmallAssemblyIsNotTried(t *testing.T) {
}
}

// Verifies that an input other than source contents that changed while csc compiled leaves no
// baseline, and says why, so the next body edit compiles in full.
// Verifies that an input other than source contents that changed while csc compiled leaves neither a
// baseline nor a record of the compile to reuse, and the note says why, so the next body edit
// compiles in full.
func TestNonSourceInputChangedDuringCompileLeavesNoBaseline(t *testing.T) {
project := newFastPathProject(t, fastPathMinimumSources)
recorder := newCachedSchedulerRecorder("Alpha", "Beta")
Expand All @@ -205,7 +206,7 @@ func TestNonSourceInputChangedDuringCompileLeavesNoBaseline(t *testing.T) {

project.editBody(t, 0, "edited")
second := newCachedSchedulerRecorder("Alpha", "Beta")
assertFallback(t, project.run(t, second, false), second, "no baseline: the last full compile did not record one")
assertFallback(t, project.run(t, second, false), second, "no baseline: no full compile of this assembly is on record")
}

// Verifies that a baseline whose saved generator output was deleted or changed is not used, since
Expand Down
12 changes: 9 additions & 3 deletions cli/dispatcher/internal/compilecheck/result_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,15 @@ func (c Compiler) reuseOrCompile(
if err != nil || key == "" || result.RawOutput != "" {
return result, err
}
if writeErr := records.RecordCompile(unit.Assembly.AssemblyName, key,
resultstore.CompileResult[Diagnostic]{Succeeded: result.Succeeded, Diagnostics: result.Diagnostics}, unitOutputNames(unit)); writeErr != nil {
return UnitResult{}, writeErr
// Why the key is read again before recording: csc reads the sources while it compiles, so an edit
// made during the compile would otherwise be recorded under the key of the bytes before it, and
// putting the file back would replay what the edited file reported. CompileUnit has already
// removed the previous record, so leaving this one out makes the next run compile again.
if c.keyStillHolds(plan, unit, key) {
if writeErr := records.RecordCompile(unit.Assembly.AssemblyName, key,
resultstore.CompileResult[Diagnostic]{Succeeded: result.Succeeded, Diagnostics: result.Diagnostics}, unitOutputNames(unit)); writeErr != nil {
return UnitResult{}, writeErr
}
}

// Why --full records too: running --full before finishing would otherwise drop the baseline, and
Expand Down
32 changes: 32 additions & 0 deletions cli/dispatcher/internal/compilecheck/scheduler_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,35 @@ func TestCompileUnitsRecordsNothingWhenASuccessfulCompileWroteNoOutput(t *testin
t.Errorf("the next run should have compiled the unit, got %d runs", recorder.runs["Alpha"])
}
}

// Verifies a compile of sources edited while csc ran is not recorded under the key of the bytes
// before the edit, so putting those bytes back compiles them again rather than reusing what the
// edited file reported.
func TestCompileUnitsDoesNotRecordACompileOfSourcesEditedDuringIt(t *testing.T) {
project := newCachedSchedulerProject(t, nil, "Alpha")
project.compileOrFail(t, newCachedSchedulerRecorder("Alpha"))
project.editSource(t, "Alpha", "class Alpha { void X() { } }")
edited := newCachedSchedulerRecorder("Alpha")
var editErr error
edited.during = func(name string) {
if name == "Alpha" {
editErr = os.WriteFile(filepath.Join(project.root, "Assets", "Alpha", "Alpha.cs"),
[]byte("class Alpha { void Y() { } }"), 0o600)
}
}
project.compileOrFail(t, edited)
if editErr != nil {
t.Fatalf("failed to edit the source during the compile: %v", editErr)
}
if edited.runs["Alpha"] != 1 {
t.Fatalf("expected the edited unit to compile, got %d runs", edited.runs["Alpha"])
}
project.editSource(t, "Alpha", "class Alpha { void X() { } }")

recorder := newCachedSchedulerRecorder("Alpha")
project.compileOrFail(t, recorder)

if recorder.runs["Alpha"] != 1 {
t.Errorf("expected the reverted source to compile again, got %d runs", recorder.runs["Alpha"])
}
}
10 changes: 6 additions & 4 deletions docs/compile-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ build - does not call the worker again: it reports what the previous check repor

Otherwise the assembly is compiled in full as before, and `FastPathFallbacks` says why:

- `no baseline: no full compile of this assembly is on record` (none ran yet, or a later run
skipped or blocked the assembly, which removes its result and baseline)
- `no baseline: no full compile of this assembly is on record` (none ran yet, a later run
skipped or blocked the assembly, which removes its result and baseline, or an input changed while
the last full compile ran, so it left no record; `FastPathBaselineNotes` of that run says which)
- `no baseline: the last full compile had errors` (a full compile that fails drops the baseline an
earlier one left)
- `no baseline: the last full compile did not record one` (`FastPathBaselineNotes` of that run
Expand All @@ -176,8 +177,9 @@ Otherwise the assembly is compiled in full as before, and `FastPathFallbacks` sa
not support)

A full compile that succeeds records a new baseline, `--full` runs included, so the run after it can
use the check again. A source or another input edited while csc was compiling leaves no baseline,
and `FastPathBaselineNotes` says why a successful full compile left none. An input edited while the
use the check again. A source or another input edited while csc was compiling leaves no baseline
and no record of that compile to reuse, so the next run compiles the assembly again;
`FastPathBaselineNotes` says why a successful full compile left no baseline. An input edited while the
changed files were being checked leaves no record of that check, so the next run checks them again.

So the first body edit is compiled in full more often than it looks:
Expand Down
Loading