-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Remove CompilationPair abstraction in diagnostic analyzer subsystem #80141
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
Remove CompilationPair abstraction in diagnostic analyzer subsystem #80141
Conversation
namespace A | ||
{ | ||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the correct behavior. The test here is testing " 'usings' should be INSIDE the namespace, and it is an ERROR otherwise". So it was a bug this wasn't moving in in CodeCleanup.
Note: this differs from teh tests taht validate that nothing change when the option suggestions the change, but the severity is set to 'hidden'. in that case we do not make any change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a sense why this changed? (I agree the behavior looks right, but this means we had a bug somewhere...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When i was debugging through, teh wrong options were coming through, and it meant that the default severity was getting pulled in (which is 'hidden'). Because of this, the codecleanup layer doesn't actually want to make a change due to 'hidden' effectively being equivalent to 'the user didn't actually set anything themselves'.
Draft until API is reviewed and approved. |
{ | ||
// No specific options factory. Can use the shared context. | ||
if (_analyzerToCachedOptions is null) | ||
return context; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: this is the common case for those not referencing hte roslyn sdk. So there should be almost no overhead in that common case.
internal SymbolAnalysisContext WithOptions(AnalyzerOptions options) | ||
=> this.Options == options | ||
? this | ||
: new(this.Symbol, this.Compilation, options, _reportDiagnostic, _isSupportedDiagnostic, this.IsGeneratedCode, this.FilterTree, this.FilterSpan, this.CancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: these context types are structs. So this is not actually allocating a new instance on the heap. Just returning a tweaked version with the right options to pass along.
/// from the shared instance provided in <see cref="Options"/>. If <see langword="null"/> then <see cref="Options"/> | ||
/// will be used for all analyzers. | ||
/// </summary> | ||
public Func<DiagnosticAnalyzer, AnalyzerOptions>? AnalyzerSpecificOptionsFactory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: this prop doesn't need to be public (though that matches the reest here). The API review covers if we should expose this (since it was a constructor param) or if we should keep it internally since we hvve no actual public consumers.
bool logAnalyzerExecutionTime, | ||
bool reportSuppressedDiagnostics, | ||
Func<Exception, bool>? analyzerExceptionFilter, | ||
Func<DiagnosticAnalyzer, AnalyzerOptions>? analyzerSpecificOptionsFactory) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new public API that needs API review.
analyzerExceptionFilter: exceptionFilter, | ||
analyzerSpecificOptionsFactory)); | ||
|
||
(AnalyzerOptions sharedOptions, Func<DiagnosticAnalyzer, AnalyzerOptions>? analyzerSpecificOptionsFactory) GetOptions() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is hte meat of the change on the IDE side.
src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs
Outdated
Show resolved
Hide resolved
.../Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs
Outdated
Show resolved
Hide resolved
var map = new Dictionary<DiagnosticAnalyzer, AnalyzerOptions>(ReferenceEqualityComparer.Instance); | ||
|
||
// Deduping map for the distinct AnalyzerConfigOptionsProvider we get back from analyzerSpecificOptionsFactory | ||
var optionsProviderToOptions = new Dictionary<AnalyzerConfigOptionsProvider, AnalyzerOptions>(ReferenceEqualityComparer.Instance); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: this deduping is tested. we make sure that if two analyzers return the same AnalyzerConfigOptionsProvider that they will then get passed the exact same AnalyzerOptions instance in callbacks.
API change was approved. @jasonmalinowski @dotnet/roslyn-compiler for eyes. |
diagnostics.Verify(Diagnostic("ID0001", "B").WithLocation(1, 8)); | ||
} | ||
|
||
private sealed class OptionsOverrideDiagnosticAnalyzer(AnalyzerConfigOptionsProvider customOptions) : DiagnosticAnalyzer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these tests validate that we see the different options through all the callbacks.
Microsoft.CodeAnalysis.CommandLineResource.LinkedResourceFileName.get -> string? | ||
Microsoft.CodeAnalysis.CommandLineResource.ResourceName.get -> string! | ||
Microsoft.CodeAnalysis.Compilation.EmitDifference(Microsoft.CodeAnalysis.Emit.EmitBaseline! baseline, System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.Emit.SemanticEdit>! edits, System.Func<Microsoft.CodeAnalysis.ISymbol!, bool>! isAddedSymbol, System.IO.Stream! metadataStream, System.IO.Stream! ilStream, System.IO.Stream! pdbStream, Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitDifferenceResult! | ||
Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.CompilationWithAnalyzersOptions(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions? options, System.Action<System.Exception!, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer!, Microsoft.CodeAnalysis.Diagnostic!>? onAnalyzerException, bool concurrentAnalysis, bool logAnalyzerExecutionTime, bool reportSuppressedDiagnostics, System.Func<System.Exception!, bool>? analyzerExceptionFilter, System.Func<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer!, Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider!>? getAnalyzerConfigOptionsProvider) -> void |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only public change is this one constructor.
src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt
Outdated
Show resolved
Hide resolved
@jasonmalinowski this is ready for review. I'm also going to kick off another insertion run to ensure everything is as expected. |
.../Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you'll be renaming the file as a follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. I'm even working on making the "make filename match type" fixer able to fix-all. So i can do these in bulk.
.../Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs
Outdated
Show resolved
Hide resolved
.../Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs
Outdated
Show resolved
Hide resolved
.../Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs
Show resolved
Hide resolved
src/Features/Core/Portable/Diagnostics/Service/DocumentAnalysisExecutor.cs
Outdated
Show resolved
Hide resolved
CancellationToken cancellationToken) | ||
{ | ||
var hostAnalyzers = documentAnalysisScope?.HostAnalyzers ?? compilationWithAnalyzers.HostAnalyzers; | ||
var hostAnalyzers = documentAnalysisScope?.Analyzers ?? compilationWithAnalyzers.Analyzers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if local needs a rename, because it's suspicious it was 'hostAnalyzers' before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do. i agree it's suspicious. can you think of a reason we'd only be using hostanalyzers for suppression analyzers? @mavasani do you perhaps knows?
i'm tempted to leave this as is (with the rename). but if there's a sound reason for only using host analyzers, we could always filter these down to those.
Unfortunately, we don't have any tests indicating if this is intentional or errant.
namespace A | ||
{ | ||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a sense why this changed? (I agree the behavior looks right, but this means we had a bug somewhere...)
…zerService_CompilationWithAnalyzersPair.cs Co-authored-by: Jason Malinowski <[email protected]>
…yrusNajmabadi/roslyn into noCompilationPairWithApiChange
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment
src/Features/Core/Portable/Diagnostics/Service/DocumentAnalysisExecutor.cs
Outdated
Show resolved
Hide resolved
RoslynDebug.Assert(AnalysisScope.TextDocument is Document); | ||
Contract.ThrowIfFalse(analyzer.IsCompilerAnalyzer()); | ||
Contract.ThrowIfNull(_compilationWithAnalyzers); | ||
Contract.ThrowIfFalse(_compilationBasedAnalyzersInAnalysisScope.Contains(analyzer)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we even already had this assert here. so it massively simplifies the code below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I sign off; obviously that question is still pending but I'm not sure I can figure that one out...
filteredHostAnalyzers = filteredHostAnalyzers.AddRange(filteredProjectSuppressors); | ||
// Ensure we filter out DocumentDiagnosticAnalyzers (they're used to get diagnostics, without involving a | ||
// compilation), and also ensure the list has no duplicates. | ||
analyzers = analyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()).Distinct(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Followup to #80200
API change request is here: #80143
Build: https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=12380432&view=results
PR: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/670048