public StatementRange(TStatementSyntax firstStatement, TStatementSyntax lastStatement)
 {
     Debug.Assert(firstStatement != null);
     Debug.Assert(lastStatement != null);
     Debug.Assert(firstStatement.Parent != null);
     Debug.Assert(firstStatement.Parent == lastStatement.Parent);
     Debug.Assert(firstStatement.SpanStart <= lastStatement.SpanStart);
     FirstStatement = firstStatement;
     LastStatement  = lastStatement;
 }
Пример #2
0
            private async Task <bool> TryInitializeAsync(
                TService service,
                Document document,
                TLocalDeclarationStatementSyntax node,
                CancellationToken cancellationToken)
            {
                var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>();

                this.DeclarationStatement = node;

                var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(this.DeclarationStatement);

                if (variables.Count != 1)
                {
                    return(false);
                }

                this.VariableDeclarator = (TVariableDeclaratorSyntax)variables[0];
                if (!service.IsValidVariableDeclarator(this.VariableDeclarator))
                {
                    return(false);
                }

                this.OutermostBlock = this.DeclarationStatement.Parent;
                if (!syntaxFacts.IsExecutableBlock(this.OutermostBlock))
                {
                    return(false);
                }

                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                this.LocalSymbol = (ILocalSymbol)semanticModel.GetDeclaredSymbol(
                    service.GetVariableDeclaratorSymbolNode(this.VariableDeclarator), cancellationToken);
                if (this.LocalSymbol == null)
                {
                    // This can happen in broken code, for example: "{ object x; object }"
                    return(false);
                }

                var findReferencesResult = await SymbolFinder.FindReferencesAsync(this.LocalSymbol, document.Project.Solution, cancellationToken).ConfigureAwait(false);

                var findReferencesList = findReferencesResult.ToList();

                if (findReferencesList.Count != 1)
                {
                    return(false);
                }

                var references = findReferencesList[0].Locations.ToList();

                if (references.Count == 0)
                {
                    return(false);
                }

                var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                var referencingStatements =
                    (from r in references
                     let token = syntaxRoot.FindToken(r.Location.SourceSpan.Start)
                                 let statement = token.GetAncestor <TStatementSyntax>()
                                                 where statement != null
                                                 select statement).ToSet();

                if (referencingStatements.Count == 0)
                {
                    return(false);
                }

                this.InnermostBlock = syntaxFacts.FindInnermostCommonExecutableBlock(referencingStatements);
                if (this.InnermostBlock == null)
                {
                    return(false);
                }

                this.InnermostBlockStatements = syntaxFacts.GetExecutableBlockStatements(this.InnermostBlock);
                this.OutermostBlockStatements = syntaxFacts.GetExecutableBlockStatements(this.OutermostBlock);

                var allAffectedStatements = new HashSet <TStatementSyntax>(referencingStatements.SelectMany(
                                                                               expr => expr.GetAncestorsOrThis <TStatementSyntax>()));

                this.FirstStatementAffectedInInnermostBlock = this.InnermostBlockStatements.FirstOrDefault(allAffectedStatements.Contains);

                if (this.FirstStatementAffectedInInnermostBlock == null)
                {
                    return(false);
                }

                if (this.FirstStatementAffectedInInnermostBlock == this.DeclarationStatement)
                {
                    return(false);
                }

                var originalIndexInBlock = this.InnermostBlockStatements.IndexOf(this.DeclarationStatement);
                var firstStatementIndexAffectedInBlock = this.InnermostBlockStatements.IndexOf(this.FirstStatementAffectedInInnermostBlock);

                if (originalIndexInBlock >= 0 &&
                    originalIndexInBlock < firstStatementIndexAffectedInBlock)
                {
                    // Don't want to move a decl past other decls in order to move it to the first
                    // affected statement.  If we do we can end up in the following situation:
#if false
                    int x = 0;
                    int y = 0;
                    Console.WriteLine(x + y);
#endif
                    // Each of these declarations will want to 'move' down to the WriteLine
                    // statement and we don't want to keep offering the refactoring.  Note: this
                    // solution is overly aggressive.  Technically if 'y' weren't referenced in
                    // Console.Writeline, then it might be a good idea to move the 'x'.  But this
                    // gives good enough behavior most of the time.
                    if (InDeclarationStatementGroup(originalIndexInBlock, firstStatementIndexAffectedInBlock))
                    {
                        return(false);
                    }
                }

                var previousToken = FirstStatementAffectedInInnermostBlock.GetFirstToken().GetPreviousToken();
                var affectedSpan  = TextSpan.FromBounds(previousToken.SpanStart, FirstStatementAffectedInInnermostBlock.Span.End);
                if (semanticModel.SyntaxTree.OverlapsHiddenPosition(affectedSpan, cancellationToken))
                {
                    return(false);
                }

                return(true);
            }