protected override Task FixAllAsync(
            Document document,
            ImmutableArray <Diagnostic> diagnostics,
            SyntaxEditor editor,
            CancellationToken cancellationToken)
        {
            var syntaxRoot = editor.OriginalRoot;

            foreach (var diagnostic in diagnostics)
            {
                var firstUnreachableStatementLocation = diagnostic.AdditionalLocations.Single();
                var firstUnreachableStatement         = (StatementSyntax)firstUnreachableStatementLocation.FindNode(cancellationToken);

                editor.RemoveNode(firstUnreachableStatement, SyntaxRemoveOptions.KeepUnbalancedDirectives);

                var sections = RemoveUnreachableCodeHelpers.GetSubsequentUnreachableSections(firstUnreachableStatement);
                foreach (var section in sections)
                {
                    foreach (var statement in section)
                    {
                        editor.RemoveNode(statement, SyntaxRemoveOptions.KeepUnbalancedDirectives);
                    }
                }
            }

            return(Task.CompletedTask);
        }
Beispiel #2
0
        private void ProcessUnreachableDiagnostic(
            SemanticModelAnalysisContext context, SyntaxNode root, TextSpan sourceSpan, bool fadeOutCode)
        {
            var node = root.FindNode(sourceSpan);

            // Note: this approach works as the language only supports the concept of
            // unreachable statements.  If we ever get unreachable subexpressions, then
            // we'll need to revise this code accordingly.
            var firstUnreachableStatement = node.FirstAncestorOrSelf <StatementSyntax>();

            if (firstUnreachableStatement == null ||
                firstUnreachableStatement.SpanStart != sourceSpan.Start)
            {
                return;
            }

            // At a high level, we can think about us wanting to fade out a "section" of unreachable
            // statements.  However, the compiler only reports the first statement in that "section".
            // We want to figure out what other statements are in that section and fade them all out
            // along with the first statement.  This is made somewhat tricky due to the fact that
            // subsequent sibling statements possibly being reachable due to explicit gotos+labels.
            //
            // On top of this, an unreachable section might not be contiguous.  This is possible
            // when there is unreachable code that contains a local function declaration in-situ.
            // This is legal, and the local function declaration may be called from other reachable code.
            //
            // As such, it's not possible to just get first unreachable statement, and the last, and
            // then report that whole region as unreachable.  Instead, when we are told about an
            // unreachable statement, we simply determine which other statements are also unreachable
            // and bucket them into contiguous chunks.
            //
            // We then fade each of these contiguous chunks, while also having each diagnostic we
            // report point back to the first unreachable statement so that we can easily determine
            // what to remove if the user fixes the issue.  (The fix itself has to go recompute this
            // as the total set of statements to remove may be larger than the actual faded code
            // that that diagnostic corresponds to).

            // Get the location of this first unreachable statement.  It will be given to all
            // the diagnostics we create off of this single compiler diagnostic so that we always
            // know how to find it regardless of which of our diagnostics the user invokes the
            // fix off of.
            var firstStatementLocation = root.SyntaxTree.GetLocation(firstUnreachableStatement.FullSpan);

            if (!firstUnreachableStatement.IsParentKind(SyntaxKind.Block) &&
                !firstUnreachableStatement.IsParentKind(SyntaxKind.SwitchSection))
            {
                // Can't actually remove this statement (it's an embedded statement in something
                // like an 'if-statement').  Just fade the code out, but don't offer to remove it.
                if (fadeOutCode)
                {
                    context.ReportDiagnostic(
                        Diagnostic.Create(UnnecessaryWithoutSuggestionDescriptor, firstStatementLocation));
                }
                return;
            }

            // 'additionalLocations' is how we always pass along the locaiton of the first unreachable
            // statement in this group.
            var additionalLocations = SpecializedCollections.SingletonEnumerable(firstStatementLocation);

            var descriptor = fadeOutCode ? UnnecessaryWithSuggestionDescriptor : Descriptor;

            context.ReportDiagnostic(
                Diagnostic.Create(descriptor, firstStatementLocation, additionalLocations));

            var sections = RemoveUnreachableCodeHelpers.GetSubsequentUnreachableSections(firstUnreachableStatement);

            foreach (var section in sections)
            {
                var span     = TextSpan.FromBounds(section[0].FullSpan.Start, section.Last().FullSpan.End);
                var location = root.SyntaxTree.GetLocation(span);

                // Mark subsequent sections as being 'cascaded'.  We don't need to actually process them
                // when doing a fix-all as they'll be scooped up when we process the fix for the first
                // section.
                context.ReportDiagnostic(
                    Diagnostic.Create(descriptor, location, additionalLocations, s_subsequentSectionProperties));
            }
        }