diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs index 31879182e3d37..07964c595f4a1 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(); @@ -1614,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( @@ -1677,21 +1692,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 +1743,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 +1751,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 +1759,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 +1864,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 +2181,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 +2189,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 +2199,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 0483d0cd41fc6..fb9f2ed41f367 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 29671b91449a4..3e388bebf0909 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs @@ -653,18 +653,32 @@ public readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 Microsoft.CodeAnalysis.ModuleMetadata! Microsoft.CodeAnalysis.Compilation.CreateBuiltinOperator(string! name, Microsoft.CodeAnalysis.ITypeSymbol! returnType, Microsoft.CodeAnalysis.ITypeSymbol! leftType, Microsoft.CodeAnalysis.ITypeSymbol! rightType) -> Microsoft.CodeAnalysis.IMethodSymbol! Microsoft.CodeAnalysis.Compilation.CreateBuiltinOperator(string! name, Microsoft.CodeAnalysis.ITypeSymbol! returnType, Microsoft.CodeAnalysis.ITypeSymbol! operandType) -> Microsoft.CodeAnalysis.IMethodSymbol! +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..716a5f32c0694 100644 --- a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs +++ b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs @@ -1313,10 +1313,11 @@ 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; public static readonly DiagnosticDescriptor Warning = new DiagnosticDescriptor( "GeneratedCodeAnalyzerWarning", @@ -1342,12 +1343,23 @@ 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); + + protected AbstractGeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, bool testIsGeneratedCodeInCallbacks) { _generatedCodeAnalysisFlagsOpt = generatedCodeAnalysisFlagsOpt; + _testIsGeneratedCodeInCallbacks = testIsGeneratedCodeInCallbacks; } - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning, Error, Summary); + protected abstract TSyntaxKind ClassDeclarationSyntaxKind { get; } + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning, Error, Summary, Summary2); public override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction(this.OnCompilationStart); @@ -1365,23 +1377,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})"), + ImmutableArray.Create(ClassDeclarationSyntaxKind)); + + 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); }); } 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 =