diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs index cc565b910e336dadb330decb8cb34b1fbb117986..738ec1c62bcf1eb21183496bc8537af9c09d96db 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 30785813c3b203300b11cb79000e0ee7200c5128..e96e0d97c15de98aea3e1d13060fd33a7817bb2d 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 9f0a1636c68757280c91136438f3e54d6a448d52..e4d7e16d74a4cda32d13af94ac3a9db0145074a1 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 4377404e431873dbfb96b8c4e5a3865c9b2481dd..91f4f4db05031af724d9b905ac8518b47d084bdb 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 6faaa70bc459feb8a8d4a6e314e344816895e52d..95327637d25128611ba2a36f873e0981773a3209 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 +