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
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,39 @@
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CodeAnalysis
namespace Microsoft.CodeAnalysis;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

view with whitespace off.


internal partial class SolutionCompilationState
{
internal partial class SolutionCompilationState
/// <summary>
/// Represents a change that needs to be made to a <see cref="Compilation"/>, <see cref="GeneratorDriver"/>, or both in response to
/// some user edit.
/// </summary>
private abstract partial class CompilationAndGeneratorDriverTranslationAction
{
public abstract Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i far preferred subclasses having to be intentional about what they should do here. i already did something similar for CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput previously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now, the only non-abstract method is TryMergeWithPrior. And that one is fine to stay virtual as it's only used as a perf optimization, not a correctness one.


/// <summary>
/// Represents a change that needs to be made to a <see cref="Compilation"/>, <see cref="GeneratorDriver"/>, or both in response to
/// some user edit.
/// Whether or not <see cref="TransformCompilationAsync" /> can be called on Compilations that may contain
/// generated documents.
/// </summary>
private abstract partial class CompilationAndGeneratorDriverTranslationAction
{
public virtual Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken)
{
return Task.FromResult(oldCompilation);
}

/// <summary>
/// Whether or not <see cref="TransformCompilationAsync" /> can be called on Compilations that may contain
/// generated documents.
/// </summary>
/// <remarks>
/// Most translation actions add or remove a single syntax tree which means we can do the "same" change
/// to a compilation that contains the generated files and one that doesn't; however some translation actions
/// (like <see cref="ReplaceAllSyntaxTreesAction"/>) will unilaterally remove all trees, and would have unexpected
/// side effects. This opts those out of operating on ones with generated documents where there would be side effects.
/// </remarks>
public abstract bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput { get; }
/// <remarks>
/// Most translation actions add or remove a single syntax tree which means we can do the "same" change
/// to a compilation that contains the generated files and one that doesn't; however some translation actions
/// (like <see cref="ReplaceAllSyntaxTreesAction"/>) will unilaterally remove all trees, and would have unexpected
/// side effects. This opts those out of operating on ones with generated documents where there would be side effects.
/// </remarks>
public abstract bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput { get; }

public abstract GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver);
public abstract GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver);

/// <summary>
/// When changes are made to a solution, we make a list of translation actions. If multiple similar changes happen in rapid
/// succession, we may be able to merge them without holding onto intermediate state.
/// </summary>
/// <param name="priorAction">The action prior to this one. May be a different type.</param>
/// <returns>A non-null <see cref="CompilationAndGeneratorDriverTranslationAction" /> if we could create a merged one, null otherwise.</returns>
public virtual CompilationAndGeneratorDriverTranslationAction? TryMergeWithPrior(CompilationAndGeneratorDriverTranslationAction priorAction)
=> null;
}
/// <summary>
/// When changes are made to a solution, we make a list of translation actions. If multiple similar changes happen in rapid
/// succession, we may be able to merge them without holding onto intermediate state.
/// </summary>
/// <param name="priorAction">The action prior to this one. May be a different type.</param>
/// <returns>A non-null <see cref="CompilationAndGeneratorDriverTranslationAction" /> if we could create a merged one, null otherwise.</returns>
public virtual CompilationAndGeneratorDriverTranslationAction? TryMergeWithPrior(CompilationAndGeneratorDriverTranslationAction priorAction)
=> null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@ internal sealed class TouchAdditionalDocumentAction(AdditionalDocumentState oldS
private readonly AdditionalDocumentState _newState = newState;

// Changing an additional document doesn't change the compilation directly, so we can "apply" the
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping
// the compilation with stale trees around, answering true is still important.
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping the
// compilation with stale trees around, answering true is still important.
public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput => true;

public override Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for cases where there was an existing comment explaining things, i didn't comment.

=> Task.FromResult(oldCompilation);

public override CompilationAndGeneratorDriverTranslationAction? TryMergeWithPrior(CompilationAndGeneratorDriverTranslationAction priorAction)
{
if (priorAction is TouchAdditionalDocumentAction priorTouchAction &&
Expand Down Expand Up @@ -198,12 +201,14 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generat

internal sealed class AddOrRemoveAnalyzerReferencesAction(string language, ImmutableArray<AnalyzerReference> referencesToAdd = default, ImmutableArray<AnalyzerReference> referencesToRemove = default) : CompilationAndGeneratorDriverTranslationAction
{

// Changing analyzer references doesn't change the compilation directly, so we can "apply" the
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping
// the compilation with stale trees around, answering true is still important.
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping the
// compilation with stale trees around, answering true is still important.
public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput => true;

public override Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken)
=> Task.FromResult(oldCompilation);

public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver)
{
if (!referencesToRemove.IsDefaultOrEmpty)
Expand All @@ -222,12 +227,14 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generat

internal sealed class AddAdditionalDocumentsAction(ImmutableArray<AdditionalDocumentState> additionalDocuments) : CompilationAndGeneratorDriverTranslationAction
{

// Changing an additional document doesn't change the compilation directly, so we can "apply" the
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping
// the compilation with stale trees around, answering true is still important.
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping the
// compilation with stale trees around, answering true is still important.
public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput => true;

public override Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken)
=> Task.FromResult(oldCompilation);

public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver)
{
return generatorDriver.AddAdditionalTexts(additionalDocuments.SelectAsArray(static documentState => documentState.AdditionalText));
Expand All @@ -236,12 +243,14 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generat

internal sealed class RemoveAdditionalDocumentsAction(ImmutableArray<AdditionalDocumentState> additionalDocuments) : CompilationAndGeneratorDriverTranslationAction
{

// Changing an additional document doesn't change the compilation directly, so we can "apply" the
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping
// the compilation with stale trees around, answering true is still important.
// translation (which is a no-op). Since we use a 'false' here to mean that it's not worth keeping the
// compilation with stale trees around, answering true is still important.
public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput => true;

public override Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken)
=> Task.FromResult(oldCompilation);

public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver)
{
return generatorDriver.RemoveAdditionalTexts(additionalDocuments.SelectAsArray(static documentState => documentState.AdditionalText));
Expand All @@ -252,6 +261,11 @@ internal sealed class ReplaceGeneratorDriverAction(GeneratorDriver oldGeneratorD
{
public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput => true;

// Replacing the generator doesn't change the non-generator compilation. So we can just return the old
// compilation as is.
public override Task<Compilation> TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken)
=> Task.FromResult(oldCompilation);

public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver _)
{
// The GeneratorDriver that we have here is from a prior version of the Project, it may be missing state changes due
Expand Down