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); }
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()); }