Exemple #1
0
        private ImmutableArray <Diagnostic> AnalyzeSemanticModel(SemanticModelAnalysisContext context, int positionOfFirstReducingNullableDirective, SimpleIntervalTree <TextSpan, TextSpanIntervalIntrospector>?codeBlockIntervalTree, SimpleIntervalTree <TextSpan, TextSpanIntervalIntrospector>?possibleNullableImpactIntervalTree)
        {
            var root = context.SemanticModel.SyntaxTree.GetCompilationUnitRoot(context.CancellationToken);

            using (var simplifier = new NullableImpactingSpanWalker(context.SemanticModel, positionOfFirstReducingNullableDirective, ignoredSpans: codeBlockIntervalTree, context.CancellationToken))
            {
                simplifier.Visit(root);
                possibleNullableImpactIntervalTree ??= new SimpleIntervalTree <TextSpan, TextSpanIntervalIntrospector>(new TextSpanIntervalIntrospector(), values: null);
                foreach (var interval in simplifier.Spans)
                {
                    possibleNullableImpactIntervalTree.AddIntervalInPlace(interval);
                }
            }

            using var diagnostics = TemporaryArray <Diagnostic> .Empty;

            var compilationOptions = ((CSharpCompilationOptions)context.SemanticModel.Compilation.Options).NullableContextOptions;

            DirectiveTriviaSyntax? previousRetainedDirective = null;
            NullableContextOptions?retainedOptions           = compilationOptions;

            DirectiveTriviaSyntax?currentOptionsDirective = null;
            var currentOptions = retainedOptions;

            for (var directive = root.GetFirstDirective(); directive is not null; directive = directive.GetNextDirective())
            {
                context.CancellationToken.ThrowIfCancellationRequested();

                if (directive.IsKind(SyntaxKind.NullableDirectiveTrivia, out NullableDirectiveTriviaSyntax? nullableDirectiveTrivia))
                {
                    // Once we reach a new directive, check to see if we can remove the previous directive
                    var removedCurrent = false;
                    if (IsReducing(retainedOptions, currentOptions))
                    {
                        // We can't have found a reducing directive and not know which directive it was
                        Contract.ThrowIfNull(currentOptionsDirective);

                        if (possibleNullableImpactIntervalTree is null ||
                            !possibleNullableImpactIntervalTree.HasIntervalThatOverlapsWith(currentOptionsDirective.Span.End, nullableDirectiveTrivia.SpanStart - currentOptionsDirective.Span.End))
                        {
                            diagnostics.Add(Diagnostic.Create(Descriptor, currentOptionsDirective.GetLocation()));
                        }
                    }

                    if (!removedCurrent)
                    {
                        previousRetainedDirective = currentOptionsDirective;
                        retainedOptions           = currentOptions;
                    }

                    currentOptionsDirective = nullableDirectiveTrivia;
                    currentOptions          = CSharpRemoveRedundantNullableDirectiveDiagnosticAnalyzer.GetNullableContextOptions(compilationOptions, currentOptions, nullableDirectiveTrivia);
                }
                else if (directive.IsKind(SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElifDirectiveTrivia, SyntaxKind.ElseDirectiveTrivia))
                {
                    possibleNullableImpactIntervalTree ??= new SimpleIntervalTree <TextSpan, TextSpanIntervalIntrospector>(new TextSpanIntervalIntrospector(), values: null);
                    possibleNullableImpactIntervalTree.AddIntervalInPlace(directive.Span);
                }
            }

            // Once we reach the end of the file, check to see if we can remove the last directive
            if (IsReducing(retainedOptions, currentOptions))
            {
                // We can't have found a reducing directive and not know which directive it was
                Contract.ThrowIfNull(currentOptionsDirective);

                if (possibleNullableImpactIntervalTree is null ||
                    !possibleNullableImpactIntervalTree.HasIntervalThatOverlapsWith(currentOptionsDirective.Span.End, root.Span.End - currentOptionsDirective.Span.End))
                {
                    diagnostics.Add(Diagnostic.Create(Descriptor, currentOptionsDirective.GetLocation()));
                }
            }

            return(diagnostics.ToImmutableAndClear());
        }
Exemple #2
0
 private static ImmutableArray <TextSpan> AnalyzeCodeBlock(CodeBlockAnalysisContext context, int positionOfFirstReducingNullableDirective)
 {
     using var simplifier = new NullableImpactingSpanWalker(context.SemanticModel, positionOfFirstReducingNullableDirective, ignoredSpans: null, context.CancellationToken);
     simplifier.Visit(context.CodeBlock);
     return(simplifier.Spans);
 }