-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Simplify code in analyzer executor #76943
Conversation
@@ -204,7 +204,7 @@ public void ExecuteInitializeMethod(HostSessionStartAnalysisScope sessionScope, | |||
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate. | |||
ExecuteAndCatchIfThrows( | |||
sessionScope.Analyzer, | |||
data => data.analyzer.Initialize(data.context), | |||
static data => data.analyzer.Initialize(data.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.
made all lambdas explicitly static to ensure we don't accidentally capture in them.
// This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we | ||
// can use the same instance across all actions. | ||
var context = new AnalyzerCompilationStartAnalysisContext(compilationScope, | ||
Compilation, AnalyzerOptions, _compilationAnalysisValueProviderFactory, 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.
lifted contexts out of loops. this avoids allocations for the class-based ones, and removes unnecessary cpu for the struct ones.
{ | ||
data.action(data.context); | ||
data.blockEndActions?.AddAll(data.scope.CodeBlockEndActions); | ||
data.syntaxNodeActions?.AddRange(data.scope.SyntaxNodeActions); |
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 two lines don't need to be in the lambda . we are protecting the call out to data.action as that is user code which may throw. after taht, all we're doing is taking the data we collected and adding to the builders we own. this is all code we own and cannot throw. removing from teh lambda greatly simplifies the lambda (making it like all teh rest we have) and greatly simplifes the data we pass to it.
new AnalysisContextInfo(Compilation, declaredSymbol, declaredNode), | ||
cancellationToken); | ||
|
||
codeBlockEndActions!.AddAll(codeBlockScope.CodeBlockEndActions); | ||
syntaxNodeActions!.AddRange(codeBlockScope.SyntaxNodeActions); |
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.
the ?. was unnecessary. we always have these values when going into these particular codepaths.
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: the !
will go away in the strong-typign PR as we will have a strong tie that if we get here, we must have made these temp collections to store the results in.
@RikkiGibson @jcouv this pulls out a bunch of hte machanical changes from the the other prs. If this codes in first, it will make the others easier to review. |
foreach (var startAction in actions) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); |
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.
cancellation check moved into ExecuteAndCatchIfThrows for consistency and simplicity for all callers.
// can use the same instance across all actions. | ||
var context = new AnalyzerCompilationStartAnalysisContext(compilationScope, | ||
Compilation, AnalyzerOptions, _compilationAnalysisValueProviderFactory, cancellationToken); | ||
var contextInfo = new AnalysisContextInfo(Compilation); |
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.
contexxt info also lifted out if we have loops.
(action: startAction.Action, context), | ||
new AnalysisContextInfo(Compilation), | ||
static data => data.startAction.Action(data.context), | ||
(startAction, 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.
made t he lambda and arguments pattern consistent across all usages in the file.
data => data.action(data.context), | ||
(action, context), | ||
static data => data.suppressor.ReportSuppressions(data.context), | ||
(suppressor, context), | ||
new AnalysisContextInfo(Compilation), |
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 in a loop, so didn't bother making a local.
@@ -201,11 +201,10 @@ public void ExecuteInitializeMethod(HostSessionStartAnalysisScope sessionScope, | |||
{ | |||
var context = new AnalyzerAnalysisContext(sessionScope, severityFilter); | |||
|
|||
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate. |
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 is non-sensical. we don't run things asynchronously, and we have no intent to ever do that. this may reflect an idea early on at a point in time when analyzers were being initially developed.
var context = new SemanticModelAnalysisContext(semanticModel, AnalyzerOptions, diagReporter.AddDiagnosticAction, | ||
isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); | ||
|
||
// Catch Exception from action. |
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.
pointless comment that was used inconsistently. The method is ExecuteAndCatchIfThrows. We don't need to comment that that's what it is doing (especially only on a subset of the calls to it).
}, | ||
(action: operationBlockStartAction.Action, context: operationStartContext, scope: operationBlockScope, blockEndActions: operationBlockEndActions, operationActions: operationActions), | ||
static data => data.operationBlockStartAction.Action(data.operationStartContext), | ||
(operationBlockStartAction, operationStartContext), | ||
new AnalysisContextInfo(Compilation, declaredSymbol), |
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 context info will be lifted out of the loop in another pr (when this all gets strongly typed).
data => data.action(data.context), | ||
(action: codeBlockAction.Action, context: context), | ||
static data => data.codeBlockAction.Action(data.context), | ||
(codeBlockAction, context), | ||
new AnalysisContextInfo(Compilation, declaredSymbol, declaredNode), |
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.
same with this. the moving to strongly typed code will allow us to live this out of the loop.
@@ -964,7 +995,10 @@ public void ExecuteSyntaxNodeActions<TLanguageKindEnum>( | |||
|
|||
var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingTopmostNodeForAnalysis, analyzer, cancellationToken); | |||
|
|||
using var _ = PooledDelegates.GetPooledFunction((d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (self: this, analyzer), out Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic); | |||
using var _ = PooledDelegates.GetPooledFunction( | |||
static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), |
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: i don't like the use of 'ct' as a name here, as it means code can accidentally capture an outer variable called 'cancellationToken'. However, as these are all static lambdas now, we don't have to worry about this. So i kept the name the same.
using var _ = PooledDelegates.GetPooledFunction( | ||
static (d, supportedSuppressions) => supportedSuppressions.Contains(d), | ||
supportedSuppressions, | ||
out Func<SuppressionDescriptor, bool> isSupportedSuppression); |
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.
moved to pooling here to avoid delegate allocation, and for consistency with our other methods.
@@ -292,15 +294,19 @@ public void ExecuteSuppressionAction(DiagnosticSuppressor suppressor, ImmutableA | |||
cancellationToken.ThrowIfCancellationRequested(); | |||
|
|||
var supportedSuppressions = _analyzerManager.GetSupportedSuppressionDescriptors(suppressor, this, cancellationToken); | |||
Func<SuppressionDescriptor, bool> isSupportedSuppression = supportedSuppressions.Contains; | |||
Action<SuppressionAnalysisContext> action = suppressor.ReportSuppressions; |
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 delegate allocation was unnecessary. we can just pass the suppressor to ExecuteAndCatchIfThrows which can then call .ReportSuppressions on htat without needing a delegate indirectino.
@RikkiGibson @jcouv this is ready for review (this was the correct PR to ping. ignore the other one :)). |
@RikkiGibson @jcouv ptal :) |
@RikkiGibson @jcouv ptal. Thanks! |
Reviewing now |
Action<SuppressionAnalysisContext> action = suppressor.ReportSuppressions; | ||
|
||
using var _ = PooledDelegates.GetPooledFunction( | ||
static (d, supportedSuppressions) => supportedSuppressions.Contains(d), |
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.
nit: this would have helped me understand what this code was doing a little faster.
static (d, supportedSuppressions) => supportedSuppressions.Contains(d), | |
static (descriptor, supportedSuppressions) => supportedSuppressions.Contains(descriptor), |
I think the fact that the resulting function signature is only hinted at in the out
argument, which comes last, made the first few of these I read a bit slow to grasp.
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.
If you decide to make a change here, feel free to do so in follow-up PR. Thanks
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.
sure. let me do that in followup. i agree that the single letters are not always great.
|
||
foreach (var symbolAction in symbolActions) | ||
{ | ||
var action = symbolAction.Action; | ||
var kinds = symbolAction.Kinds; | ||
cancellationToken.ThrowIfCancellationRequested(); |
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.
It looks like this call was removed from some loops, for example for CompilationStart actions, but left in others. Was there a particular reason for that?
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 removed when the loop was simple and called directly into a method taht checked the CT> i kept when the loop was non-trivial and might not call something that checked.
@@ -890,13 +923,14 @@ private void ExecuteBlockActions<TBlockAction>( | |||
var codeBlockAction = blockAction as CodeBlockAnalyzerAction; | |||
if (codeBlockAction != null) | |||
{ | |||
// This context is a struct, so it's fine to create a new one for each action. |
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.
It looks like we're not lifting this out because we don't know which one we will need on each iteration? Is that right?
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.
correct. but in a followup this will be lifted out as these iterations will be separated in a strongly typed fashion for the node vs iop cases.
new AnalysisContextInfo(Compilation, declaredSymbol), | ||
cancellationToken); | ||
|
||
operationBlockEndActions!.AddAll(operationBlockScope.OperationBlockEndActions); |
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.
If this really is always non-null, it would be better to change its initial assignment from try-cast to direct cast.
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 code gets rewritten in a follow-up where there is no cast at all. This pr is just about pulling out mechanical, simple, changes from the more complex pr.
No description provided.