private void VerifyDiagnostics(SemanticModel model) { #if DEBUG // Exclude unused import diagnostics since they are never reported when a span is passed. // (See CSharp/VisualBasicCompilation.GetDiagnosticsForMethodBodiesInTree.) bool shouldInclude(Diagnostic d) => _range.IntersectsWith(d.Location.SourceSpan) && !IsUnusedImportDiagnostic(d); // make sure what we got from range is same as what we got from whole diagnostics var rangeDeclaractionDiagnostics = model.GetDeclarationDiagnostics(_range).ToArray(); var rangeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics(_range).ToArray(); var rangeDiagnostics = rangeDeclaractionDiagnostics.Concat(rangeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); var wholeDeclarationDiagnostics = model.GetDeclarationDiagnostics().ToArray(); var wholeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics().ToArray(); var wholeDiagnostics = wholeDeclarationDiagnostics.Concat(wholeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); if (!AreEquivalent(rangeDiagnostics, wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); // make sure we hold onto these for debugging. GC.KeepAlive(rangeDeclaractionDiagnostics); GC.KeepAlive(rangeMethodBodyDiagnostics); GC.KeepAlive(rangeDiagnostics); GC.KeepAlive(wholeDeclarationDiagnostics); GC.KeepAlive(wholeMethodBodyDiagnostics); GC.KeepAlive(wholeDiagnostics); }
private void VerifyDiagnostics(SemanticModel model) { Func <Diagnostic, bool> shouldInclude = d => _range.IntersectsWith(d.Location.SourceSpan); // make sure what we got from range is same as what we got from whole diagnostics var rangeDeclaractionDiagnostics = model.GetDeclarationDiagnostics(_range).ToArray(); var rangeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics(_range).ToArray(); var rangeDiagnostics = rangeDeclaractionDiagnostics.Concat(rangeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); var set = new HashSet <Diagnostic>(rangeDiagnostics); var wholeDeclarationDiagnostics = model.GetDeclarationDiagnostics().ToArray(); var wholeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics().ToArray(); var wholeDiagnostics = wholeDeclarationDiagnostics.Concat(wholeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); if (!set.SetEquals(wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); // make sure we hold onto these even in ret build for debugging. GC.KeepAlive(set); GC.KeepAlive(rangeDeclaractionDiagnostics); GC.KeepAlive(rangeMethodBodyDiagnostics); GC.KeepAlive(rangeDiagnostics); GC.KeepAlive(wholeDeclarationDiagnostics); GC.KeepAlive(wholeMethodBodyDiagnostics); GC.KeepAlive(wholeDiagnostics); } }
public static void ForeachClassSyntax(this CodeWriter codeWriter, IEnumerable <ClassDeclarationSyntax> classSyntax, Func <INamedTypeSymbol, CodeWriter, CodeFile> codeFileFactory) { _ = codeFileFactory ?? throw new ArgumentNullException(nameof(codeFileFactory)); var dic = new Dictionary <string, ClassSyntaxCachedInfo>(); foreach (var clazz in classSyntax ?? Enumerable.Empty <ClassDeclarationSyntax>()) { SemanticModel model = codeWriter.Compilation.GetSemanticModel(clazz.SyntaxTree); var clazzSymbol = model.GetDeclaredSymbol(clazz); var digs = model.GetDeclarationDiagnostics(); if (digs.Length > 0) { // Log warning.. } var qualifiedName = clazzSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); dic[qualifiedName] = new ClassSyntaxCachedInfo { Handled = false, NameTypedSymbol = clazzSymbol, Syntax = clazz, QualifiedName = qualifiedName, }; } foreach (var classCachedInfo in dic.Values) { Visit(classCachedInfo); } void Visit(ClassSyntaxCachedInfo value) { if (value.Handled) { return; } if (value.NameTypedSymbol.BaseType != null) { var baseQualifiedName = value.NameTypedSymbol.BaseType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); if (dic.TryGetValue(baseQualifiedName, out var baseCachedInfo)) { Visit(baseCachedInfo); } } SemanticModel model = codeWriter.Compilation.GetSemanticModel(value.Syntax.SyntaxTree); var clazzSymbol = model.GetDeclaredSymbol(value.Syntax); codeWriter.WriteCodeFile(codeFileFactory(clazzSymbol, codeWriter)); value.Handled = true; } }
private async Task <ImmutableArray <Diagnostic> > GetSemanticDiagnosticsAsync(SemanticModel model, DiagnosticAnalyzer analyzer, bool isCompilerAnalyzer, CancellationToken cancellationToken) { // PERF: // 1. Compute diagnostics for all analyzers with a single invocation into CompilationWithAnalyzers. // This is critical for better analyzer execution performance through re-use of bound node cache. // 2. Ensure that the compiler analyzer is treated specially and does not block on diagnostic computation // for rest of the analyzers. This is needed to ensure faster refresh for compiler diagnostics while typing. RoslynDebug.Assert(_compilationWithAnalyzers != null); var span = AnalysisScope.Span; if (isCompilerAnalyzer) { #if DEBUG VerifySpanBasedCompilerDiagnostics(model); #endif var adjustedSpan = await GetAdjustedSpanForCompilerAnalyzerAsync().ConfigureAwait(false); // TODO: Move this invocation to OOP return(await _compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, adjustedSpan, ImmutableArray.Create(analyzer), cancellationToken).ConfigureAwait(false)); } // We specially handle IPragmaSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers' // context to compute unnecessary pragma suppression diagnostics. // This is required because this analyzer relies on reported compiler + analyzer diagnostics // for unnecessary pragma analysis. if (analyzer is IPragmaSuppressionsAnalyzer suppressionsAnalyzer && !AnalysisScope.Span.HasValue) { using var _ = ArrayBuilder <Diagnostic> .GetInstance(out var builder); await suppressionsAnalyzer.AnalyzeAsync(model, span, _compilationWithAnalyzers, _analyzerInfoCache.GetDiagnosticDescriptors, IsCompilationEndAnalyzer, builder.Add, cancellationToken).ConfigureAwait(false); return(builder.ToImmutable()); } if (_lazySemanticDiagnostics == null) { // TODO: Move this invocation to OOP var analysisResult = await _compilationWithAnalyzers.GetAnalysisResultAsync(model, span, _compilationBasedAnalyzersInAnalysisScope, cancellationToken).ConfigureAwait(false); var treeDiagnostics = analysisResult.SemanticDiagnostics.TryGetValue(model.SyntaxTree, out var value) ? value : ImmutableDictionary <DiagnosticAnalyzer, ImmutableArray <Diagnostic> > .Empty; Interlocked.CompareExchange(ref _lazySemanticDiagnostics, treeDiagnostics, null); } return(_lazySemanticDiagnostics.TryGetValue(analyzer, out var diagnostics) ? diagnostics : ImmutableArray <Diagnostic> .Empty); bool IsCompilationEndAnalyzer(DiagnosticAnalyzer analyzer) { RoslynDebug.AssertNotNull(_compilationWithAnalyzers); return(_analyzerInfoCache.IsCompilationEndAnalyzer(analyzer, AnalysisScope.Document.Project, _compilationWithAnalyzers.Compilation) ?? true); } async Task <TextSpan?> GetAdjustedSpanForCompilerAnalyzerAsync() { // This method is to workaround a bug (https://github.com/dotnet/roslyn/issues/1557) // once that bug is fixed, we should be able to use given span as it is. Debug.Assert(isCompilerAnalyzer); if (!span.HasValue) { return(null); } var service = AnalysisScope.Document.GetRequiredLanguageService <ISyntaxFactsService>(); var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var startNode = service.GetContainingMemberDeclaration(root, span.Value.Start); var endNode = service.GetContainingMemberDeclaration(root, span.Value.End); if (startNode == endNode) { // use full member span if (service.IsMethodLevelMember(startNode)) { return(startNode.FullSpan); } // use span as it is return(span); } var startSpan = service.IsMethodLevelMember(startNode) ? startNode.FullSpan : span.Value; var endSpan = service.IsMethodLevelMember(endNode) ? endNode.FullSpan : span.Value; return(TextSpan.FromBounds(Math.Min(startSpan.Start, endSpan.Start), Math.Max(startSpan.End, endSpan.End))); } #if DEBUG void VerifySpanBasedCompilerDiagnostics(SemanticModel model) { if (!span.HasValue) { return; } // make sure what we got from range is same as what we got from whole diagnostics var rangeDeclaractionDiagnostics = model.GetDeclarationDiagnostics(span.Value).ToArray(); var rangeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics(span.Value).ToArray(); var rangeDiagnostics = rangeDeclaractionDiagnostics.Concat(rangeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); var wholeDeclarationDiagnostics = model.GetDeclarationDiagnostics().ToArray(); var wholeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics().ToArray(); var wholeDiagnostics = wholeDeclarationDiagnostics.Concat(wholeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); if (!AnalyzerHelper.AreEquivalent(rangeDiagnostics, wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); // make sure we hold onto these for debugging. GC.KeepAlive(rangeDeclaractionDiagnostics); GC.KeepAlive(rangeMethodBodyDiagnostics); GC.KeepAlive(rangeDiagnostics); GC.KeepAlive(wholeDeclarationDiagnostics); GC.KeepAlive(wholeMethodBodyDiagnostics); GC.KeepAlive(wholeDiagnostics); } return;
private void VerifyDiagnostics(SemanticModel model) { // Exclude unused import diagnostics since they are never reported when a span is passed. // (See CSharp/VisualBasicCompilation.GetDiagnosticsForMethodBodiesInTree.) Func<Diagnostic, bool> shouldInclude = d => _range.IntersectsWith(d.Location.SourceSpan) && !IsUnusedImportDiagnostic(d); // make sure what we got from range is same as what we got from whole diagnostics var rangeDeclaractionDiagnostics = model.GetDeclarationDiagnostics(_range).ToArray(); var rangeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics(_range).ToArray(); var rangeDiagnostics = rangeDeclaractionDiagnostics.Concat(rangeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); var set = new HashSet<Diagnostic>(rangeDiagnostics); var wholeDeclarationDiagnostics = model.GetDeclarationDiagnostics().ToArray(); var wholeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics().ToArray(); var wholeDiagnostics = wholeDeclarationDiagnostics.Concat(wholeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); if (!set.SetEquals(wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); // make sure we hold onto these even in ret build for debugging. GC.KeepAlive(set); GC.KeepAlive(rangeDeclaractionDiagnostics); GC.KeepAlive(rangeMethodBodyDiagnostics); GC.KeepAlive(rangeDiagnostics); GC.KeepAlive(wholeDeclarationDiagnostics); GC.KeepAlive(wholeMethodBodyDiagnostics); GC.KeepAlive(wholeDiagnostics); } }
private void VerifyDiagnostics(SemanticModel model) { Func<Diagnostic, bool> shouldInclude = d => _range.IntersectsWith(d.Location.SourceSpan); // make sure what we got from range is same as what we got from whole diagnostics var rangeDeclaractionDiagnostics = model.GetDeclarationDiagnostics(_range).ToArray(); var rangeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics(_range).ToArray(); var rangeDiagnostics = rangeDeclaractionDiagnostics.Concat(rangeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); var set = new HashSet<Diagnostic>(rangeDiagnostics); var wholeDeclarationDiagnostics = model.GetDeclarationDiagnostics().ToArray(); var wholeMethodBodyDiagnostics = model.GetMethodBodyDiagnostics().ToArray(); var wholeDiagnostics = wholeDeclarationDiagnostics.Concat(wholeMethodBodyDiagnostics).Where(shouldInclude).ToArray(); if (!set.SetEquals(wholeDiagnostics)) { // otherwise, report non-fatal watson so that we can fix those cases FatalError.ReportWithoutCrash(new Exception("Bug in GetDiagnostics")); // make sure we hold onto these even in ret build for debugging. GC.KeepAlive(set); GC.KeepAlive(rangeDeclaractionDiagnostics); GC.KeepAlive(rangeMethodBodyDiagnostics); GC.KeepAlive(rangeDiagnostics); GC.KeepAlive(wholeDeclarationDiagnostics); GC.KeepAlive(wholeMethodBodyDiagnostics); GC.KeepAlive(wholeDiagnostics); } }