Skip to content

Commit 77510c0

Browse files
authored
Report warning when changing file that is only included in stale projects (#78276)
1 parent 3b36ed6 commit 77510c0

20 files changed

+197
-12
lines changed

src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di
169169
AddGeneralDiagnostic(EditAndContinueErrorCode.DocumentIsOutOfSyncWithDebuggee, nameof(FeaturesResources.DocumentIsOutOfSyncWithDebuggee), DiagnosticSeverity.Warning);
170170
AddGeneralDiagnostic(EditAndContinueErrorCode.UnableToReadSourceFileOrPdb, nameof(FeaturesResources.UnableToReadSourceFileOrPdb));
171171
AddGeneralDiagnostic(EditAndContinueErrorCode.AddingTypeRuntimeCapabilityRequired, nameof(FeaturesResources.ChangesRequiredSynthesizedType));
172+
AddGeneralDiagnostic(EditAndContinueErrorCode.UpdatingDocumentInStaleProject, nameof(FeaturesResources.Changing_source_file_0_in_a_stale_project_has_no_effect_until_the_project_is_rebuit), DiagnosticSeverity.Warning);
172173

173174
s_descriptors = builder.ToImmutable();
174175
}

src/Features/Core/Portable/EditAndContinue/EditAndContinueErrorCode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ internal enum EditAndContinueErrorCode
1313
DocumentIsOutOfSyncWithDebuggee = 5,
1414
UnableToReadSourceFileOrPdb = 6,
1515
AddingTypeRuntimeCapabilityRequired = 7,
16+
UpdatingDocumentInStaleProject = 8,
1617
}

