Esempio n. 1
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);

                this.VariableDeclaration = (TVariableDeclarationSyntax)variables;
                if (!service.IsValidVariableDeclaration(this.VariableDeclaration))
                {
                    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.GetVariableDeclarationSymbolNode(this.VariableDeclaration), 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);
                }

                this.IndexOfDeclarationStatementInInnermostBlock   = this.InnermostBlockStatements.IndexOf(this.DeclarationStatement);
                this.IndexOfFirstStatementAffectedInInnermostBlock = this.InnermostBlockStatements.IndexOf(this.FirstStatementAffectedInInnermostBlock);
                if (this.IndexOfDeclarationStatementInInnermostBlock >= 0 &&
                    this.IndexOfDeclarationStatementInInnermostBlock < this.IndexOfFirstStatementAffectedInInnermostBlock)
                {
                    // Don't want to move a decl with initializer 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.

                    // Note that if the variable declaration has no initializer, then we still want to offer
                    // the move as the closest reference will be an assignment to the variable
                    // and we should be able to merge the declaration and assignment into a single
                    // statement.
                    // So, we also check if the variable declaration has an initializer below.

                    if (syntaxFacts.GetInitializerOfVariableDeclaration(this.VariableDeclaration) != null &&
                        InDeclarationStatementGroup(this.IndexOfDeclarationStatementInInnermostBlock, this.IndexOfFirstStatementAffectedInInnermostBlock))
                    {
                        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);
            }