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);
                }
            }
Exemple #3
0
        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;
            }
        }
Exemple #4
0
        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);
                }
            }