src/Features/Core/Portable/EditAndContinue/EditSession.cs

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ internal static async ValueTask<bool> HasChangedOrAddedDocumentsAsync(Project ol
354354
foreach (var documentId in newProject.State.DocumentStates.GetChangedStateIds(oldProject.State.DocumentStates, ignoreUnchangedContent: true))
355355
{
356356
var document = newProject.GetRequiredDocument(documentId);
357-
if (document.State.Attributes.DesignTimeOnly)
357+
if (!document.State.SupportsEditAndContinue())
358358
{
359359
continue;
360360
}
@@ -375,7 +375,7 @@ internal static async ValueTask<bool> HasChangedOrAddedDocumentsAsync(Project ol
375375
foreach (var documentId in newProject.State.DocumentStates.GetAddedStateIds(oldProject.State.DocumentStates))
376376
{
377377
var document = newProject.GetRequiredDocument(documentId);
378-
if (document.State.Attributes.DesignTimeOnly)
378+
if (!document.State.SupportsEditAndContinue())
379379
{
380380
continue;
381381
}
@@ -827,6 +827,30 @@ public async ValueTask<SolutionUpdate> EmitSolutionUpdateAsync(Solution solution
827827
using var _5 = ArrayBuilder<Document>.GetInstance(out var changedOrAddedDocuments);
828828
using var _6 = ArrayBuilder<(DocumentId, ImmutableArray<RudeEditDiagnostic>)>.GetInstance(out var documentsWithRudeEdits);
829829
using var _7 = ArrayBuilder<ProjectId>.GetInstance(out var projectsToStale);
830+
831+
// After all projects have been analyzed "true" value indicates changed document that is only included in stale projects.
832+
var changedDocumentsStaleness = new Dictionary<string, bool>(SolutionState.FilePathComparer);
833+
834+
void UpdateChangedDocumentsStaleness(bool isStale)
835+
{
836+
foreach (var changedDocument in changedOrAddedDocuments)
837+
{
838+
var path = changedDocument.FilePath;
839+
840+
// Only documents that support EnC (have paths) are added to the list.
841+
Contract.ThrowIfNull(path);
842+
843+
if (isStale)
844+
{
845+
_ = changedDocumentsStaleness.TryAdd(path, true);
846+
}
847+
else
848+
{
849+
changedDocumentsStaleness[path] = false;
850+
}
851+
}
852+
}
853+
830854
Diagnostic? syntaxError = null;
831855

832856
var oldSolution = DebuggingSession.LastCommittedSolution;
@@ -860,13 +884,6 @@ public async ValueTask<SolutionUpdate> EmitSolutionUpdateAsync(Solution solution
860884
continue;
861885
}
862886

863-
// We don't track document changes in stale projects until they are rebuilt (removed from stale set).
864-
if (oldSolution.IsStaleProject(newProject.Id))
865-
{
866-
Log.Write($"EnC state of {newProject.Name} '{newProject.FilePath}' queried: project is stale");
867-
continue;
868-
}
869-
870887
await PopulateChangedAndAddedDocumentsAsync(Log, oldProject, newProject, changedOrAddedDocuments, diagnostics, cancellationToken).ConfigureAwait(false);
871888
if (changedOrAddedDocuments.IsEmpty)
872889
{
@@ -875,6 +892,16 @@ public async ValueTask<SolutionUpdate> EmitSolutionUpdateAsync(Solution solution
875892

876893
Log.Write($"Found {changedOrAddedDocuments.Count} potentially changed document(s) in project {newProject.Name} '{newProject.FilePath}'");
877894

895+
var isStaleProject = oldSolution.IsStaleProject(newProject.Id);
896+
897+
// We don't consider document changes in stale projects until they are rebuilt (removed from stale set).
898+
if (isStaleProject)
899+
{
900+
Log.Write($"EnC state of {newProject.Name} '{newProject.FilePath}' queried: project is stale");
901+
UpdateChangedDocumentsStaleness(isStale: true);
902+
continue;
903+
}
904+
878905
var (mvid, mvidReadError) = await DebuggingSession.GetProjectModuleIdAsync(newProject, cancellationToken).ConfigureAwait(false);
879906
if (mvidReadError != null)
880907
{
@@ -891,6 +918,7 @@ public async ValueTask<SolutionUpdate> EmitSolutionUpdateAsync(Solution solution
891918
if (mvid == Guid.Empty)
892919
{
893920
Log.Write($"Changes not applied to {newProject.Name} '{newProject.FilePath}': project not built");
921+
UpdateChangedDocumentsStaleness(isStale: true);
894922
continue;
895923
}
896924

@@ -931,10 +959,15 @@ public async ValueTask<SolutionUpdate> EmitSolutionUpdateAsync(Solution solution
931959
// The project is considered stale as long as it has at least one document that is out-of-sync.
932960
// Treat the project the same as if it hasn't been built. We won't produce delta for it until it gets rebuilt.
933961
Log.Write($"Changes not applied to {newProject.Name} '{newProject.FilePath}': binaries not up-to-date");
962+
934963
projectsToStale.Add(newProject.Id);
964+
UpdateChangedDocumentsStaleness(isStale: true);
965+
935966
continue;
936967
}
937968

969+
UpdateChangedDocumentsStaleness(isStale: false);
970+
938971
foreach (var changedDocumentAnalysis in changedDocumentAnalyses)
939972
{
940973
if (changedDocumentAnalysis.SyntaxError != null)
@@ -1160,6 +1193,22 @@ async ValueTask LogDocumentChangesAsync(int? generation, CancellationToken cance
11601193
}
11611194
}
11621195

1196+
// Report stale document updates.
1197+
// We report a warning when a changed/added document is only included in (linked to) stale projects.
1198+
1199+
foreach (var (documentPath, isStale) in changedDocumentsStaleness)
1200+
{
1201+
if (isStale)
1202+
{
1203+
foreach (var documentId in solution.GetDocumentIdsWithFilePath(documentPath))
1204+
{
1205+
var descriptor = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.UpdatingDocumentInStaleProject);
1206+
var diagnostic = Diagnostic.Create(descriptor, Location.Create(documentPath, textSpan: default, lineSpan: default), [documentPath]);
1207+
diagnostics.Add(new ProjectDiagnostics(documentId.ProjectId, [diagnostic]));
1208+
}
1209+
}
1210+
}
1211+
11631212
// log capabilities for edit sessions with changes or reported errors:
11641213
if (blockUpdates || deltas.Count > 0)
11651214
{

src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ internal ImmutableArray<ManagedHotReloadDiagnostic> GetAllDiagnostics()
9898
public required Solution? Solution { get; init; }
9999

100100
public required ModuleUpdates ModuleUpdates { get; init; }
101+
102+
/// <summary>
103+
/// Reported diagnostics, other than rude edits, per project.
104+
/// May contain multiple entries for the same project.
105+
/// </summary>
101106
public required ImmutableArray<ProjectDiagnostics> Diagnostics { get; init; }
107+
102108
public required ImmutableArray<ProjectDiagnostics> RudeEdits { get; init; }
103109
public required Diagnostic? SyntaxError { get; init; }
104110

src/Features/Core/Portable/FeaturesResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,6 +2681,9 @@ Zero-width positive lookbehind assertions are typically used at the beginning of
26812681
<data name="ChangesRequiredSynthesizedType" xml:space="preserve">
26822682
<value>One or more changes result in a new type being created by the compiler, which requires restarting the application because it is not supported by the runtime</value>
26832683
</data>
2684+
<data name="Changing_source_file_0_in_a_stale_project_has_no_effect_until_the_project_is_rebuit" xml:space="preserve">
2685+
<value>Changing source file '{0}' in a stale project has no effect until the project is rebuit.</value>
2686+
</data>
26842687
<data name="Miscellaneous_Files" xml:space="preserve">
26852688
<value>Miscellaneous Files</value>
26862689
</data>

src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Features/Core/Portable/xlf/FeaturesResources.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Features/Core/Portable/xlf/FeaturesResources.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Features/Core/Portable/xlf/FeaturesResources.it.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)