private DirectiveTriviaSyntax?GetPreviousPossiblyRelatedDirective()
        {
            DirectiveTriviaSyntax?d = this;

            while (d != null)
            {
                d = d.GetPreviousDirective();
                if (d != null)
                {
                    // skip matched sets
                    switch (d.Kind())
                    {
                    case SyntaxKind.EndIfDirectiveTrivia:
                        while (d != null && d.Kind() != SyntaxKind.IfDirectiveTrivia)
                        {
                            d = d.GetPreviousRelatedDirective();
                        }

                        continue;

                    case SyntaxKind.EndRegionDirectiveTrivia:
                        while (d != null && d.Kind() != SyntaxKind.RegionDirectiveTrivia)
                        {
                            d = d.GetPreviousRelatedDirective();
                        }

                        continue;
                    }
                }

                return(d);
            }

            return(null);
        }
        private DirectiveTriviaSyntax?GetPreviousRelatedDirective()
        {
            DirectiveTriviaSyntax?d = this;

            switch (d.Kind())
            {
            case SyntaxKind.EndIfDirectiveTrivia:
                while (d != null)
                {
                    switch (d.Kind())
                    {
                    case SyntaxKind.IfDirectiveTrivia:
                    case SyntaxKind.ElifDirectiveTrivia:
                    case SyntaxKind.ElseDirectiveTrivia:
                        return(d);
                    }

                    d = d.GetPreviousPossiblyRelatedDirective();
                }

                break;

            case SyntaxKind.ElifDirectiveTrivia:
                d = d.GetPreviousPossiblyRelatedDirective();

                while (d != null)
                {
                    switch (d.Kind())
                    {
                    case SyntaxKind.IfDirectiveTrivia:
                    case SyntaxKind.ElifDirectiveTrivia:
                        return(d);
                    }

                    d = d.GetPreviousPossiblyRelatedDirective();
                }

                break;

            case SyntaxKind.ElseDirectiveTrivia:
                while (d != null)
                {
                    switch (d.Kind())
                    {
                    case SyntaxKind.IfDirectiveTrivia:
                    case SyntaxKind.ElifDirectiveTrivia:
                        return(d);
                    }

                    d = d.GetPreviousPossiblyRelatedDirective();
                }

                break;

            case SyntaxKind.EndRegionDirectiveTrivia:
                while (d != null)
                {
                    if (d.Kind() == SyntaxKind.RegionDirectiveTrivia)
                    {
                        return(d);
                    }

                    d = d.GetPreviousPossiblyRelatedDirective();
                }

                break;
            }

            return(null);
        }
Exemplo n.º 3
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());
        }