From e03680980de7af37c8dfed578e8cf3f4475df7a7 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 17 Aug 2022 21:13:21 +0530 Subject: [PATCH 1/2] Expose IsGeneratedCode boolean property on diagnostic analyzer contexts Closes #7578 Based on the recent feature requests in allowing more fine grained generated code analysis, we have decided to initially expose a boolean IsGeneratedCode property on each of the analysis contexts. Note that this flag indicates if the callback symbol/node/operation/tree is considered generated code or not based on compiler's internal heuristics for analyzer execution. This flag helps the analyzers that only want to analyze certain kinds of generated code to narrow down their custom fine grained generated code analysis to only those callbacks that have this flag set to true. --- .../Diagnostics/DiagnosticAnalyzerTests.cs | 99 +++++++-- .../DiagnosticAnalyzer/AnalyzerDriver.cs | 38 ++-- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 81 +++++--- ...nalyzerManager.AnalyzerExecutionContext.cs | 3 +- .../DiagnosticAnalyzer/AnalyzerManager.cs | 16 +- .../DiagnosticAnalysisContext.cs | 196 ++++++++++++++---- .../DiagnosticStartAnalysisScope.cs | 10 +- .../Core/Portable/PublicAPI.Unshipped.txt | 10 + .../Diagnostics/CommonDiagnosticAnalyzers.cs | 121 ++++++++++- 9 files changed, 448 insertions(+), 126 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs index 6e63d087598e1..7e2be79d81af6 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs @@ -1447,13 +1447,17 @@ public void TestGeneratedCodeAnalyzer() [System.CodeDom.Compiler.GeneratedCodeAttribute(""tool"", ""version"")] class GeneratedCode{0} {{ - private class Nested{0} {{ }} + private class Nested{0} {{ void NestedMethod() {{ System.Console.WriteLine(0); }} }} + + void GeneratedCodeMethod() {{ System.Console.WriteLine(0); }} }} class NonGeneratedCode{0} {{ [System.CodeDom.Compiler.GeneratedCodeAttribute(""tool"", ""version"")] - private class NestedGeneratedCode{0} {{ }} + private class NestedGeneratedCode{0} {{ void NestedGeneratedCodeMethod() {{ System.Console.WriteLine(0); }} }} + + void NonGeneratedCodeMethod() {{ System.Console.WriteLine(0); }} }} "; var generatedFileNames = new List @@ -1583,7 +1587,7 @@ partial class PartialType AddExpectedLocalDiagnostics(builder, false, squiggledText, line, column, GeneratedCodeAnalysisFlags.ReportDiagnostics, diagnosticArgument); // Expected compilation diagnostics - AddExpectedNonLocalDiagnostic(builder, "PartialType", compilation.SyntaxTrees[0].FilePath); + AddExpectedNonLocalDiagnostic(builder, GeneratedCodeAnalyzer.Summary, "PartialType(IsGeneratedCode:False)", $"{compilation.SyntaxTrees[0].FilePath}(IsGeneratedCode:False)"); var expected = builder.ToArrayAndFree(); @@ -1677,21 +1681,32 @@ public void FunkyMethod() private static void VerifyGeneratedCodeAnalyzerDiagnostics(Compilation compilation, AnalyzerOptions analyzerOptions, Func isGeneratedFileName, GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt) { var expected = GetExpectedGeneratedCodeAnalyzerDiagnostics(compilation, isGeneratedFileName, generatedCodeAnalysisFlagsOpt); - VerifyGeneratedCodeAnalyzerDiagnostics(compilation, expected, generatedCodeAnalysisFlagsOpt, analyzerOptions); + VerifyGeneratedCodeAnalyzerDiagnostics(compilation, expected, generatedCodeAnalysisFlagsOpt, analyzerOptions, testIsGeneratedCodeInCallbacks: true); } - private static void VerifyGeneratedCodeAnalyzerDiagnostics(Compilation compilation, DiagnosticDescription[] expected, GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, AnalyzerOptions analyzerOptions = null) + private static void VerifyGeneratedCodeAnalyzerDiagnostics(Compilation compilation, DiagnosticDescription[] expected, GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, AnalyzerOptions analyzerOptions = null, bool testIsGeneratedCodeInCallbacks = false) { - var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt) }; + var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt, testIsGeneratedCodeInCallbacks) }; compilation.VerifyAnalyzerDiagnostics(analyzers, analyzerOptions, null, expected: expected); } private static DiagnosticDescription[] GetExpectedGeneratedCodeAnalyzerDiagnostics(Compilation compilation, Func isGeneratedFileName, GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt) { - var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt) }; + var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt, testIsGeneratedCodeInCallbacks: true) }; var files = compilation.SyntaxTrees.Select(t => t.FilePath).ToImmutableArray(); var sortedCallbackSymbolNames = new SortedSet(); var sortedCallbackTreePaths = new SortedSet(); + var sortedCallbackSyntaxNodeNames = new SortedSet(); + var sortedCallbackOperationNames = new SortedSet(); + var sortedCallbackSemanticModelPaths = new SortedSet(); + var sortedCallbackSymbolStartNames = new SortedSet(); + var sortedCallbackSymbolEndNames = new SortedSet(); + var sortedCallbackOperationBlockStartNames = new SortedSet(); + var sortedCallbackOperationBlockEndNames = new SortedSet(); + var sortedCallbackOperationBlockNames = new SortedSet(); + var sortedCallbackCodeBlockStartNames = new SortedSet(); + var sortedCallbackCodeBlockEndNames = new SortedSet(); + var sortedCallbackCodeBlockNames = new SortedSet(); var builder = ArrayBuilder.GetInstance(); for (int i = 0; i < compilation.SyntaxTrees.Count(); i++) { @@ -1717,7 +1732,7 @@ private static DiagnosticDescription[] GetExpectedGeneratedCodeAnalyzerDiagnosti // Type "NonGeneratedCode{0}" squiggledText = string.Format("NonGeneratedCode{0}", i); diagnosticArgument = squiggledText; - line = 8; + line = 10; column = 7; isGeneratedCode = isGeneratedFile; AddExpectedLocalDiagnostics(builder, isGeneratedCode, squiggledText, line, column, generatedCodeAnalysisFlagsOpt, diagnosticArgument); @@ -1725,7 +1740,7 @@ private static DiagnosticDescription[] GetExpectedGeneratedCodeAnalyzerDiagnosti // Type "NestedGeneratedCode{0}" squiggledText = string.Format("NestedGeneratedCode{0}", i); diagnosticArgument = squiggledText; - line = 11; + line = 13; column = 19; isGeneratedCode = true; AddExpectedLocalDiagnostics(builder, isGeneratedCode, squiggledText, line, column, generatedCodeAnalysisFlagsOpt, diagnosticArgument); @@ -1733,34 +1748,72 @@ private static DiagnosticDescription[] GetExpectedGeneratedCodeAnalyzerDiagnosti // File diagnostic squiggledText = "}"; // last token in file. diagnosticArgument = file; - line = 12; + line = 16; column = 1; isGeneratedCode = isGeneratedFile; AddExpectedLocalDiagnostics(builder, isGeneratedCode, squiggledText, line, column, generatedCodeAnalysisFlagsOpt, diagnosticArgument); // Compilation end summary diagnostic (verify callbacks into analyzer) // Analyzer always called for generated code, unless generated code analysis is explicitly disabled. + Action> addNames = null; + Action> addPath = null; if (generatedCodeAnalysisFlagsOpt == null || (generatedCodeAnalysisFlagsOpt & GeneratedCodeAnalysisFlags.Analyze) != 0) { - sortedCallbackSymbolNames.Add(string.Format("GeneratedCode{0}", i)); - sortedCallbackSymbolNames.Add(string.Format("Nested{0}", i)); - sortedCallbackSymbolNames.Add(string.Format("NonGeneratedCode{0}", i)); - sortedCallbackSymbolNames.Add(string.Format("NestedGeneratedCode{0}", i)); + addNames = names => + { + names.Add(string.Format("GeneratedCode{0}(IsGeneratedCode:True)", i)); + names.Add(string.Format("Nested{0}(IsGeneratedCode:True)", i)); + names.Add(string.Format("NonGeneratedCode{0}(IsGeneratedCode:{1})", i, isGeneratedFile)); + names.Add(string.Format("NestedGeneratedCode{0}(IsGeneratedCode:True)", i)); + }; - sortedCallbackTreePaths.Add(file); + addPath = paths => paths.Add($"{file}(IsGeneratedCode:{isGeneratedFile})"); } else if (!isGeneratedFile) { // Analyzer always called for non-generated code. - sortedCallbackSymbolNames.Add(string.Format("NonGeneratedCode{0}", i)); - sortedCallbackTreePaths.Add(file); + addNames = names => names.Add(string.Format("NonGeneratedCode{0}(IsGeneratedCode:False)", i)); + + addPath = paths => paths.Add($"{file}(IsGeneratedCode:False)"); + } + + if (addNames != null) + { + addNames(sortedCallbackSymbolNames); + addNames(sortedCallbackSyntaxNodeNames); + addNames(sortedCallbackSymbolStartNames); + addNames(sortedCallbackSymbolEndNames); + addNames(sortedCallbackOperationNames); + addNames(sortedCallbackOperationBlockStartNames); + addNames(sortedCallbackOperationBlockEndNames); + addNames(sortedCallbackOperationBlockNames); + addNames(sortedCallbackCodeBlockStartNames); + addNames(sortedCallbackCodeBlockEndNames); + addNames(sortedCallbackCodeBlockNames); + } + + if (addPath != null) + { + addPath(sortedCallbackTreePaths); + addPath(sortedCallbackSemanticModelPaths); } } // Compilation end summary diagnostic (verify callbacks into analyzer) var arg1 = sortedCallbackSymbolNames.Join(","); var arg2 = sortedCallbackTreePaths.Join(","); - AddExpectedNonLocalDiagnostic(builder, arguments: new[] { arg1, arg2 }); + var arg3 = sortedCallbackSyntaxNodeNames.Join(",") + ";" + + sortedCallbackOperationNames.Join(",") + ";" + + sortedCallbackSemanticModelPaths.Join(",") + ";" + + sortedCallbackSymbolStartNames.Join(",") + ";" + + sortedCallbackSymbolEndNames.Join(",") + ";" + + sortedCallbackOperationBlockStartNames.Join(",") + ";" + + sortedCallbackOperationBlockEndNames.Join(",") + ";" + + sortedCallbackOperationBlockNames.Join(",") + ";" + + sortedCallbackCodeBlockStartNames.Join(",") + ";" + + sortedCallbackCodeBlockEndNames.Join(",") + ";" + + sortedCallbackCodeBlockNames.Join(","); + AddExpectedNonLocalDiagnostic(builder, GeneratedCodeAnalyzer.Summary2, arguments: new[] { arg1, arg2, arg3 }); if (compilation.Options.GeneralDiagnosticOption == ReportDiagnostic.Error) { @@ -1800,9 +1853,9 @@ private static void AddExpectedLocalDiagnostics( } } - private static void AddExpectedNonLocalDiagnostic(ArrayBuilder builder, params string[] arguments) + private static void AddExpectedNonLocalDiagnostic(ArrayBuilder builder, DiagnosticDescriptor descriptor, params string[] arguments) { - AddExpectedDiagnostic(builder, GeneratedCodeAnalyzer.Summary.Id, squiggledText: null, line: 1, column: 1, arguments: arguments); + AddExpectedDiagnostic(builder, descriptor.Id, squiggledText: null, line: 1, column: 1, arguments: arguments); } private static void AddExpectedDiagnostic(ArrayBuilder builder, string diagnosticId, string squiggledText, int line, int column, params string[] arguments) @@ -2117,7 +2170,7 @@ public class RegularClass Diagnostic("GeneratedCodeAnalyzerError", "}").WithArguments("Source.cs").WithLocation(11, 1), Diagnostic("GeneratedCodeAnalyzerWarning", "RegularClass").WithArguments("RegularClass").WithLocation(9, 14), Diagnostic("GeneratedCodeAnalyzerError", "RegularClass").WithArguments("RegularClass").WithLocation(9, 14), - Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("RegularClass", "Source.cs").WithLocation(1, 1)); + Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("RegularClass(IsGeneratedCode:False)", "Source.cs(IsGeneratedCode:False)").WithLocation(1, 1)); analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.Analyze) }; compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, @@ -2125,7 +2178,7 @@ public class RegularClass Diagnostic("GeneratedCodeAnalyzerError", "}").WithArguments("Source.cs").WithLocation(11, 1), Diagnostic("GeneratedCodeAnalyzerWarning", "RegularClass").WithArguments("RegularClass").WithLocation(9, 14), Diagnostic("GeneratedCodeAnalyzerError", "RegularClass").WithArguments("RegularClass").WithLocation(9, 14), - Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("HiddenClass,RegularClass", "Source.cs").WithLocation(1, 1)); + Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("HiddenClass(IsGeneratedCode:True),RegularClass(IsGeneratedCode:False)", "Source.cs(IsGeneratedCode:False)").WithLocation(1, 1)); analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics) }; compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, @@ -2135,7 +2188,7 @@ public class RegularClass Diagnostic("GeneratedCodeAnalyzerError", "HiddenClass").WithArguments("HiddenClass").WithLocation(4, 14), Diagnostic("GeneratedCodeAnalyzerWarning", "RegularClass").WithArguments("RegularClass").WithLocation(9, 14), Diagnostic("GeneratedCodeAnalyzerError", "RegularClass").WithArguments("RegularClass").WithLocation(9, 14), - Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("HiddenClass,RegularClass", "Source.cs").WithLocation(1, 1)); + Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("HiddenClass(IsGeneratedCode:True),RegularClass(IsGeneratedCode:False)", "Source.cs(IsGeneratedCode:False)").WithLocation(1, 1)); } [Fact, WorkItem(15903, "https://github.com/dotnet/roslyn/issues/15903")] diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index cf6ebf4af17b3..3228a432ccfd0 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -389,10 +389,10 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn _lazyGeneratedCodeAnalysisFlagsMap = await CreateGeneratedCodeAnalysisFlagsMapAsync(UnsuppressedAnalyzers, AnalyzerManager, analyzerExecutor, _severityFilter).ConfigureAwait(false); _lazyTreatAllCodeAsNonGeneratedCode = ComputeShouldTreatAllCodeAsNonGeneratedCode(UnsuppressedAnalyzers, GeneratedCodeAnalysisFlagsMap); _lazyDoNotAnalyzeGeneratedCode = ComputeShouldSkipAnalysisOnGeneratedCode(UnsuppressedAnalyzers, GeneratedCodeAnalysisFlagsMap, TreatAllCodeAsNonGeneratedCode); - _lazyGeneratedCodeFilesMap = TreatAllCodeAsNonGeneratedCode ? null : new ConcurrentDictionary(); - _lazyGeneratedCodeSymbolsForTreeMap = TreatAllCodeAsNonGeneratedCode ? null : new Dictionary>(); - _lazyIsGeneratedCodeSymbolMap = TreatAllCodeAsNonGeneratedCode ? null : new ConcurrentDictionary(); - _lazyTreesWithHiddenRegionsMap = TreatAllCodeAsNonGeneratedCode ? null : new ConcurrentDictionary(); + _lazyGeneratedCodeFilesMap = new ConcurrentDictionary(); + _lazyGeneratedCodeSymbolsForTreeMap = new Dictionary>(); + _lazyIsGeneratedCodeSymbolMap = new ConcurrentDictionary(); + _lazyTreesWithHiddenRegionsMap = new ConcurrentDictionary(); _lazySuppressedAnalyzersForTreeMap = new ConcurrentDictionary>(); _lazyGeneratedCodeAttribute = analyzerExecutor.Compilation?.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute"); @@ -1124,7 +1124,7 @@ internal ImmutableArray ApplyProgrammaticSuppressionsAndFilterDiagno private bool IsInGeneratedCode(Location location, Compilation compilation, CancellationToken cancellationToken) { - if (TreatAllCodeAsNonGeneratedCode || !location.IsInSource) + if (!location.IsInSource) { return false; } @@ -1571,7 +1571,7 @@ async Task processContainerOnMemberCompletedAsync(INamespaceOrTypeSymbol contain { if (containerSymbol != null && AnalyzerExecutor.TryExecuteSymbolEndActionsForContainer(containerSymbol, processedMemberSymbol, - analyzer, s_getTopmostNodeForAnalysis, analysisState, out var processedContainerEvent)) + analyzer, s_getTopmostNodeForAnalysis, analysisState, IsGeneratedCodeSymbol(containerSymbol), out var processedContainerEvent)) { await OnEventProcessedCoreAsync(processedContainerEvent, ImmutableArray.Create(analyzer), analysisState, cancellationToken).ConfigureAwait(false); } @@ -1641,7 +1641,7 @@ await GetPerSymbolAnalyzerActionsAsync(symbol, analysisScope, analysisState, can if (processedState.Kind == EventProcessedStateKind.Processed && hasPerSymbolActions && - !TryExecuteSymbolEndActions(perSymbolActions.AnalyzerActions, symbolEvent, analysisScope, analysisState, out var subsetProcessedAnalyzers)) + !TryExecuteSymbolEndActions(perSymbolActions.AnalyzerActions, symbolEvent, analysisScope, analysisState, isGeneratedCodeSymbol, out var subsetProcessedAnalyzers)) { Debug.Assert(!subsetProcessedAnalyzers.IsDefault); processedState = subsetProcessedAnalyzers.IsEmpty ? EventProcessedState.NotProcessed : EventProcessedState.CreatePartiallyProcessed(subsetProcessedAnalyzers); @@ -1702,6 +1702,7 @@ private bool TryExecuteSymbolEndActions( SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState? analysisState, + bool isGeneratedCodeSymbol, out ImmutableArray subsetProcessedAnalyzers) { Debug.Assert(AnalyzerActions.SymbolStartActionsCount > 0); @@ -1732,7 +1733,7 @@ private bool TryExecuteSymbolEndActions( var symbolEndActionsForAnalyzer = groupedActions.ToImmutableArrayOrEmpty(); if (!symbolEndActionsForAnalyzer.IsEmpty && - !AnalyzerExecutor.TryExecuteSymbolEndActions(symbolEndActionsForAnalyzer, analyzer, symbolEvent, s_getTopmostNodeForAnalysis, analysisState)) + !AnalyzerExecutor.TryExecuteSymbolEndActions(symbolEndActionsForAnalyzer, analyzer, symbolEvent, s_getTopmostNodeForAnalysis, isGeneratedCodeSymbol, analysisState)) { success = false; continue; @@ -2159,15 +2160,18 @@ async ValueTask getInheritedActionsAsync(AnalyzerDriver static async ValueTask getSymbolActionsCoreAsync(AnalyzerDriver driver, ISymbol symbol, DiagnosticAnalyzer analyzer) { - if (!driver.UnsuppressedAnalyzers.Contains(analyzer) || - driver.IsGeneratedCodeSymbol(symbol) && driver.ShouldSkipAnalysisOnGeneratedCode(analyzer)) + if (!driver.UnsuppressedAnalyzers.Contains(analyzer)) { return AnalyzerActions.Empty; } - else + + var isGeneratedCodeSymbol = driver.IsGeneratedCodeSymbol(symbol); + if (isGeneratedCodeSymbol && driver.ShouldSkipAnalysisOnGeneratedCode(analyzer)) { - return await driver.AnalyzerManager.GetPerSymbolAnalyzerActionsAsync(symbol, analyzer, driver.AnalyzerExecutor).ConfigureAwait(false); + return AnalyzerActions.Empty; } + + return await driver.AnalyzerManager.GetPerSymbolAnalyzerActionsAsync(symbol, isGeneratedCodeSymbol, analyzer, driver.AnalyzerExecutor).ConfigureAwait(false); } } @@ -2217,11 +2221,6 @@ private static async Task /// Executes the symbol start actions. /// + /// Symbol whose symbol start actions are to be executed. + /// Analyzer whose symbol start actions are to be executed. /// whose symbol start actions are to be executed. /// Symbol scope to store the analyzer actions. + /// Flag indicating if the symbol being analyzed is generated code. public void ExecuteSymbolStartActions( ISymbol symbol, DiagnosticAnalyzer analyzer, ImmutableArray actions, - HostSymbolStartAnalysisScope symbolScope) + HostSymbolStartAnalysisScope symbolScope, + bool isGeneratedCodeSymbol) { - if (IsAnalyzerSuppressedForSymbol(analyzer, symbol)) + if (isGeneratedCodeSymbol && _shouldSkipAnalysisOnGeneratedCode(analyzer) || + IsAnalyzerSuppressedForSymbol(analyzer, symbol)) { return; } @@ -331,7 +336,7 @@ public void ExecuteSymbolStartActions( _cancellationToken.ThrowIfCancellationRequested(); var context = new AnalyzerSymbolStartAnalysisContext(startAction.Analyzer, symbolScope, - symbol, Compilation, AnalyzerOptions, _cancellationToken); + symbol, Compilation, AnalyzerOptions, isGeneratedCodeSymbol, _cancellationToken); ExecuteAndCatchIfThrows( startAction.Analyzer, @@ -514,7 +519,7 @@ private void ExecuteSymbolActionsCore( _cancellationToken.ThrowIfCancellationRequested(); var context = new SymbolAnalysisContext(symbol, Compilation, AnalyzerOptions, addDiagnostic, - isSupportedDiagnostic, _cancellationToken); + isSupportedDiagnostic, isGeneratedCodeSymbol, _cancellationToken); ExecuteAndCatchIfThrows( symbolAction.Analyzer, @@ -535,6 +540,7 @@ private void ExecuteSymbolActionsCore( /// Completed member symbol. /// Analyzer whose actions are to be executed. /// Delegate to get topmost declaration node for a symbol declaration reference. + /// Flag indicating if the containing symbol being analyzed is generated code. /// An optional object to track analysis state. /// /// True, if successfully executed the actions for the given analysis scope OR all the actions have already been executed for the given analysis scope. @@ -546,6 +552,7 @@ public bool TryExecuteSymbolEndActionsForContainer( DiagnosticAnalyzer analyzer, Func getTopMostNodeForAnalysis, AnalysisState? analysisState, + bool isGeneratedCode, [NotNullWhen(returnValue: true)] out SymbolDeclaredCompilationEvent? containingSymbolDeclaredEvent) { containingSymbolDeclaredEvent = null; @@ -556,7 +563,7 @@ public bool TryExecuteSymbolEndActionsForContainer( ImmutableArray endActions = containerEndActionsAndEvent.symbolEndActions; containingSymbolDeclaredEvent = containerEndActionsAndEvent.symbolDeclaredEvent; - return TryExecuteSymbolEndActionsCore(endActions, analyzer, containingSymbolDeclaredEvent, getTopMostNodeForAnalysis, analysisState); + return TryExecuteSymbolEndActionsCore(endActions, analyzer, containingSymbolDeclaredEvent, getTopMostNodeForAnalysis, isGeneratedCode, analysisState); } /// @@ -566,6 +573,7 @@ public bool TryExecuteSymbolEndActionsForContainer( /// Analyzer whose actions are to be executed. /// Symbol event to be analyzed. /// Delegate to get topmost declaration node for a symbol declaration reference. + /// Flag indicating if the symbol being analyzed is generated code. /// An optional object to track analysis state. /// /// True, if successfully executed the actions for the given analysis scope OR all the actions have already been executed for the given analysis scope. @@ -576,10 +584,11 @@ public bool TryExecuteSymbolEndActions( DiagnosticAnalyzer analyzer, SymbolDeclaredCompilationEvent symbolDeclaredEvent, Func getTopMostNodeForAnalysis, + bool isGeneratedCode, AnalysisState? analysisState) { return _analyzerManager.TryStartExecuteSymbolEndActions(symbolEndActions, analyzer, symbolDeclaredEvent) && - TryExecuteSymbolEndActionsCore(symbolEndActions, analyzer, symbolDeclaredEvent, getTopMostNodeForAnalysis, analysisState); + TryExecuteSymbolEndActionsCore(symbolEndActions, analyzer, symbolDeclaredEvent, getTopMostNodeForAnalysis, isGeneratedCode, analysisState); } private bool TryExecuteSymbolEndActionsCore( @@ -587,6 +596,7 @@ private bool TryExecuteSymbolEndActionsCore( DiagnosticAnalyzer analyzer, SymbolDeclaredCompilationEvent symbolDeclaredEvent, Func getTopMostNodeForAnalysis, + bool isGeneratedCode, AnalysisState? analysisState) { var symbol = symbolDeclaredEvent.Symbol; @@ -597,7 +607,7 @@ private bool TryExecuteSymbolEndActionsCore( { if (TryStartSymbolEndAnalysis(symbol, analyzer, analysisState, out analyzerState)) { - ExecuteSymbolEndActionsCore(symbolEndActions, analyzer, symbolDeclaredEvent, getTopMostNodeForAnalysis, analyzerState); + ExecuteSymbolEndActionsCore(symbolEndActions, analyzer, symbolDeclaredEvent, getTopMostNodeForAnalysis, isGeneratedCode, analyzerState); MarkSymbolEndAnalysisComplete(symbol, analyzer, analysisState); return true; } @@ -627,9 +637,11 @@ private void ExecuteSymbolEndActionsCore( DiagnosticAnalyzer analyzer, SymbolDeclaredCompilationEvent symbolDeclaredEvent, Func getTopMostNodeForAnalysis, + bool isGeneratedCode, AnalyzerStateData? analyzerState) { Debug.Assert(getTopMostNodeForAnalysis != null); + Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); Debug.Assert(!IsAnalyzerSuppressedForSymbol(analyzer, symbolDeclaredEvent.Symbol)); var symbol = symbolDeclaredEvent.Symbol; @@ -646,7 +658,7 @@ private void ExecuteSymbolEndActionsCore( _cancellationToken.ThrowIfCancellationRequested(); var context = new SymbolAnalysisContext(symbol, Compilation, AnalyzerOptions, addDiagnostic, - isSupportedDiagnostic, _cancellationToken); + isSupportedDiagnostic, isGeneratedCode, _cancellationToken); ExecuteAndCatchIfThrows( symbolAction.Analyzer, @@ -728,7 +740,7 @@ private void ExecuteSemanticModelActionsCore( _cancellationToken.ThrowIfCancellationRequested(); var context = new SemanticModelAnalysisContext(semanticModel, AnalyzerOptions, diagReporter.AddDiagnosticAction, - isSupportedDiagnostic, analysisScope.FilterSpanOpt, _cancellationToken); + isSupportedDiagnostic, analysisScope.FilterSpanOpt, isGeneratedCode, _cancellationToken); // Catch Exception from action. ExecuteAndCatchIfThrows( @@ -811,7 +823,7 @@ private void ExecuteSyntaxTreeActionsCore( { _cancellationToken.ThrowIfCancellationRequested(); - var context = new SyntaxTreeAnalysisContext(tree, AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, Compilation, _cancellationToken); + var context = new SyntaxTreeAnalysisContext(tree, AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, Compilation, isGeneratedCode, _cancellationToken); // Catch Exception from action. ExecuteAndCatchIfThrows( @@ -907,16 +919,18 @@ private void ExecuteSyntaxNodeAction( SemanticModel semanticModel, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, SyntaxNodeAnalyzerStateData? analyzerState) where TLanguageKindEnum : struct { Debug.Assert(analyzerState == null || analyzerState.CurrentNode == node); + Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(syntaxNodeAction.Analyzer)); Debug.Assert(!IsAnalyzerSuppressedForTree(syntaxNodeAction.Analyzer, node.SyntaxTree)); if (ShouldExecuteAction(analyzerState, syntaxNodeAction)) { var syntaxNodeContext = new SyntaxNodeAnalysisContext(node, containingSymbol, semanticModel, AnalyzerOptions, addDiagnostic, - isSupportedDiagnostic, _cancellationToken); + isSupportedDiagnostic, isGeneratedCode, _cancellationToken); ExecuteAndCatchIfThrows( syntaxNodeAction.Analyzer, @@ -935,15 +949,17 @@ private void ExecuteOperationAction( SemanticModel semanticModel, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, OperationAnalyzerStateData? analyzerState) { Debug.Assert(analyzerState == null || analyzerState.CurrentOperation == operation); + Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(operationAction.Analyzer)); Debug.Assert(!IsAnalyzerSuppressedForTree(operationAction.Analyzer, semanticModel.SyntaxTree)); if (ShouldExecuteAction(analyzerState, operationAction)) { var operationContext = new OperationAnalysisContext(operation, containingSymbol, semanticModel.Compilation, - AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, _cancellationToken); + AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, isGeneratedCode, _cancellationToken); ExecuteAndCatchIfThrows( operationAction.Analyzer, data => data.action(data.context), @@ -1124,7 +1140,7 @@ private void ExecuteBlockActionsCore; var codeBlockScope = new HostCodeBlockStartAnalysisScope(); var blockStartContext = new AnalyzerCodeBlockStartAnalysisContext(startAction.Analyzer, - codeBlockScope, declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, _cancellationToken); + codeBlockScope, declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, isGeneratedCode, _cancellationToken); // Catch Exception from the start action. ExecuteAndCatchIfThrows( @@ -1145,7 +1161,8 @@ private void ExecuteBlockActionsCore; var operationBlockScope = new HostOperationBlockStartAnalysisScope(); var operationStartContext = new AnalyzerOperationBlockStartAnalysisContext(startAction.Analyzer, - operationBlockScope, operationBlocks, declaredSymbol, semanticModel.Compilation, AnalyzerOptions, GetControlFlowGraph, _cancellationToken); + operationBlockScope, operationBlocks, declaredSymbol, semanticModel.Compilation, AnalyzerOptions, + GetControlFlowGraph, isGeneratedCode, _cancellationToken); // Catch Exception from the start action. ExecuteAndCatchIfThrows( @@ -1185,20 +1202,20 @@ private void ExecuteBlockActionsCore)getNodesToAnalyze(executableBlocks); - ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, analyzerState?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); + ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, isGeneratedCode, analyzerState?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); } else if (operationActions != null) { var operationActionsByKind = GetOperationActionsByKind(operationActions); var operationsToAnalyze = (IEnumerable)getNodesToAnalyze(executableBlocks); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, analyzerState?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); + ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, isGeneratedCode, analyzerState?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); } } executableNodeActions.Free(); - ExecuteBlockActions(blockActions, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, analyzerState); - ExecuteBlockActions(blockEndActions, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, analyzerState); + ExecuteBlockActions(blockActions, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, isGeneratedCode, analyzerState); + ExecuteBlockActions(blockEndActions, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, isGeneratedCode, analyzerState); diagReporter.Free(); } @@ -1212,10 +1229,12 @@ private void ExecuteBlockActions( ImmutableArray operationBlocks, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, AnalysisState.BlockAnalyzerStateData? analyzerState) where TBlockAction : AnalyzerAction where TNodeStateData : AnalyzerStateData, new() { + Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); Debug.Assert(!IsAnalyzerSuppressedForTree(analyzer, declaredNode.SyntaxTree)); foreach (var blockAction in blockActions) @@ -1225,7 +1244,7 @@ private void ExecuteBlockActions( var codeBlockAction = blockAction as CodeBlockAnalyzerAction; if (codeBlockAction != null) { - var context = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken); + var context = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, _cancellationToken); ExecuteAndCatchIfThrows( codeBlockAction.Analyzer, @@ -1239,7 +1258,7 @@ private void ExecuteBlockActions( if (operationBlockAction != null) { var context = new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, - AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, _cancellationToken); + AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, isGeneratedCode, _cancellationToken); ExecuteAndCatchIfThrows( operationBlockAction.Analyzer, @@ -1342,7 +1361,7 @@ private void ExecuteSyntaxNodeActionsCore( var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, filterSpan, analyzer); using var _ = PooledDelegates.GetPooledFunction((d, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d), (self: this, analyzer), out Func isSupportedDiagnostic); - ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, containingSymbol, model, getKind, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, analyzerState); + ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, containingSymbol, model, getKind, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, isGeneratedCode, analyzerState); diagReporter.Free(); } @@ -1355,16 +1374,18 @@ private void ExecuteSyntaxNodeActions( Func getKind, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, SyntaxNodeAnalyzerStateData? analyzerState) where TLanguageKindEnum : struct { Debug.Assert(nodeActionsByKind.Any()); + Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); Debug.Assert(!IsAnalyzerSuppressedForTree(analyzer, model.SyntaxTree)); var partiallyProcessedNode = analyzerState?.CurrentNode; if (partiallyProcessedNode != null) { - ExecuteSyntaxNodeActions(partiallyProcessedNode, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, analyzerState); + ExecuteSyntaxNodeActions(partiallyProcessedNode, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, analyzerState); } foreach (var child in nodesToAnalyze) @@ -1373,7 +1394,7 @@ private void ExecuteSyntaxNodeActions( { SetCurrentNode(analyzerState, child); - ExecuteSyntaxNodeActions(child, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, analyzerState); + ExecuteSyntaxNodeActions(child, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, analyzerState); } } } @@ -1386,6 +1407,7 @@ private void ExecuteSyntaxNodeActions( Func getKind, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, SyntaxNodeAnalyzerStateData? analyzerState) where TLanguageKindEnum : struct { @@ -1393,7 +1415,7 @@ private void ExecuteSyntaxNodeActions( { foreach (var action in actionsForKind) { - ExecuteSyntaxNodeAction(action, node, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerState); + ExecuteSyntaxNodeAction(action, node, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, analyzerState); } } @@ -1480,7 +1502,7 @@ private void ExecuteOperationActionsCore( var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, filterSpan, analyzer); using var _ = PooledDelegates.GetPooledFunction((d, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d), (self: this, analyzer), out Func isSupportedDiagnostic); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, containingSymbol, model, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, analyzerState); + ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, containingSymbol, model, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, isGeneratedCode, analyzerState); diagReporter.Free(); } @@ -1492,16 +1514,18 @@ private void ExecuteOperationActions( SemanticModel model, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, OperationAnalyzerStateData? analyzerState) { Debug.Assert(operationActionsByKind != null); Debug.Assert(operationActionsByKind.Any()); + Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); Debug.Assert(!IsAnalyzerSuppressedForTree(analyzer, model.SyntaxTree)); var partiallyProcessedOperation = analyzerState?.CurrentOperation; if (partiallyProcessedOperation != null) { - ExecuteOperationActions(partiallyProcessedOperation, operationActionsByKind, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerState); + ExecuteOperationActions(partiallyProcessedOperation, operationActionsByKind, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, analyzerState); } foreach (var child in operationsToAnalyze) @@ -1510,7 +1534,7 @@ private void ExecuteOperationActions( { SetCurrentOperation(analyzerState, child); - ExecuteOperationActions(child, operationActionsByKind, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerState); + ExecuteOperationActions(child, operationActionsByKind, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, analyzerState); } } } @@ -1522,13 +1546,14 @@ private void ExecuteOperationActions( SemanticModel model, Action addDiagnostic, Func isSupportedDiagnostic, + bool isGeneratedCode, OperationAnalyzerStateData? analyzerState) { if (operationActionsByKind.TryGetValue(operation.Kind, out var actionsForKind)) { foreach (var action in actionsForKind) { - ExecuteOperationAction(action, operation, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerState); + ExecuteOperationAction(action, operation, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, analyzerState); } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs index 0c6c14e31c0d4..4c3def0582586 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs @@ -128,6 +128,7 @@ public void ClearCompilationScopeTask() public Task GetSymbolAnalysisScopeAsync( ISymbol symbol, + bool isGeneratedCodeSymbol, ImmutableArray symbolStartActions, AnalyzerExecutor analyzerExecutor) { @@ -145,7 +146,7 @@ public Task GetSymbolAnalysisScopeAsync( HostSymbolStartAnalysisScope getSymbolAnalysisScopeCore() { var symbolAnalysisScope = new HostSymbolStartAnalysisScope(); - analyzerExecutor.ExecuteSymbolStartActions(symbol, _analyzer, symbolStartActions, symbolAnalysisScope); + analyzerExecutor.ExecuteSymbolStartActions(symbol, _analyzer, symbolStartActions, symbolAnalysisScope, isGeneratedCodeSymbol); var symbolEndActions = symbolAnalysisScope.GetAnalyzerActions(_analyzer); if (symbolEndActions.SymbolEndActionsCount > 0) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs index ee0853643cf62..2eea05eedadd0 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs @@ -87,23 +87,25 @@ private async ValueTask GetCompilationAnalysi private async Task GetSymbolAnalysisScopeAsync( ISymbol symbol, + bool isGeneratedCodeSymbol, DiagnosticAnalyzer analyzer, ImmutableArray symbolStartActions, AnalyzerExecutor analyzerExecutor) { var analyzerExecutionContext = GetAnalyzerExecutionContext(analyzer); - return await GetSymbolAnalysisScopeCoreAsync(symbol, symbolStartActions, analyzerExecutor, analyzerExecutionContext).ConfigureAwait(false); + return await GetSymbolAnalysisScopeCoreAsync(symbol, isGeneratedCodeSymbol, symbolStartActions, analyzerExecutor, analyzerExecutionContext).ConfigureAwait(false); } private async Task GetSymbolAnalysisScopeCoreAsync( ISymbol symbol, + bool isGeneratedCodeSymbol, ImmutableArray symbolStartActions, AnalyzerExecutor analyzerExecutor, AnalyzerExecutionContext analyzerExecutionContext) { try { - return await analyzerExecutionContext.GetSymbolAnalysisScopeAsync(symbol, symbolStartActions, analyzerExecutor).ConfigureAwait(false); + return await analyzerExecutionContext.GetSymbolAnalysisScopeAsync(symbol, isGeneratedCodeSymbol, symbolStartActions, analyzerExecutor).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -112,7 +114,7 @@ private async Task GetSymbolAnalysisScopeCoreAsync analyzerExecutionContext.ClearSymbolScopeTask(symbol); analyzerExecutor.CancellationToken.ThrowIfCancellationRequested(); - return await GetSymbolAnalysisScopeCoreAsync(symbol, symbolStartActions, analyzerExecutor, analyzerExecutionContext).ConfigureAwait(false); + return await GetSymbolAnalysisScopeCoreAsync(symbol, isGeneratedCodeSymbol, symbolStartActions, analyzerExecutor, analyzerExecutionContext).ConfigureAwait(false); } } @@ -169,7 +171,11 @@ public async ValueTask GetAnalyzerActionsAsync(DiagnosticAnalyz /// These are the actions registered during the various RegisterSymbolStartAction method invocations for the given symbol on different analysis contexts. /// [PerformanceSensitive("https://github.com/dotnet/roslyn/issues/23582", OftenCompletesSynchronously = true)] - public async ValueTask GetPerSymbolAnalyzerActionsAsync(ISymbol symbol, DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor) + public async ValueTask GetPerSymbolAnalyzerActionsAsync( + ISymbol symbol, + bool isGeneratedCodeSymbol, + DiagnosticAnalyzer analyzer, + AnalyzerExecutor analyzerExecutor) { var analyzerActions = await GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false); if (analyzerActions.SymbolStartActionsCount > 0) @@ -177,7 +183,7 @@ public async ValueTask GetPerSymbolAnalyzerActionsAsync(ISymbol var filteredSymbolStartActions = getFilteredActionsByKind(analyzerActions.SymbolStartActions); if (filteredSymbolStartActions.Length > 0) { - var symbolScope = await GetSymbolAnalysisScopeAsync(symbol, analyzer, filteredSymbolStartActions, analyzerExecutor).ConfigureAwait(false); + var symbolScope = await GetSymbolAnalysisScopeAsync(symbol, isGeneratedCodeSymbol, analyzer, filteredSymbolStartActions, analyzerExecutor).ConfigureAwait(false); return symbolScope.GetAnalyzerActions(analyzer); } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs index 888d8058251f0..134485110bee9 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs @@ -653,18 +653,32 @@ public struct SemanticModelAnalysisContext /// internal TextSpan? FilterSpan { get; } + /// + /// Indicates if the underlying is generated code. + /// + public bool IsGeneratedCode { get; } + + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public SemanticModelAnalysisContext(SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) - : this(semanticModel, options, reportDiagnostic, isSupportedDiagnostic, filterSpan: null, cancellationToken) + : this(semanticModel, options, reportDiagnostic, isSupportedDiagnostic, filterSpan: null, isGeneratedCode: false, cancellationToken) { } - internal SemanticModelAnalysisContext(SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, TextSpan? filterSpan, CancellationToken cancellationToken) + internal SemanticModelAnalysisContext( + SemanticModel semanticModel, + AnalyzerOptions options, + Action reportDiagnostic, + Func isSupportedDiagnostic, + TextSpan? filterSpan, + bool isGeneratedCode, + CancellationToken cancellationToken) { _semanticModel = semanticModel; _options = options; _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; FilterSpan = filterSpan; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -717,13 +731,32 @@ public struct SymbolAnalysisContext internal Func IsSupportedDiagnostic => _isSupportedDiagnostic; + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public SymbolAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(symbol, compilation, options, reportDiagnostic, isSupportedDiagnostic, isGeneratedCode: false, cancellationToken) + { + } + + internal SymbolAnalysisContext( + ISymbol symbol, + Compilation compilation, + AnalyzerOptions options, + Action reportDiagnostic, + Func isSupportedDiagnostic, + bool isGeneratedCode, + CancellationToken cancellationToken) { _symbol = symbol; _compilation = compilation; _options = options; _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -762,16 +795,28 @@ public abstract class SymbolStartAnalysisContext /// public AnalyzerOptions Options { get; } + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken { get; } + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public SymbolStartAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, CancellationToken cancellationToken) + : this(symbol, compilation, options, isGeneratedCode: false, cancellationToken) + { + } + + internal SymbolStartAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, bool isGeneratedCode, CancellationToken cancellationToken) { Symbol = symbol; Compilation = compilation; Options = options; + IsGeneratedCode = isGeneratedCode; CancellationToken = cancellationToken; } @@ -899,17 +944,35 @@ public abstract class CodeBlockStartAnalysisContext where TLa /// public AnalyzerOptions Options { get { return _options; } } + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken { get { return _cancellationToken; } } + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 protected CodeBlockStartAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, CancellationToken cancellationToken) + : this(codeBlock, owningSymbol, semanticModel, options, isGeneratedCode: false, cancellationToken) + { + } + + private protected CodeBlockStartAnalysisContext( + SyntaxNode codeBlock, + ISymbol owningSymbol, + SemanticModel semanticModel, + AnalyzerOptions options, + bool isGeneratedCode, + CancellationToken cancellationToken) { _codeBlock = codeBlock; _owningSymbol = owningSymbol; _semanticModel = semanticModel; _options = options; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -976,12 +1039,31 @@ public struct CodeBlockAnalysisContext /// public AnalyzerOptions Options { get { return _options; } } + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken { get { return _cancellationToken; } } + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public CodeBlockAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(codeBlock, owningSymbol, semanticModel, options, reportDiagnostic, isSupportedDiagnostic, isGeneratedCode: false, cancellationToken) + { + } + + internal CodeBlockAnalysisContext( + SyntaxNode codeBlock, + ISymbol owningSymbol, + SemanticModel semanticModel, + AnalyzerOptions options, + Action reportDiagnostic, + Func isSupportedDiagnostic, + bool isGeneratedCode, + CancellationToken cancellationToken) { _codeBlock = codeBlock; _owningSymbol = owningSymbol; @@ -989,6 +1071,7 @@ public CodeBlockAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, Sema _options = options; _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -1051,24 +1134,25 @@ public abstract class OperationBlockStartAnalysisContext /// public AnalyzerOptions Options => _options; + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken => _cancellationToken; + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 protected OperationBlockStartAnalysisContext( ImmutableArray operationBlocks, ISymbol owningSymbol, Compilation compilation, AnalyzerOptions options, CancellationToken cancellationToken) + : this(operationBlocks, owningSymbol, compilation, options, getControlFlowGraph: null, isGeneratedCode: false, cancellationToken) { - _operationBlocks = operationBlocks; - _owningSymbol = owningSymbol; - _compilation = compilation; - _options = options; - _cancellationToken = cancellationToken; - _getControlFlowGraph = null; } internal OperationBlockStartAnalysisContext( @@ -1076,7 +1160,8 @@ internal OperationBlockStartAnalysisContext( ISymbol owningSymbol, Compilation compilation, AnalyzerOptions options, - Func getControlFlowGraph, + Func? getControlFlowGraph, + bool isGeneratedCode, CancellationToken cancellationToken) { _operationBlocks = operationBlocks; @@ -1084,6 +1169,7 @@ internal OperationBlockStartAnalysisContext( _compilation = compilation; _options = options; _getControlFlowGraph = getControlFlowGraph; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -1173,11 +1259,17 @@ public struct OperationBlockAnalysisContext /// public AnalyzerOptions Options => _options; + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken => _cancellationToken; + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public OperationBlockAnalysisContext( ImmutableArray operationBlocks, ISymbol owningSymbol, @@ -1186,15 +1278,8 @@ public OperationBlockAnalysisContext( Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(operationBlocks, owningSymbol, compilation, options, reportDiagnostic, isSupportedDiagnostic, getControlFlowGraph: null, isGeneratedCode: false, cancellationToken) { - _operationBlocks = operationBlocks; - _owningSymbol = owningSymbol; - _compilation = compilation; - _options = options; - _reportDiagnostic = reportDiagnostic; - _isSupportedDiagnostic = isSupportedDiagnostic; - _cancellationToken = cancellationToken; - _getControlFlowGraph = null; } internal OperationBlockAnalysisContext( @@ -1204,7 +1289,8 @@ internal OperationBlockAnalysisContext( AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, - Func getControlFlowGraph, + Func? getControlFlowGraph, + bool isGeneratedCode, CancellationToken cancellationToken) { _operationBlocks = operationBlocks; @@ -1214,6 +1300,7 @@ internal OperationBlockAnalysisContext( _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; _getControlFlowGraph = getControlFlowGraph; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -1273,6 +1360,11 @@ public struct SyntaxTreeAnalysisContext /// public AnalyzerOptions Options => _options; + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// @@ -1280,23 +1372,27 @@ public struct SyntaxTreeAnalysisContext internal Compilation? Compilation => _compilationOpt; + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public SyntaxTreeAnalysisContext(SyntaxTree tree, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(tree, options, reportDiagnostic, isSupportedDiagnostic, compilation: null, isGeneratedCode: false, cancellationToken) { - _tree = tree; - _options = options; - _reportDiagnostic = reportDiagnostic; - _isSupportedDiagnostic = isSupportedDiagnostic; - _compilationOpt = null; - _cancellationToken = cancellationToken; } - internal SyntaxTreeAnalysisContext(SyntaxTree tree, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, Compilation compilation, CancellationToken cancellationToken) + internal SyntaxTreeAnalysisContext( + SyntaxTree tree, + AnalyzerOptions options, + Action reportDiagnostic, + Func isSupportedDiagnostic, + Compilation? compilation, + bool isGeneratedCode, + CancellationToken cancellationToken) { _tree = tree; _options = options; _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; _compilationOpt = compilation; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } @@ -1413,12 +1509,37 @@ public struct SyntaxNodeAnalysisContext /// public AnalyzerOptions Options => _options; + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken => _cancellationToken; + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public SyntaxNodeAnalysisContext(SyntaxNode node, ISymbol? containingSymbol, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(node, containingSymbol, semanticModel, options, reportDiagnostic, isSupportedDiagnostic, isGeneratedCode: false, cancellationToken) + { + } + + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 + public SyntaxNodeAnalysisContext(SyntaxNode node, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(node, null, semanticModel, options, reportDiagnostic, isSupportedDiagnostic, isGeneratedCode: false, cancellationToken) + { + } + + internal SyntaxNodeAnalysisContext( + SyntaxNode node, + ISymbol? containingSymbol, + SemanticModel semanticModel, + AnalyzerOptions options, + Action reportDiagnostic, + Func isSupportedDiagnostic, + bool isGeneratedCode, + CancellationToken cancellationToken) { _node = node; _containingSymbol = containingSymbol; @@ -1426,14 +1547,10 @@ public SyntaxNodeAnalysisContext(SyntaxNode node, ISymbol? containingSymbol, Sem _options = options; _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } - public SyntaxNodeAnalysisContext(SyntaxNode node, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) - : this(node, null, semanticModel, options, reportDiagnostic, isSupportedDiagnostic, cancellationToken) - { - } - /// /// Report a about a . /// @@ -1483,11 +1600,17 @@ public struct OperationAnalysisContext /// public AnalyzerOptions Options => _options; + /// + /// Indicates if the is generated code. + /// + public bool IsGeneratedCode { get; } + /// /// Token to check for requested cancellation of the analysis. /// public CancellationToken CancellationToken => _cancellationToken; + // TODO: Mark obsolete, tracked with https://github.com/dotnet/roslyn/issues/63440 public OperationAnalysisContext( IOperation operation, ISymbol containingSymbol, @@ -1496,15 +1619,8 @@ public OperationAnalysisContext( Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) + : this(operation, containingSymbol, compilation, options, reportDiagnostic, isSupportedDiagnostic, getControlFlowGraph: null, isGeneratedCode: false, cancellationToken) { - _operation = operation; - _containingSymbol = containingSymbol; - _compilation = compilation; - _options = options; - _reportDiagnostic = reportDiagnostic; - _isSupportedDiagnostic = isSupportedDiagnostic; - _cancellationToken = cancellationToken; - _getControlFlowGraph = null; } internal OperationAnalysisContext( @@ -1514,7 +1630,8 @@ internal OperationAnalysisContext( AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, - Func getControlFlowGraph, + Func? getControlFlowGraph, + bool isGeneratedCode, CancellationToken cancellationToken) { _operation = operation; @@ -1524,6 +1641,7 @@ internal OperationAnalysisContext( _reportDiagnostic = reportDiagnostic; _isSupportedDiagnostic = isSupportedDiagnostic; _getControlFlowGraph = getControlFlowGraph; + IsGeneratedCode = isGeneratedCode; _cancellationToken = cancellationToken; } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs index 47f3197be569a..e1d484806c696 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs @@ -231,8 +231,9 @@ internal AnalyzerSymbolStartAnalysisContext(DiagnosticAnalyzer analyzer, ISymbol owningSymbol, Compilation compilation, AnalyzerOptions options, + bool isGeneratedCode, CancellationToken cancellationToken) - : base(owningSymbol, compilation, options, cancellationToken) + : base(owningSymbol, compilation, options, isGeneratedCode, cancellationToken) { _analyzer = analyzer; _scope = scope; @@ -295,8 +296,9 @@ internal AnalyzerCodeBlockStartAnalysisContext(DiagnosticAnalyzer analyzer, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, + bool isGeneratedCode, CancellationToken cancellationToken) - : base(codeBlock, owningSymbol, semanticModel, options, cancellationToken) + : base(codeBlock, owningSymbol, semanticModel, options, isGeneratedCode, cancellationToken) { _analyzer = analyzer; _scope = scope; @@ -330,8 +332,9 @@ internal AnalyzerOperationBlockStartAnalysisContext(DiagnosticAnalyzer analyzer, Compilation compilation, AnalyzerOptions options, Func getControlFlowGraph, + bool isGeneratedCode, CancellationToken cancellationToken) - : base(operationBlocks, owningSymbol, compilation, options, getControlFlowGraph, cancellationToken) + : base(operationBlocks, owningSymbol, compilation, options, getControlFlowGraph, isGeneratedCode, cancellationToken) { _analyzer = analyzer; _scope = scope; @@ -569,6 +572,7 @@ public void RegisterSymbolAction(DiagnosticAnalyzer analyzer, Action int *REMOVED*static Microsoft.CodeAnalysis.ModuleMetadata.CreateFromMetadata(System.IntPtr metadata, int size, System.IDisposable! owner, bool disposeOwner) -> Microsoft.CodeAnalysis.ModuleMetadata! +Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.CodeBlockStartAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.OperationBlockAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.OperationBlockStartAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.SymbolStartAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.IsGeneratedCode.get -> bool +Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.IsGeneratedCode.get -> bool Microsoft.CodeAnalysis.GeneratorDriver.ReplaceGenerators(System.Collections.Immutable.ImmutableArray generators) -> Microsoft.CodeAnalysis.GeneratorDriver! override sealed Microsoft.CodeAnalysis.CompilationOptions.GetHashCode() -> int static Microsoft.CodeAnalysis.ModuleMetadata.CreateFromMetadata(System.IntPtr metadata, int size, System.Action! onDispose) -> Microsoft.CodeAnalysis.ModuleMetadata! diff --git a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs index 37f00075a7655..a64ad3359f0bc 100644 --- a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs +++ b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs @@ -1317,6 +1317,7 @@ public override void Initialize(AnalysisContext context) public class GeneratedCodeAnalyzer : DiagnosticAnalyzer { private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlagsOpt; + private readonly bool _testIsGeneratedCodeInCallbacks; public static readonly DiagnosticDescriptor Warning = new DiagnosticDescriptor( "GeneratedCodeAnalyzerWarning", @@ -1342,12 +1343,21 @@ public class GeneratedCodeAnalyzer : DiagnosticAnalyzer DiagnosticSeverity.Warning, true); - public GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt) + public static readonly DiagnosticDescriptor Summary2 = new DiagnosticDescriptor( + "GeneratedCodeAnalyzerSummary", + "Title2", + "GeneratedCodeAnalyzer received callbacks for: '{0}' types and '{1}' files and '{2}' additional IsGeneratedCode callbacks", + "Category", + DiagnosticSeverity.Warning, + true); + + public GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, bool testIsGeneratedCodeInCallbacks = false) { _generatedCodeAnalysisFlagsOpt = generatedCodeAnalysisFlagsOpt; + _testIsGeneratedCodeInCallbacks = testIsGeneratedCodeInCallbacks; } - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning, Error, Summary); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning, Error, Summary, Summary2); public override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction(this.OnCompilationStart); @@ -1365,23 +1375,124 @@ private void OnCompilationStart(CompilationStartAnalysisContext context) var sortedCallbackTreePaths = new SortedSet(); context.RegisterSymbolAction(symbolContext => { - sortedCallbackSymbolNames.Add(symbolContext.Symbol.Name); + sortedCallbackSymbolNames.Add($"{symbolContext.Symbol.Name}(IsGeneratedCode:{symbolContext.IsGeneratedCode})"); ReportSymbolDiagnostics(symbolContext.Symbol, symbolContext.ReportDiagnostic); }, SymbolKind.NamedType); context.RegisterSyntaxTreeAction(treeContext => { - sortedCallbackTreePaths.Add(treeContext.Tree.FilePath); + sortedCallbackTreePaths.Add($"{treeContext.Tree.FilePath}(IsGeneratedCode:{treeContext.IsGeneratedCode})"); ReportTreeDiagnostics(treeContext.Tree, treeContext.ReportDiagnostic); }); + var sortedCallbackSyntaxNodeNames = new SortedSet(); + var sortedCallbackOperationNames = new SortedSet(); + var sortedCallbackSemanticModelPaths = new SortedSet(); + var sortedCallbackSymbolStartNames = new SortedSet(); + var sortedCallbackSymbolEndNames = new SortedSet(); + var sortedCallbackOperationBlockStartNames = new SortedSet(); + var sortedCallbackOperationBlockEndNames = new SortedSet(); + var sortedCallbackOperationBlockNames = new SortedSet(); + var sortedCallbackCodeBlockStartNames = new SortedSet(); + var sortedCallbackCodeBlockEndNames = new SortedSet(); + var sortedCallbackCodeBlockNames = new SortedSet(); + if (_testIsGeneratedCodeInCallbacks) + { + // Test all remaining analysis contexts that expose "IsGeneratdCode" flag + context.RegisterSyntaxNodeAction(context => + sortedCallbackSyntaxNodeNames.Add($"{context.ContainingSymbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})"), + SyntaxKind.ClassDeclaration); + + context.RegisterSemanticModelAction(context => + sortedCallbackSemanticModelPaths.Add($"{context.SemanticModel.SyntaxTree.FilePath}(IsGeneratedCode:{context.IsGeneratedCode})")); + + context.RegisterSymbolStartAction(context => + { + sortedCallbackSymbolStartNames.Add($"{context.Symbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})"); + + context.RegisterOperationBlockStartAction(context => + { + if (context.OwningSymbol.Kind != SymbolKind.Method || + context.OperationBlocks.IsEmpty) + { + return; + } + + sortedCallbackOperationBlockStartNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"); + + context.RegisterOperationAction(context => + sortedCallbackOperationNames.Add($"{context.ContainingSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"), + OperationKind.Invocation); + + context.RegisterOperationBlockEndAction(context => + sortedCallbackOperationBlockEndNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})")); + }); + + context.RegisterOperationBlockAction(context => + { + if (context.OwningSymbol.Kind != SymbolKind.Method || + context.OperationBlocks.IsEmpty) + { + return; + } + + sortedCallbackOperationBlockNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"); + }); + + context.RegisterSymbolEndAction(context => + sortedCallbackSymbolEndNames.Add($"{context.Symbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})")); + }, SymbolKind.NamedType); + + context.RegisterCodeBlockStartAction(context => + { + if (context.OwningSymbol.Kind != SymbolKind.Method) + { + return; + } + + sortedCallbackCodeBlockStartNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"); + + context.RegisterCodeBlockEndAction(context => + sortedCallbackCodeBlockEndNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})")); + }); + + context.RegisterCodeBlockAction(context => + { + if (context.OwningSymbol.Kind != SymbolKind.Method) + { + return; + } + + sortedCallbackCodeBlockNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"); + }); + } + context.RegisterCompilationEndAction(endContext => { var arg1 = sortedCallbackSymbolNames.Join(","); var arg2 = sortedCallbackTreePaths.Join(","); + var args = new object[] { arg1, arg2 }; + var rule = Summary; + + if (_testIsGeneratedCodeInCallbacks) + { + var arg3 = sortedCallbackSyntaxNodeNames.Join(",") + ";" + + sortedCallbackOperationNames.Join(",") + ";" + + sortedCallbackSemanticModelPaths.Join(",") + ";" + + sortedCallbackSymbolStartNames.Join(",") + ";" + + sortedCallbackSymbolEndNames.Join(",") + ";" + + sortedCallbackOperationBlockStartNames.Join(",") + ";" + + sortedCallbackOperationBlockEndNames.Join(",") + ";" + + sortedCallbackOperationBlockNames.Join(",") + ";" + + sortedCallbackCodeBlockStartNames.Join(",") + ";" + + sortedCallbackCodeBlockEndNames.Join(",") + ";" + + sortedCallbackCodeBlockNames.Join(","); + args = new object[] { arg1, arg2, arg3 }; + rule = Summary2; + } // Summary diagnostics about received callbacks. - var diagnostic = Diagnostic.Create(Summary, Location.None, arg1, arg2); + var diagnostic = Diagnostic.Create(rule, Location.None, args); endContext.ReportDiagnostic(diagnostic); }); } From 9905432059bea85b4ff7550139d4b5bd408b60e0 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 18 Aug 2022 17:55:05 +0530 Subject: [PATCH 2/2] Fix VB tests --- .../Diagnostics/DiagnosticAnalyzerTests.cs | 11 ++ .../Diagnostics/CommonDiagnosticAnalyzers.cs | 14 +- .../Diagnostics/DiagnosticAnalyzerTests.vb | 120 +++++++++++++++--- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs index 7e2be79d81af6..0af8d06bdbc2a 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs @@ -1618,6 +1618,17 @@ class TypeInGeneratedFile { } expected: Diagnostic("GeneratedCodeAnalyzer2Warning", "TypeInUserFile").WithArguments("TypeInUserFile", "2").WithLocation(2, 7)); } + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class GeneratedCodeAnalyzer : AbstractGeneratedCodeAnalyzer + { + public GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlags, bool testIsGeneratedCodeInCallbacks = false) + : base(generatedCodeAnalysisFlags, testIsGeneratedCodeInCallbacks) + { + } + + protected override SyntaxKind ClassDeclarationSyntaxKind => SyntaxKind.ClassDeclaration; + } + internal class OwningSymbolTestAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor ExpressionDescriptor = new DiagnosticDescriptor( diff --git a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs index a64ad3359f0bc..716a5f32c0694 100644 --- a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs +++ b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs @@ -1313,8 +1313,8 @@ public override void Initialize(AnalysisContext context) } } - [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - public class GeneratedCodeAnalyzer : DiagnosticAnalyzer + public abstract class AbstractGeneratedCodeAnalyzer : DiagnosticAnalyzer + where TSyntaxKind : struct { private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlagsOpt; private readonly bool _testIsGeneratedCodeInCallbacks; @@ -1351,12 +1351,14 @@ public class GeneratedCodeAnalyzer : DiagnosticAnalyzer DiagnosticSeverity.Warning, true); - public GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, bool testIsGeneratedCodeInCallbacks = false) + protected AbstractGeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, bool testIsGeneratedCodeInCallbacks) { _generatedCodeAnalysisFlagsOpt = generatedCodeAnalysisFlagsOpt; _testIsGeneratedCodeInCallbacks = testIsGeneratedCodeInCallbacks; } + protected abstract TSyntaxKind ClassDeclarationSyntaxKind { get; } + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning, Error, Summary, Summary2); public override void Initialize(AnalysisContext context) { @@ -1399,9 +1401,9 @@ private void OnCompilationStart(CompilationStartAnalysisContext context) if (_testIsGeneratedCodeInCallbacks) { // Test all remaining analysis contexts that expose "IsGeneratdCode" flag - context.RegisterSyntaxNodeAction(context => + context.RegisterSyntaxNodeAction(context => sortedCallbackSyntaxNodeNames.Add($"{context.ContainingSymbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})"), - SyntaxKind.ClassDeclaration); + ImmutableArray.Create(ClassDeclarationSyntaxKind)); context.RegisterSemanticModelAction(context => sortedCallbackSemanticModelPaths.Add($"{context.SemanticModel.SyntaxTree.FilePath}(IsGeneratedCode:{context.IsGeneratedCode})")); @@ -1443,7 +1445,7 @@ private void OnCompilationStart(CompilationStartAnalysisContext context) sortedCallbackSymbolEndNames.Add($"{context.Symbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})")); }, SymbolKind.NamedType); - context.RegisterCodeBlockStartAction(context => + context.RegisterCodeBlockStartAction(context => { if (context.OwningSymbol.Kind != SymbolKind.Method) { diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb index bf6885e0f1bd2..bb3d03e3ad70f 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb @@ -823,13 +823,27 @@ End Class _ Class GeneratedCode{0} Private Class Nested{0} + Private Sub NestedMethod() + System.Console.WriteLine(0) + End Sub End Class + + Private Sub GeneratedCodeMethod() + System.Console.WriteLine(0) + End Sub End Class Class NonGeneratedCode{0} _ Private Class NestedGeneratedCode{0} + Private Sub NestedGeneratedCodeMethod() + System.Console.WriteLine(0) + End Sub End Class + + Private Sub NonGeneratedCodeMethod() + System.Console.WriteLine(0) + End Sub End Class ]]>.Value @@ -918,7 +932,7 @@ End Class AddExpectedLocalDiagnostics(builder, False, squiggledText, line, column, GeneratedCodeAnalysisFlags.ReportDiagnostics, diagnosticArgument) ' Expected compilation diagnostics - AddExpectedNonLocalDiagnostic(builder, "PartialType", compilation.SyntaxTrees(0).FilePath) + AddExpectedNonLocalDiagnostic(builder, GeneratedCodeAnalyzer.Summary, "PartialType(IsGeneratedCode:False)", $"{compilation.SyntaxTrees(0).FilePath}(IsGeneratedCode:False)") Dim expected = builder.ToArrayAndFree() @@ -1142,19 +1156,30 @@ End Class Private Shared Sub VerifyGeneratedCodeAnalyzerDiagnostics(compilation As Compilation, isGeneratedFileName As Func(Of String, Boolean), generatedCodeAnalysisFlagsOpt As GeneratedCodeAnalysisFlags?) Dim expected = GetExpectedGeneratedCodeAnalyzerDiagnostics(compilation, isGeneratedFileName, generatedCodeAnalysisFlagsOpt) - VerifyGeneratedCodeAnalyzerDiagnostics(compilation, expected, generatedCodeAnalysisFlagsOpt) + VerifyGeneratedCodeAnalyzerDiagnostics(compilation, expected, generatedCodeAnalysisFlagsOpt, testIsGeneratedCodeInCallbacks:=True) End Sub - Private Shared Sub VerifyGeneratedCodeAnalyzerDiagnostics(compilation As Compilation, expected As DiagnosticDescription(), generatedCodeAnalysisFlagsOpt As GeneratedCodeAnalysisFlags?) - Dim analyzers = New DiagnosticAnalyzer() {New GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt)} + Private Shared Sub VerifyGeneratedCodeAnalyzerDiagnostics(compilation As Compilation, expected As DiagnosticDescription(), generatedCodeAnalysisFlagsOpt As GeneratedCodeAnalysisFlags?, Optional testIsGeneratedCodeInCallbacks As Boolean = False) + Dim analyzers = New DiagnosticAnalyzer() {New GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt, testIsGeneratedCodeInCallbacks)} compilation.VerifyAnalyzerDiagnostics(analyzers, Nothing, Nothing, expected) End Sub Private Shared Function GetExpectedGeneratedCodeAnalyzerDiagnostics(compilation As Compilation, isGeneratedFileName As Func(Of String, Boolean), generatedCodeAnalysisFlagsOpt As GeneratedCodeAnalysisFlags?) As DiagnosticDescription() - Dim analyzers = New DiagnosticAnalyzer() {New GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt)} + Dim analyzers = New DiagnosticAnalyzer() {New GeneratedCodeAnalyzer(generatedCodeAnalysisFlagsOpt, testIsGeneratedCodeInCallbacks:=True)} Dim files = compilation.SyntaxTrees.Select(Function(t) t.FilePath).ToImmutableArray() Dim sortedCallbackSymbolNames = New SortedSet(Of String)() Dim sortedCallbackTreePaths = New SortedSet(Of String)() + Dim sortedCallbackSyntaxNodeNames = New SortedSet(Of String)() + Dim sortedCallbackOperationNames = New SortedSet(Of String)() + Dim sortedCallbackSemanticModelPaths = New SortedSet(Of String)() + Dim sortedCallbackSymbolStartNames = New SortedSet(Of String)() + Dim sortedCallbackSymbolEndNames = New SortedSet(Of String)() + Dim sortedCallbackOperationBlockStartNames = New SortedSet(Of String)() + Dim sortedCallbackOperationBlockEndNames = New SortedSet(Of String)() + Dim sortedCallbackOperationBlockNames = New SortedSet(Of String)() + Dim sortedCallbackCodeBlockStartNames = New SortedSet(Of String)() + Dim sortedCallbackCodeBlockEndNames = New SortedSet(Of String)() + Dim sortedCallbackCodeBlockNames = New SortedSet(Of String)() Dim builder = ArrayBuilder(Of DiagnosticDescription).GetInstance() For i As Integer = 0 To compilation.SyntaxTrees.Count() - 1 Dim file = files(i) @@ -1179,7 +1204,7 @@ End Class ' Type "NonGeneratedCode{0}" squiggledText = String.Format("NonGeneratedCode{0}", i) diagnosticArgument = squiggledText - line = 8 + line = 15 column = 7 isGeneratedCode = isGeneratedFile AddExpectedLocalDiagnostics(builder, isGeneratedCode, squiggledText, line, column, generatedCodeAnalysisFlagsOpt, diagnosticArgument) @@ -1187,7 +1212,7 @@ End Class ' Type "NestedGeneratedCode{0}" squiggledText = String.Format("NestedGeneratedCode{0}", i) diagnosticArgument = squiggledText - line = 10 + line = 17 column = 16 isGeneratedCode = True AddExpectedLocalDiagnostics(builder, isGeneratedCode, squiggledText, line, column, generatedCodeAnalysisFlagsOpt, diagnosticArgument) @@ -1195,31 +1220,73 @@ End Class ' File diagnostic squiggledText = "Class" ' last token in file. diagnosticArgument = file - line = 12 + line = 26 column = 5 isGeneratedCode = isGeneratedFile AddExpectedLocalDiagnostics(builder, isGeneratedCode, squiggledText, line, column, generatedCodeAnalysisFlagsOpt, diagnosticArgument) ' Compilation end summary diagnostic (verify callbacks into analyzer) ' Analyzer always called for generated code, unless generated code analysis is explicitly disabled. + Dim addNames As Action(Of SortedSet(Of String)) = Nothing + Dim addPath As Action(Of SortedSet(Of String)) = Nothing + Dim index = i If generatedCodeAnalysisFlagsOpt Is Nothing OrElse (generatedCodeAnalysisFlagsOpt And GeneratedCodeAnalysisFlags.Analyze) <> 0 Then - sortedCallbackSymbolNames.Add(String.Format("GeneratedCode{0}", i)) - sortedCallbackSymbolNames.Add(String.Format("Nested{0}", i)) - sortedCallbackSymbolNames.Add(String.Format("NonGeneratedCode{0}", i)) - sortedCallbackSymbolNames.Add(String.Format("NestedGeneratedCode{0}", i)) - - sortedCallbackTreePaths.Add(file) + addNames = Sub(names As SortedSet(Of String)) + names.Add(String.Format("GeneratedCode{0}(IsGeneratedCode:True)", index)) + names.Add(String.Format("Nested{0}(IsGeneratedCode:True)", index)) + names.Add(String.Format("NonGeneratedCode{0}(IsGeneratedCode:{1})", index, isGeneratedFile)) + names.Add(String.Format("NestedGeneratedCode{0}(IsGeneratedCode:True)", index)) + End Sub + + addPath = Sub(paths As SortedSet(Of String)) + paths.Add($"{file}(IsGeneratedCode:{isGeneratedFile})") + End Sub ElseIf Not isGeneratedFile Then ' Analyzer always called for non-generated code. - sortedCallbackSymbolNames.Add(String.Format("NonGeneratedCode{0}", i)) - sortedCallbackTreePaths.Add(file) + addNames = Sub(names As SortedSet(Of String)) + names.Add(String.Format("NonGeneratedCode{0}(IsGeneratedCode:False)", index)) + End Sub + + addPath = Sub(paths As SortedSet(Of String)) + paths.Add($"{file}(IsGeneratedCode:False)") + End Sub + End If + + If addNames IsNot Nothing Then + addNames(sortedCallbackSymbolNames) + addNames(sortedCallbackSyntaxNodeNames) + addNames(sortedCallbackSymbolStartNames) + addNames(sortedCallbackSymbolEndNames) + addNames(sortedCallbackOperationNames) + addNames(sortedCallbackOperationBlockStartNames) + addNames(sortedCallbackOperationBlockEndNames) + addNames(sortedCallbackOperationBlockNames) + addNames(sortedCallbackCodeBlockStartNames) + addNames(sortedCallbackCodeBlockEndNames) + addNames(sortedCallbackCodeBlockNames) + End If + + If addPath IsNot Nothing Then + addPath(sortedCallbackTreePaths) + addPath(sortedCallbackSemanticModelPaths) End If Next ' Compilation end summary diagnostic (verify callbacks into analyzer) Dim arg1 = sortedCallbackSymbolNames.Join(",") Dim arg2 = sortedCallbackTreePaths.Join(",") - AddExpectedNonLocalDiagnostic(builder, {arg1, arg2}) + Dim arg3 = sortedCallbackSyntaxNodeNames.Join(",") + ";" + + sortedCallbackOperationNames.Join(",") + ";" + + sortedCallbackSemanticModelPaths.Join(",") + ";" + + sortedCallbackSymbolStartNames.Join(",") + ";" + + sortedCallbackSymbolEndNames.Join(",") + ";" + + sortedCallbackOperationBlockStartNames.Join(",") + ";" + + sortedCallbackOperationBlockEndNames.Join(",") + ";" + + sortedCallbackOperationBlockNames.Join(",") + ";" + + sortedCallbackCodeBlockStartNames.Join(",") + ";" + + sortedCallbackCodeBlockEndNames.Join(",") + ";" + + sortedCallbackCodeBlockNames.Join(",") + AddExpectedNonLocalDiagnostic(builder, GeneratedCodeAnalyzer.Summary2, {arg1, arg2, arg3}) If compilation.Options.GeneralDiagnosticOption = ReportDiagnostic.Error Then For i As Integer = 0 To builder.Count - 1 @@ -1255,8 +1322,8 @@ End Class End If End Sub - Private Shared Sub AddExpectedNonLocalDiagnostic(builder As ArrayBuilder(Of DiagnosticDescription), ParamArray arguments As String()) - AddExpectedDiagnostic(builder, GeneratedCodeAnalyzer.Summary.Id, Nothing, 1, 1, arguments) + Private Shared Sub AddExpectedNonLocalDiagnostic(builder As ArrayBuilder(Of DiagnosticDescription), descriptor As DiagnosticDescriptor, ParamArray arguments As String()) + AddExpectedDiagnostic(builder, descriptor.Id, Nothing, 1, 1, arguments) End Sub Private Shared Sub AddExpectedDiagnostic(builder As ArrayBuilder(Of DiagnosticDescription), diagnosticId As String, squiggledText As String, line As Integer, column As Integer, ParamArray arguments As String()) @@ -1264,6 +1331,21 @@ End Class builder.Add(diag) End Sub + + Friend NotInheritable Class GeneratedCodeAnalyzer + Inherits AbstractGeneratedCodeAnalyzer(Of SyntaxKind) + + Public Sub New(generatedCodeAnalysisFlags As GeneratedCodeAnalysisFlags?, Optional testIsGeneratedCodeInCallbacks As Boolean = False) + MyBase.New(generatedCodeAnalysisFlags, testIsGeneratedCodeInCallbacks) + End Sub + + Protected Overrides ReadOnly Property ClassDeclarationSyntaxKind As SyntaxKind + Get + Return SyntaxKind.ClassBlock + End Get + End Property + End Class + Public Sub TestFieldReferenceAnalyzer_InAttributes() Dim source =