From b2ccff104ac86ee2745a85d03da80f43f6865fc5 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 12 Jan 2017 19:03:17 -0800 Subject: [PATCH] Treat hidden code (#line hidden) as generated code for analyzers By default, we will analyze and report diagnostics in hidden code (similar to generated code). However, if an analyzer configures generated code analysis, we will treat the hidden code as generated code and skip analysis/diagnostics as per the configuration. Fixes #15903 --- .../Diagnostics/DiagnosticAnalyzerTests.cs | 199 ++++++++++++++++++ .../DiagnosticAnalyzer/AnalyzerDriver.cs | 48 ++++- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 56 +++-- .../Portable/Syntax/SyntaxTreeExtensions.cs | 15 ++ .../Shared/Extensions/SyntaxTreeExtensions.cs | 14 -- .../Core/Portable/Workspaces.csproj | 3 + 6 files changed, 304 insertions(+), 31 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs index cc565b910e3..738ec1c62bc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs @@ -1883,5 +1883,204 @@ void M2(int a, int b) Diagnostic("Parameter_ID", "a").WithLocation(26, 37), // M4 Diagnostic("Parameter_ID", "index").WithLocation(28, 25)); // indexer } + + [Fact, WorkItem(15903, "https://github.com/dotnet/roslyn/issues/15903")] + public void TestSymbolAnalyzer_HiddenRegions() + { + string source = @" + +#line hidden +public class HiddenClass +{ +} + +#line default +public class RegularClass +{ +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.None) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("GeneratedCodeAnalyzerWarning", "}").WithArguments("Source.cs").WithLocation(11, 1), + 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)); + + analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.Analyze) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("GeneratedCodeAnalyzerWarning", "}").WithArguments("Source.cs").WithLocation(11, 1), + 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)); + + analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("GeneratedCodeAnalyzerWarning", "}").WithArguments("Source.cs").WithLocation(11, 1), + Diagnostic("GeneratedCodeAnalyzerError", "}").WithArguments("Source.cs").WithLocation(11, 1), + Diagnostic("GeneratedCodeAnalyzerWarning", "HiddenClass").WithArguments("HiddenClass").WithLocation(4, 14), + 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)); + } + + [Fact, WorkItem(15903, "https://github.com/dotnet/roslyn/issues/15903")] + public void TestSyntaxAndOperationAnalyzer_HiddenRegions() + { + string source = @" + +public class Class +{ + void DummyMethod(int i) { } + +#line hidden + void HiddenMethod() + { + var hiddenVar = 0; + DummyMethod(hiddenVar); + } +#line default + + void NonHiddenMethod() + { + var userVar = 0; + DummyMethod(userVar); + } + + void MixMethod() + { +#line hidden + var mixMethodHiddenVar = 0; +#line default + var mixMethodUserVar = 0; + + DummyMethod(mixMethodHiddenVar + mixMethodUserVar); + } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, TestOptions.RegularWithIOperationFeature, "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var syntaxKinds = ImmutableArray.Create(SyntaxKind.VariableDeclaration); + var operationKinds = ImmutableArray.Create(OperationKind.VariableDeclaration); + + var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeSyntaxAndOperationAnalyzer(GeneratedCodeAnalysisFlags.None, syntaxKinds, operationKinds) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0").WithArguments("Node: var userVar = 0").WithLocation(17, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodUserVar = 0").WithArguments("Node: var mixMethodUserVar = 0").WithLocation(26, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0;").WithArguments("Operation: NonHiddenMethod").WithLocation(17, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodUserVar = 0;").WithArguments("Operation: MixMethod").WithLocation(26, 9), + Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("Node: var mixMethodUserVar = 0,Node: var userVar = 0,Operation: MixMethod,Operation: NonHiddenMethod").WithLocation(1, 1)); + + analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeSyntaxAndOperationAnalyzer(GeneratedCodeAnalysisFlags.Analyze, syntaxKinds, operationKinds) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0").WithArguments("Node: var userVar = 0").WithLocation(17, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0;").WithArguments("Operation: NonHiddenMethod").WithLocation(17, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodUserVar = 0").WithArguments("Node: var mixMethodUserVar = 0").WithLocation(26, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodUserVar = 0;").WithArguments("Operation: MixMethod").WithLocation(26, 9), + Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("Node: var hiddenVar = 0,Node: var mixMethodHiddenVar = 0,Node: var mixMethodUserVar = 0,Node: var userVar = 0,Operation: HiddenMethod,Operation: MixMethod,Operation: NonHiddenMethod").WithLocation(1, 1)); + + analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeSyntaxAndOperationAnalyzer(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics, syntaxKinds, operationKinds) }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("GeneratedCodeAnalyzerWarning", "var hiddenVar = 0").WithArguments("Node: var hiddenVar = 0").WithLocation(10, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var hiddenVar = 0;").WithArguments("Operation: HiddenMethod").WithLocation(10, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0").WithArguments("Node: var userVar = 0").WithLocation(17, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var userVar = 0;").WithArguments("Operation: NonHiddenMethod").WithLocation(17, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodHiddenVar = 0").WithArguments("Node: var mixMethodHiddenVar = 0").WithLocation(24, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodUserVar = 0").WithArguments("Node: var mixMethodUserVar = 0").WithLocation(26, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodHiddenVar = 0;").WithArguments("Operation: MixMethod").WithLocation(24, 9), + Diagnostic("GeneratedCodeAnalyzerWarning", "var mixMethodUserVar = 0;").WithArguments("Operation: MixMethod").WithLocation(26, 9), + Diagnostic("GeneratedCodeAnalyzerSummary").WithArguments("Node: var hiddenVar = 0,Node: var mixMethodHiddenVar = 0,Node: var mixMethodUserVar = 0,Node: var userVar = 0,Operation: HiddenMethod,Operation: MixMethod,Operation: NonHiddenMethod").WithLocation(1, 1)); + } + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class GeneratedCodeSyntaxAndOperationAnalyzer : DiagnosticAnalyzer + { + private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlagsOpt; + private readonly ImmutableArray _syntaxKinds; + private readonly ImmutableArray _operationKinds; + + public static readonly DiagnosticDescriptor Warning = new DiagnosticDescriptor( + "GeneratedCodeAnalyzerWarning", + "Title", + "GeneratedCodeAnalyzerMessage for '{0}'", + "Category", + DiagnosticSeverity.Warning, + true); + + public static readonly DiagnosticDescriptor Summary = new DiagnosticDescriptor( + "GeneratedCodeAnalyzerSummary", + "Title2", + "GeneratedCodeAnalyzer received callbacks for: '{0}' entities", + "Category", + DiagnosticSeverity.Warning, + true); + + public GeneratedCodeSyntaxAndOperationAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, ImmutableArray syntaxKinds, ImmutableArray operationKinds) + { + _generatedCodeAnalysisFlagsOpt = generatedCodeAnalysisFlagsOpt; + _syntaxKinds = syntaxKinds; + _operationKinds = operationKinds; + } + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning, Summary); + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(this.OnCompilationStart); + + if (_generatedCodeAnalysisFlagsOpt.HasValue) + { + // Configure analysis on generated code. + context.ConfigureGeneratedCodeAnalysis(_generatedCodeAnalysisFlagsOpt.Value); + } + } + + private void OnCompilationStart(CompilationStartAnalysisContext context) + { + var sortedCallbackEntityNames = new SortedSet(); + context.RegisterSyntaxNodeAction(syntaxContext => + { + sortedCallbackEntityNames.Add($"Node: {syntaxContext.Node.ToString()}"); + ReportNodeDiagnostics(syntaxContext.Node, syntaxContext.ReportDiagnostic); + }, _syntaxKinds); + + context.RegisterOperationAction(operationContext => + { + sortedCallbackEntityNames.Add($"Operation: {operationContext.ContainingSymbol.Name}"); + ReportOperationDiagnostics(operationContext.Operation, operationContext.ContainingSymbol.Name, operationContext.ReportDiagnostic); + }, _operationKinds); + + context.RegisterCompilationEndAction(endContext => + { + // Summary diagnostic about received callbacks. + var diagnostic = CodeAnalysis.Diagnostic.Create(Summary, Location.None, sortedCallbackEntityNames.Join(",")); + endContext.ReportDiagnostic(diagnostic); + }); + } + + private void ReportNodeDiagnostics(SyntaxNode node, Action addDiagnostic) + { + ReportDiagnosticsCore(addDiagnostic, node.Location, $"Node: {node.ToString()}"); + } + + private void ReportOperationDiagnostics(IOperation operation, string name, Action addDiagnostic) + { + ReportDiagnosticsCore(addDiagnostic, operation.Syntax.Location, $"Operation: {name}"); + } + + private void ReportDiagnosticsCore(Action addDiagnostic, Location location, params object[] messageArguments) + { + // warning diagnostic + var diagnostic = CodeAnalysis.Diagnostic.Create(Warning, location, messageArguments); + addDiagnostic(diagnostic); + } + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 30785813c3b..e96e0d97c15 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -79,6 +79,11 @@ internal abstract partial class AnalyzerDriver : IDisposable /// private Dictionary> _lazyGeneratedCodeSymbolsMap; + /// + /// Lazily populated dictionary indicating whether a source file has any hidden regions - we populate it lazily to avoid realizing all syntax trees in the compilation upfront. + /// + private Dictionary _lazyTreesWithHiddenRegionsMap; + /// /// Symbol for . /// @@ -155,6 +160,7 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn _treatAllCodeAsNonGeneratedCode = ShouldTreatAllCodeAsNonGeneratedCode(unsuppressedAnalyzers, _generatedCodeAnalysisFlagsMap); _lazyGeneratedCodeFilesMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary(); _lazyGeneratedCodeSymbolsMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary>(); + _lazyTreesWithHiddenRegionsMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary(); _generatedCodeAttribute = analyzerExecutor.Compilation?.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute"); _symbolActionsByKind = MakeSymbolActionsByKind(); @@ -243,7 +249,7 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn var analyzerExecutor = AnalyzerExecutor.Create( compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnosticOpt, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, - IsCompilerAnalyzer, analyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, GetAnalyzerGate, + IsCompilerAnalyzer, analyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedCodeLocation, GetAnalyzerGate, analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, cancellationToken); Initialize(analyzerExecutor, diagnosticQueue, compilationData, cancellationToken); @@ -602,8 +608,8 @@ private bool IsInGeneratedCode(Location location, Compilation compilation, Cance return false; } - // Check if this is a generated code file by its extension. - if (IsGeneratedCode(location.SourceTree)) + // Check if this is a generated code location. + if (IsGeneratedCodeLocation(location)) { return true; } @@ -1265,7 +1271,7 @@ private bool IsGeneratedCodeSymbol(ISymbol symbol) foreach (var declaringRef in symbol.DeclaringSyntaxReferences) { - if (!IsGeneratedCode(declaringRef.SyntaxTree)) + if (!IsGeneratedCodeLocation(declaringRef.GetLocation())) { return false; } @@ -1298,6 +1304,38 @@ protected bool IsGeneratedCode(SyntaxTree tree) protected bool DoNotAnalyzeGeneratedCode => _doNotAnalyzeGeneratedCode; + // Location is in generated code if either the containing tree is a generated code file OR if it is a hidden source location. + protected bool IsGeneratedCodeLocation(Location location) => + IsGeneratedCode(location.SourceTree) || IsHiddenSourceLocation(location); + + protected bool IsHiddenSourceLocation(Location location) => + location.IsInSource && + HasHiddenRegions(location.SourceTree) && + location.SourceTree.IsHiddenPosition(location.SourceSpan.Start); + + private bool HasHiddenRegions(SyntaxTree tree) + { + Debug.Assert(tree != null); + + if (_treatAllCodeAsNonGeneratedCode) + { + return false; + } + + Debug.Assert(_lazyTreesWithHiddenRegionsMap != null); + + lock (_lazyTreesWithHiddenRegionsMap) + { + bool hasHiddenRegions; + if (!_lazyTreesWithHiddenRegionsMap.TryGetValue(tree, out hasHiddenRegions)) + { + hasHiddenRegions = tree.HasHiddenRegions(); + } + + return hasHiddenRegions; + } + } + internal async Task GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CompilationOptions compilationOptions, CancellationToken cancellationToken) { var executor = analyzerExecutor.WithCancellationToken(cancellationToken); @@ -1589,7 +1627,7 @@ private bool ShouldExecuteOperationBlockActions(AnalysisScope analysisScope, ISy continue; } - var isInGeneratedCode = isGeneratedCodeSymbol || IsGeneratedCode(decl.SyntaxTree); + var isInGeneratedCode = isGeneratedCodeSymbol || IsGeneratedCodeLocation(decl.GetLocation()); if (isInGeneratedCode && DoNotAnalyzeGeneratedCode) { analysisStateOpt?.MarkDeclarationComplete(symbol, i, analysisScope.Analyzers); diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index 9f0a1636c68..e4d7e16d74a 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -41,6 +41,7 @@ internal class AnalyzerExecutor private readonly Func _getAnalyzerGateOpt; private readonly Func _shouldSkipAnalysisOnGeneratedCode; private readonly Func _shouldSuppressGeneratedCodeDiagnostic; + private readonly Func _isGeneratedCodeLocation; private readonly ConcurrentDictionary _analyzerExecutionTimeMapOpt; private readonly CompilationAnalysisValueProviderFactory _compilationAnalysisValueProviderFactory; private readonly CancellationToken _cancellationToken; @@ -69,6 +70,7 @@ internal class AnalyzerExecutor /// /// Delegate to identify if analysis should be skipped on generated code. /// Delegate to identify if diagnostic reported while analyzing generated code should be suppressed. + /// Delegate to identify if the given location is in generated code. /// Flag indicating whether we need to log analyzer execution time. /// Optional delegate to add categorized local analyzer diagnostics. /// Optional delegate to add categorized non-local analyzer diagnostics. @@ -83,6 +85,7 @@ internal class AnalyzerExecutor AnalyzerManager analyzerManager, Func shouldSkipAnalysisOnGeneratedCode, Func shouldSuppressGeneratedCodeDiagnostic, + Func isGeneratedCodeLocation, Func getAnalyzerGate, bool logExecutionTime = false, Action addCategorizedLocalDiagnosticOpt = null, @@ -96,7 +99,7 @@ internal class AnalyzerExecutor var analyzerExecutionTimeMapOpt = logExecutionTime ? new ConcurrentDictionary() : null; return new AnalyzerExecutor(compilation, analyzerOptions, addNonCategorizedDiagnosticOpt, onAnalyzerException, analyzerExceptionFilter, - isCompilerAnalyzer, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, + isCompilerAnalyzer, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation, getAnalyzerGate, analyzerExecutionTimeMapOpt, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, cancellationToken); } @@ -121,6 +124,7 @@ internal class AnalyzerExecutor isCompilerAnalyzer: null, shouldSkipAnalysisOnGeneratedCode: _ => false, shouldSuppressGeneratedCodeDiagnostic: (diagnostic, analyzer, compilation, ct) => false, + isGeneratedCodeLocation: _ => false, getAnalyzerGateOpt: null, onAnalyzerException: onAnalyzerException, analyzerExceptionFilter: null, @@ -141,6 +145,7 @@ internal class AnalyzerExecutor AnalyzerManager analyzerManager, Func shouldSkipAnalysisOnGeneratedCode, Func shouldSuppressGeneratedCodeDiagnostic, + Func isGeneratedCodeLocation, Func getAnalyzerGateOpt, ConcurrentDictionary analyzerExecutionTimeMapOpt, Action addCategorizedLocalDiagnosticOpt, @@ -156,6 +161,7 @@ internal class AnalyzerExecutor _analyzerManager = analyzerManager; _shouldSkipAnalysisOnGeneratedCode = shouldSkipAnalysisOnGeneratedCode; _shouldSuppressGeneratedCodeDiagnostic = shouldSuppressGeneratedCodeDiagnostic; + _isGeneratedCodeLocation = isGeneratedCodeLocation; _getAnalyzerGateOpt = getAnalyzerGateOpt; _analyzerExecutionTimeMapOpt = analyzerExecutionTimeMapOpt; _addCategorizedLocalDiagnosticOpt = addCategorizedLocalDiagnosticOpt; @@ -173,7 +179,7 @@ public AnalyzerExecutor WithCancellationToken(CancellationToken cancellationToke } return new AnalyzerExecutor(_compilation, _analyzerOptions, _addNonCategorizedDiagnosticOpt, _onAnalyzerException, _analyzerExceptionFilter, - _isCompilerAnalyzer, _analyzerManager, _shouldSkipAnalysisOnGeneratedCode, _shouldSuppressGeneratedCodeDiagnostic, + _isCompilerAnalyzer, _analyzerManager, _shouldSkipAnalysisOnGeneratedCode, _shouldSuppressGeneratedCodeDiagnostic, _isGeneratedCodeLocation, _getAnalyzerGateOpt, _analyzerExecutionTimeMapOpt, _addCategorizedLocalDiagnosticOpt, _addCategorizedNonLocalDiagnosticOpt, cancellationToken); } @@ -759,13 +765,13 @@ private void ExecuteCompilationActionsCore(ImmutableArray)getNodesToAnalyze(executableBlocks); - ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, declaredSymbol, semanticModel, getKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); + ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); } else if (operationActions != null) { var operationActionsByKind = GetOperationActionsByKind(operationActions); var operationsToAnalyze = (IEnumerable)getNodesToAnalyze(executableBlocks); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, declaredSymbol, semanticModel, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); + ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); } } @@ -903,12 +909,13 @@ private void ExecuteCompilationActionsCore(ImmutableArray( IEnumerable nodesToAnalyze, IDictionary>> nodeActionsByKind, + DiagnosticAnalyzer analyzer, ISymbol containingSymbol, SemanticModel model, Func getKind, @@ -927,7 +934,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray operationsToAnalyze, IDictionary> operationActionsByKind, + DiagnosticAnalyzer analyzer, ISymbol containingSymbol, SemanticModel model, Action addDiagnostic, @@ -1059,7 +1067,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray + /// Returns true if the provided position is in a hidden region inaccessible to the user. + /// + public static bool IsHiddenPosition(this SyntaxTree tree, int position, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!tree.HasHiddenRegions()) + { + return false; + } + + var lineVisibility = tree.GetLineVisibility(position, cancellationToken); + return lineVisibility == LineVisibility.Hidden || lineVisibility == LineVisibility.BeforeFirstLineDirective; + } } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxTreeExtensions.cs index 4377404e431..91f4f4db050 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxTreeExtensions.cs @@ -115,20 +115,6 @@ public static bool IsEntirelyHidden(this SyntaxTree tree, TextSpan span, Cancell return true; } - /// - /// Returns true if the provided position is in a hidden region inaccessible to the user. - /// - public static bool IsHiddenPosition(this SyntaxTree tree, int position, CancellationToken cancellationToken = default(CancellationToken)) - { - if (!tree.HasHiddenRegions()) - { - return false; - } - - var lineVisibility = tree.GetLineVisibility(position, cancellationToken); - return lineVisibility == LineVisibility.Hidden || lineVisibility == LineVisibility.BeforeFirstLineDirective; - } - public static async Task IsBeforeFirstTokenAsync( this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 6faaa70bc45..95327637d25 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -186,6 +186,9 @@ InternalUtilities\ReflectionUtilities.cs + + InternalUtilities\SyntaxTreeExtensions.cs + -- GitLab