Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update cancellation token handling behavior for certain analyzer APIs #69108

Merged
merged 7 commits into from
Jul 26, 2023

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Jul 19, 2023

Fixes #41522.

The old API was busted (and never used) where a cancellation token was provided at creation, and then potentially passed along if you called particular methods. This was highly unsafe as said times had no relation to each other, and you could provide a cancellation token at one point unrelated to the one used to compute diagnostics.

We fixed all our own usages everywhere to do the right thing. Specifically, to not pass a cancellation token to the constructor, and to always pass a correct one to the GetXXXDiagnostics methods. This PR just goes and adds appropriate overloads, obsolete warnings, and editorbrowsable hidings, to ensure that any other consumers do the right thing as well.

This change is entirely binary compatible with existing releases, using our standard patterns for preserving (but hiding) bad apis, and introducing new APIs that will be preferred in source.

@@ -26,7 +27,6 @@ public class CompilationWithAnalyzers
private readonly ImmutableArray<DiagnosticAnalyzer> _analyzers;
private readonly ImmutableArray<DiagnosticSuppressor> _suppressors;
private readonly CompilationWithAnalyzersOptions _analysisOptions;
private readonly CancellationToken _cancellationToken;
Copy link
Member Author

Choose a reason for hiding this comment

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

bad captured CT.

public CancellationToken CancellationToken => _cancellationToken;
[Obsolete("This CancellationToken is always 'default'", error: false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public CancellationToken CancellationToken => default;
Copy link
Member Author

Choose a reason for hiding this comment

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

kept for binary compat. but if anyone is using this, they're warned to stop.

public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions? options, CancellationToken cancellationToken)
: this(compilation, analyzers, options)
{
}
Copy link
Member Author

Choose a reason for hiding this comment

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

kept for binary compat. if anyone is using this, they're warning to stop.

@@ -225,15 +228,16 @@ private void VerifyAdditionalFile(AdditionalText file)
/// <summary>
/// Returns diagnostics produced by all <see cref="Analyzers"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsAsync()
Copy link
Member Author

Choose a reason for hiding this comment

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

can't warn on this, as we often have APIs where user provided cancellationtokens are optional, and we want to allow users to call those without a CT for simple programming purposes. We do hide this though, so that the user only sees the method below, which follows our standard api pattern of an actually 'optional' parameter.

/// <inheritdoc cref="WithAnalyzers(Compilation, ImmutableArray{DiagnosticAnalyzer}, AnalyzerOptions?)"/>
[Obsolete("Use WithAnalyzers overload without a cancellation token", error: false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static CompilationWithAnalyzers WithAnalyzers(this Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions? options, CancellationToken cancellationToken)
Copy link
Member Author

@CyrusNajmabadi CyrusNajmabadi Jul 19, 2023

Choose a reason for hiding this comment

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

kept for binary compat. if anyone is using this, they're warned to stop.

Copy link
Member

Choose a reason for hiding this comment

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

💡 Consider adding this to the banned APIs in dotnet/roslyn-analyzers so it applies even to users who reference old versions of the compiler in their analyzer projects

@CyrusNajmabadi
Copy link
Member Author

@dotnet/roslyn-compiler ptal.

/// <summary>
/// Returns a new compilation with attached diagnostic analyzers.
/// </summary>
/// <param name="compilation">Compilation to which analyzers are to be added.</param>
/// <param name="analyzers">The set of analyzers to include in future analyses.</param>
/// <param name="options">Options that are passed to analyzers.</param>
/// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param>
public static CompilationWithAnalyzers WithAnalyzers(this Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions? options = null, CancellationToken cancellationToken = default)
#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
Copy link
Member

Choose a reason for hiding this comment

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

💡 Consider using SuppressMessage for this one since it has a space specifically for the justification

sharwell added a commit to sharwell/roslyn-sdk that referenced this pull request Jul 19, 2023
@sharwell
Copy link
Member

I submitted dotnet/roslyn-sdk#1107 as a supporting change

@CyrusNajmabadi
Copy link
Member Author

@dotnet/roslyn-compiler ptal.

@CyrusNajmabadi
Copy link
Member Author

@CyrusNajmabadi
Copy link
Member Author

@333fred @RikkiGibson can you ptal?

using System.Threading;

namespace Microsoft.CodeAnalysis.Diagnostics
{
public static class DiagnosticAnalyzerExtensions
{
/// <inheritdoc cref="WithAnalyzers(Compilation, ImmutableArray{DiagnosticAnalyzer}, AnalyzerOptions?)"/>
[Obsolete("Use WithAnalyzers overload without a cancellation token", error: false)]
Copy link
Member

Choose a reason for hiding this comment

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

We're probably okay since this is just a warning, but let's confirm that we don't need to document this as a break

Copy link
Member Author

Choose a reason for hiding this comment

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

Tagging @333fred, but i'm virtually certain we don't need to document. There is no actual 'break' here. Both binary and source bulds will continue to be fine. Just that source builds may get a warning. We obsolete things like that all the time without documenting.

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM Thanks (iteration 6)

@jcouv jcouv self-assigned this Jul 26, 2023
@jcouv jcouv added this to the 17.8 milestone Jul 26, 2023
@jcouv jcouv removed the untriaged Issues and PRs which have not yet been triaged by a lead label Jul 26, 2023
@CyrusNajmabadi CyrusNajmabadi enabled auto-merge (squash) July 26, 2023 16:42
@CyrusNajmabadi CyrusNajmabadi merged commit 0dd35b3 into dotnet:main Jul 26, 2023
25 checks passed
@ghost ghost modified the milestones: 17.8, Next Jul 26, 2023
@CyrusNajmabadi CyrusNajmabadi deleted the analyzerCancellation branch July 26, 2023 19:15
@dibarbet dibarbet modified the milestones: Next, 17.8 P2 Aug 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cancellation token can no longer be passed when using CompilationWithAnalyzersOptions
6 participants