提交 b2ccff10 编写于 作者: M Manish Vasani

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
上级 b0b8066e
......@@ -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<SyntaxKind> _syntaxKinds;
private readonly ImmutableArray<OperationKind> _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<SyntaxKind> syntaxKinds, ImmutableArray<OperationKind> operationKinds)
{
_generatedCodeAnalysisFlagsOpt = generatedCodeAnalysisFlagsOpt;
_syntaxKinds = syntaxKinds;
_operationKinds = operationKinds;
}
public override ImmutableArray<DiagnosticDescriptor> 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<string>();
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<Diagnostic> addDiagnostic)
{
ReportDiagnosticsCore(addDiagnostic, node.Location, $"Node: {node.ToString()}");
}
private void ReportOperationDiagnostics(IOperation operation, string name, Action<Diagnostic> addDiagnostic)
{
ReportDiagnosticsCore(addDiagnostic, operation.Syntax.Location, $"Operation: {name}");
}
private void ReportDiagnosticsCore(Action<Diagnostic> addDiagnostic, Location location, params object[] messageArguments)
{
// warning diagnostic
var diagnostic = CodeAnalysis.Diagnostic.Create(Warning, location, messageArguments);
addDiagnostic(diagnostic);
}
}
}
}
......@@ -79,6 +79,11 @@ internal abstract partial class AnalyzerDriver : IDisposable
/// </summary>
private Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>> _lazyGeneratedCodeSymbolsMap;
/// <summary>
/// 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.
/// </summary>
private Dictionary<SyntaxTree, bool> _lazyTreesWithHiddenRegionsMap;
/// <summary>
/// Symbol for <see cref="System.CodeDom.Compiler.GeneratedCodeAttribute"/>.
/// </summary>
......@@ -155,6 +160,7 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn
_treatAllCodeAsNonGeneratedCode = ShouldTreatAllCodeAsNonGeneratedCode(unsuppressedAnalyzers, _generatedCodeAnalysisFlagsMap);
_lazyGeneratedCodeFilesMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary<SyntaxTree, bool>();
_lazyGeneratedCodeSymbolsMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>>();
_lazyTreesWithHiddenRegionsMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary<SyntaxTree, bool>();
_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<AnalyzerActionCounts> 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);
......
......@@ -41,6 +41,7 @@ internal class AnalyzerExecutor
private readonly Func<DiagnosticAnalyzer, object> _getAnalyzerGateOpt;
private readonly Func<DiagnosticAnalyzer, bool> _shouldSkipAnalysisOnGeneratedCode;
private readonly Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> _shouldSuppressGeneratedCodeDiagnostic;
private readonly Func<Location, bool> _isGeneratedCodeLocation;
private readonly ConcurrentDictionary<DiagnosticAnalyzer, TimeSpan> _analyzerExecutionTimeMapOpt;
private readonly CompilationAnalysisValueProviderFactory _compilationAnalysisValueProviderFactory;
private readonly CancellationToken _cancellationToken;
......@@ -69,6 +70,7 @@ internal class AnalyzerExecutor
/// </param>
/// <param name="shouldSkipAnalysisOnGeneratedCode">Delegate to identify if analysis should be skipped on generated code.</param>
/// <param name="shouldSuppressGeneratedCodeDiagnostic">Delegate to identify if diagnostic reported while analyzing generated code should be suppressed.</param>
/// <param name="isGeneratedCodeLocation">Delegate to identify if the given location is in generated code.</param>
/// <param name="logExecutionTime">Flag indicating whether we need to log analyzer execution time.</param>
/// <param name="addCategorizedLocalDiagnosticOpt">Optional delegate to add categorized local analyzer diagnostics.</param>
/// <param name="addCategorizedNonLocalDiagnosticOpt">Optional delegate to add categorized non-local analyzer diagnostics.</param>
......@@ -83,6 +85,7 @@ internal class AnalyzerExecutor
AnalyzerManager analyzerManager,
Func<DiagnosticAnalyzer, bool> shouldSkipAnalysisOnGeneratedCode,
Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic,
Func<Location, bool> isGeneratedCodeLocation,
Func<DiagnosticAnalyzer, object> getAnalyzerGate,
bool logExecutionTime = false,
Action<Diagnostic, DiagnosticAnalyzer, bool> addCategorizedLocalDiagnosticOpt = null,
......@@ -96,7 +99,7 @@ internal class AnalyzerExecutor
var analyzerExecutionTimeMapOpt = logExecutionTime ? new ConcurrentDictionary<DiagnosticAnalyzer, TimeSpan>() : 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<DiagnosticAnalyzer, bool> shouldSkipAnalysisOnGeneratedCode,
Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic,
Func<Location, bool> isGeneratedCodeLocation,
Func<DiagnosticAnalyzer, object> getAnalyzerGateOpt,
ConcurrentDictionary<DiagnosticAnalyzer, TimeSpan> analyzerExecutionTimeMapOpt,
Action<Diagnostic, DiagnosticAnalyzer, bool> 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<CompilationAnalyzerAct
{
var executableNodeActionsByKind = GetNodeActionsByKind(syntaxNodeActions);
var syntaxNodesToAnalyze = (IEnumerable<SyntaxNode>)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<IOperation>)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<CompilationAnalyzerAct
}
var addDiagnostic = GetAddDiagnostic(model.SyntaxTree, filterSpan, analyzer, isSyntaxDiagnostic: false);
ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, analyzerStateOpt);
ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, containingSymbol, model, getKind, addDiagnostic, analyzerStateOpt);
}
private void ExecuteSyntaxNodeActions<TLanguageKindEnum>(
IEnumerable<SyntaxNode> nodesToAnalyze,
IDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> nodeActionsByKind,
DiagnosticAnalyzer analyzer,
ISymbol containingSymbol,
SemanticModel model,
Func<SyntaxNode, TLanguageKindEnum> getKind,
......@@ -927,7 +934,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
foreach (var child in nodesToAnalyze)
{
if (ShouldExecuteNode(analyzerStateOpt, child))
if (ShouldExecuteNode(analyzerStateOpt, child, analyzer))
{
SetCurrentNode(analyzerStateOpt, child);
......@@ -1037,12 +1044,13 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
}
var addDiagnostic = GetAddDiagnostic(model.SyntaxTree, filterSpan, analyzer, isSyntaxDiagnostic: false);
ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, containingSymbol, model, addDiagnostic, analyzerStateOpt);
ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, containingSymbol, model, addDiagnostic, analyzerStateOpt);
}
private void ExecuteOperationActions(
IEnumerable<IOperation> operationsToAnalyze,
IDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> operationActionsByKind,
DiagnosticAnalyzer analyzer,
ISymbol containingSymbol,
SemanticModel model,
Action<Diagnostic> addDiagnostic,
......@@ -1059,7 +1067,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
foreach (var child in operationsToAnalyze)
{
if (ShouldExecuteOperation(analyzerStateOpt, child))
if (ShouldExecuteOperation(analyzerStateOpt, child, analyzer))
{
SetCurrentOperation(analyzerStateOpt, child);
......@@ -1413,14 +1421,38 @@ private static bool ShouldExecuteAction(AnalyzerStateData analyzerStateOpt, Anal
return analyzerStateOpt == null || !analyzerStateOpt.ProcessedActions.Contains(action);
}
private static bool ShouldExecuteNode(SyntaxNodeAnalyzerStateData analyzerStateOpt, SyntaxNode node)
private bool ShouldExecuteNode(SyntaxNodeAnalyzerStateData analyzerStateOpt, SyntaxNode node, DiagnosticAnalyzer analyzer)
{
return analyzerStateOpt == null || !analyzerStateOpt.ProcessedNodes.Contains(node);
// Check if the node has already been processed.
if (analyzerStateOpt != null && analyzerStateOpt.ProcessedNodes.Contains(node))
{
return false;
}
// Check if the node is generated code that must be skipped.
if (_shouldSkipAnalysisOnGeneratedCode(analyzer) && _isGeneratedCodeLocation(node.GetLocation()))
{
return false;
}
return true;
}
private static bool ShouldExecuteOperation(OperationAnalyzerStateData analyzerStateOpt, IOperation operation)
private bool ShouldExecuteOperation(OperationAnalyzerStateData analyzerStateOpt, IOperation operation, DiagnosticAnalyzer analyzer)
{
return analyzerStateOpt == null || !analyzerStateOpt.ProcessedOperations.Contains(operation);
// Check if the operation has already been processed.
if (analyzerStateOpt != null && analyzerStateOpt.ProcessedOperations.Contains(operation))
{
return false;
}
// Check if the operation syntax is generated code that must be skipped.
if (operation.Syntax != null && _shouldSkipAnalysisOnGeneratedCode(analyzer) && _isGeneratedCodeLocation(operation.Syntax.GetLocation()))
{
return false;
}
return true;
}
private static void SetCurrentNode(SyntaxNodeAnalyzerStateData analyzerStateOpt, SyntaxNode node)
......
......@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis
......@@ -94,5 +95,19 @@ private static int FindFirstDifference(string s1, string s2)
}
return (n1 == n2) ? -1 : n + 1;
}
/// <summary>
/// Returns <c>true</c> if the provided position is in a hidden region inaccessible to the user.
/// </summary>
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;
}
}
}
......@@ -115,20 +115,6 @@ public static bool IsEntirelyHidden(this SyntaxTree tree, TextSpan span, Cancell
return true;
}
/// <summary>
/// Returns <c>true</c> if the provided position is in a hidden region inaccessible to the user.
/// </summary>
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<bool> IsBeforeFirstTokenAsync(
this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
......
......@@ -186,6 +186,9 @@
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\ReflectionUtilities.cs">
<Link>InternalUtilities\ReflectionUtilities.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\Syntax\SyntaxTreeExtensions.cs">
<Link>InternalUtilities\SyntaxTreeExtensions.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="csi" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册