SonarSource/sonar-dotnet

Rule registration: "SonarTreeReportingContextBase.ReportIssue" assumes ShouldAnalyzeTree was called before

martin-strecker-sonarsource opened this issue · 0 comments

There are two implementations of "ReportIssue":

SonarTreeReportingContextBase

public void ReportIssue(Diagnostic diagnostic) =>
ReportIssueCore(diagnostic);

SonarCompilationReportingContextBase

public void ReportIssue(GeneratedCodeRecognizer generatedCodeRecognizer, Diagnostic diagnostic)
{
if (ShouldAnalyzeTree(diagnostic.Location.SourceTree, generatedCodeRecognizer))
{
ReportIssueCore(diagnostic);
}
}

These classes are derived from SonarTreeReportingContextBase

  • SonarCodeBlockReportingContext
  • SonarSemanticModelReportingContext
  • SonarSyntaxNodeReportingContext
  • SonarSyntaxTreeReportingContext

If the reporting happens through one of these contexts, ShouldAnalyzeTree is not checked. The reasoning is, that the check was done via "Execute" like in SonarAnalysisContext:

public void RegisterSemanticModelAction(GeneratedCodeRecognizer generatedCodeRecognizer, Action<SonarSemanticModelReportingContext> action) =>
analysisContext.RegisterSemanticModelAction(
c => Execute<SonarSemanticModelReportingContext, SemanticModelAnalysisContext>(new(this, c), action, c.SemanticModel.SyntaxTree, generatedCodeRecognizer));

private void Execute<TSonarContext, TRoslynContext>(TSonarContext context, Action<TSonarContext> action, SyntaxTree sourceTree, GeneratedCodeRecognizer generatedCodeRecognizer = null)
where TSonarContext : SonarAnalysisContextBase<TRoslynContext>
{
// For each action registered on context we need to do some pre-processing before actually calling the rule.
// First, we need to ensure the rule does apply to the current scope (main vs test source).
// Second, we call an external delegate (set by legacy SonarLint for VS) to ensure the rule should be run (usually
// the decision is made on based on whether the project contains the analyzer as NuGet).
if (context.HasMatchingScope(SupportedDiagnostics)
&& context.ShouldAnalyzeTree(sourceTree, generatedCodeRecognizer)
&& LegacyIsRegisteredActionEnabled(SupportedDiagnostics, sourceTree)
&& ShouldAnalyzeRazorFile(sourceTree))
{
action(context);
}
}

But in the other "Start" contexts this check is missing:

public void RegisterNodeAction(Action<SonarSyntaxNodeReportingContext> action, params TSyntaxKind[] symbolKinds) =>
Context.RegisterSyntaxNodeAction(x => action(new(AnalysisContext, x)), symbolKinds);

public void RegisterSymbolAction(Action<SonarSymbolReportingContext> action, params SymbolKind[] symbolKinds) =>
Context.RegisterSymbolAction(x => action(new(AnalysisContext, x)), symbolKinds);
public void RegisterCompilationEndAction(Action<SonarCompilationReportingContext> action) =>
Context.RegisterCompilationEndAction(x => action(new(AnalysisContext, x)));
public void RegisterSemanticModelAction(Action<SonarSemanticModelReportingContext> action) =>
Context.RegisterSemanticModelAction(x => action(new(AnalysisContext, x)));

.. and the new SymbolStartAnalysisContext of #8799

public void RegisterCodeBlockAction(Action<SonarCodeBlockReportingContext> action) =>
Context.RegisterCodeBlockAction(x => action(new(AnalysisContext, x)));
public void RegisterCodeBlockStartAction<TLanguageKindEnum>(Action<SonarCodeBlockStartAnalysisContext<TLanguageKindEnum>> action) where TLanguageKindEnum : struct =>
Context.RegisterCodeBlockStartAction<TLanguageKindEnum>(x => action(new(AnalysisContext, x)));
public void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds) =>
throw new NotImplementedException("SonarOperationAnalysisContext wrapper type not implemented.");
public void RegisterOperationBlockAction(Action<OperationBlockAnalysisContext> action) =>
throw new NotImplementedException("SonarOperationBlockAnalysisContext wrapper type not implemented.");
public void RegisterOperationBlockStartAction(Action<OperationBlockStartAnalysisContext> action) =>
throw new NotImplementedException("SonarOperationBlockStartAnalysisContext wrapper type not implemented.");
public void RegisterSymbolEndAction(Action<SonarSymbolReportingContext> action) =>
Context.RegisterSymbolEndAction(x => action(new(AnalysisContext, x)));
public void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SonarSyntaxNodeReportingContext> action, params TLanguageKindEnum[] syntaxKinds) where TLanguageKindEnum : struct =>
Context.RegisterSyntaxNodeAction(x => action(new(AnalysisContext, x)), syntaxKinds);

as a result, we raise in generated code in these registrations.

See also #8874