CSharpSelectionResult.cs 8.0 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
4 5 6

using System;
using System.Linq;
7
using System.Linq.Expressions;
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod
{
    internal abstract partial class CSharpSelectionResult : SelectionResult
    {
        public static async Task<CSharpSelectionResult> CreateAsync(
            OperationStatus status,
            TextSpan originalSpan,
            TextSpan finalSpan,
            OptionSet options,
            bool selectionInExpression,
            SemanticDocument document,
            SyntaxToken firstToken,
            SyntaxToken lastToken,
            CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(status);
            Contract.ThrowIfNull(document);

            var firstTokenAnnotation = new SyntaxAnnotation();
            var lastTokenAnnotation = new SyntaxAnnotation();

            var root = await document.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var newDocument = await SemanticDocument.CreateAsync(document.Document.WithSyntaxRoot(root.AddAnnotations(
                    new[]
                    {
                        Tuple.Create<SyntaxToken, SyntaxAnnotation>(firstToken, firstTokenAnnotation),
                        Tuple.Create<SyntaxToken, SyntaxAnnotation>(lastToken, lastTokenAnnotation)
                    })), cancellationToken).ConfigureAwait(false);

            if (selectionInExpression)
            {
                return new ExpressionResult(
                    status, originalSpan, finalSpan, options, selectionInExpression,
                    newDocument, firstTokenAnnotation, lastTokenAnnotation);
            }
            else
            {
                return new StatementResult(
                    status, originalSpan, finalSpan, options, selectionInExpression,
                    newDocument, firstTokenAnnotation, lastTokenAnnotation);
            }
        }

        protected CSharpSelectionResult(
            OperationStatus status,
            TextSpan originalSpan,
            TextSpan finalSpan,
            OptionSet options,
            bool selectionInExpression,
            SemanticDocument document,
            SyntaxAnnotation firstTokenAnnotation,
71 72 73
            SyntaxAnnotation lastTokenAnnotation)
            : base(status, originalSpan, finalSpan, options, selectionInExpression,
                   document, firstTokenAnnotation, lastTokenAnnotation)
74 75 76
        {
        }

77
        protected override bool UnderAnonymousOrLocalMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken)
78 79 80 81 82 83 84
        {
            var current = token.Parent;
            for (; current != null; current = current.Parent)
            {
                if (current is MemberDeclarationSyntax ||
                    current is SimpleLambdaExpressionSyntax ||
                    current is ParenthesizedLambdaExpressionSyntax ||
85 86
                    current is AnonymousMethodExpressionSyntax ||
                    current is LocalFunctionStatementSyntax)
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
                {
                    break;
                }
            }

            if (current == null || current is MemberDeclarationSyntax)
            {
                return false;
            }

            // make sure the selection contains the lambda
            return firstToken.SpanStart <= current.GetFirstToken().SpanStart &&
                   current.GetLastToken().Span.End <= lastToken.Span.End;
        }

        public StatementSyntax GetFirstStatement()
        {
            return GetFirstStatement<StatementSyntax>();
        }

        public StatementSyntax GetLastStatement()
        {
            return GetLastStatement<StatementSyntax>();
        }

        public StatementSyntax GetFirstStatementUnderContainer()
        {
            Contract.ThrowIfTrue(this.SelectionInExpression);

            var firstToken = this.GetFirstTokenInSelection();
            var statement = firstToken.Parent.GetStatementUnderContainer();
            Contract.ThrowIfNull(statement);

            return statement;
        }

        public StatementSyntax GetLastStatementUnderContainer()
        {
            Contract.ThrowIfTrue(this.SelectionInExpression);

            var lastToken = this.GetLastTokenInSelection();
            var statement = lastToken.Parent.GetStatementUnderContainer();

            Contract.ThrowIfNull(statement);
            var firstStatementUnderContainer = this.GetFirstStatementUnderContainer();
            Contract.ThrowIfFalse(statement.Parent == firstStatementUnderContainer.Parent);

            return statement;
        }

        public SyntaxNode GetInnermostStatementContainer()
        {
            Contract.ThrowIfFalse(this.SelectionInExpression);
            var containingScope = this.GetContainingScope();
            var statements = containingScope.GetAncestorsOrThis<StatementSyntax>();
            StatementSyntax last = null;

            foreach (var statement in statements)
            {
                if (statement.IsStatementContainerNode())
                {
                    return statement;
                }

                last = statement;
            }

154 155 156 157
            // expression bodied member case
            var expressionBodiedMember = this.GetContainingScopeOf<ArrowExpressionClauseSyntax>();
            if (expressionBodiedMember != null)
            {
158 159 160
                // the class/struct declaration is the innermost statement container, since the 
                // member does not have a block body
                return this.GetContainingScopeOf<TypeDeclarationSyntax>();
161 162
            }

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
            // constructor initializer case
            var constructorInitializer = this.GetContainingScopeOf<ConstructorInitializerSyntax>();
            if (constructorInitializer != null)
            {
                return constructorInitializer.Parent;
            }

            // field initializer case
            var field = this.GetContainingScopeOf<FieldDeclarationSyntax>();
            if (field != null)
            {
                return field.Parent;
            }

            Contract.ThrowIfFalse(last.IsParentKind(SyntaxKind.GlobalStatement));
            Contract.ThrowIfFalse(last.Parent.IsParentKind(SyntaxKind.CompilationUnit));
            return last.Parent.Parent;
        }

        public bool ShouldPutUnsafeModifier()
        {
            var token = this.GetFirstTokenInSelection();
            var ancestors = token.GetAncestors<SyntaxNode>();

            // if enclosing type contains unsafe keyword, we don't need to put it again
            if (ancestors.Where(a => SyntaxFacts.IsTypeDeclaration(a.Kind()))
                         .Cast<MemberDeclarationSyntax>()
                         .Any(m => m.GetModifiers().Any(SyntaxKind.UnsafeKeyword)))
            {
                return false;
            }

            return token.Parent.IsUnsafeContext();
        }

        public SyntaxKind UnderCheckedExpressionContext()
        {
            return UnderCheckedContext<CheckedExpressionSyntax>();
        }

        public SyntaxKind UnderCheckedStatementContext()
        {
            return UnderCheckedContext<CheckedStatementSyntax>();
        }

        private SyntaxKind UnderCheckedContext<T>() where T : SyntaxNode
        {
            var token = this.GetFirstTokenInSelection();
            var contextNode = token.Parent.GetAncestor<T>();
            if (contextNode == null)
            {
                return SyntaxKind.None;
            }

            return contextNode.Kind();
        }
    }
}