CSharpSelectionResult.cs 7.9 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
                {
                    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()
        {
114
            Contract.ThrowIfTrue(SelectionInExpression);
115

116
            var firstToken = GetFirstTokenInSelection();
117 118 119 120 121 122 123 124
            var statement = firstToken.Parent.GetStatementUnderContainer();
            Contract.ThrowIfNull(statement);

            return statement;
        }

        public StatementSyntax GetLastStatementUnderContainer()
        {
125
            Contract.ThrowIfTrue(SelectionInExpression);
126

127
            var lastToken = GetLastTokenInSelection();
128 129 130
            var statement = lastToken.Parent.GetStatementUnderContainer();

            Contract.ThrowIfNull(statement);
131
            var firstStatementUnderContainer = GetFirstStatementUnderContainer();
132 133 134 135 136 137 138
            Contract.ThrowIfFalse(statement.Parent == firstStatementUnderContainer.Parent);

            return statement;
        }

        public SyntaxNode GetInnermostStatementContainer()
        {
139 140
            Contract.ThrowIfFalse(SelectionInExpression);
            var containingScope = GetContainingScope();
141 142 143 144 145 146 147 148 149 150 151 152 153
            var statements = containingScope.GetAncestorsOrThis<StatementSyntax>();
            StatementSyntax last = null;

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

                last = statement;
            }

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

163
            // constructor initializer case
164
            var constructorInitializer = GetContainingScopeOf<ConstructorInitializerSyntax>();
165 166 167 168 169 170
            if (constructorInitializer != null)
            {
                return constructorInitializer.Parent;
            }

            // field initializer case
171
            var field = GetContainingScopeOf<FieldDeclarationSyntax>();
172 173 174 175 176 177 178 179 180 181 182 183
            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()
        {
184
            var token = GetFirstTokenInSelection();
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
            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
        {
210
            var token = GetFirstTokenInSelection();
211 212 213 214 215 216 217 218 219 220
            var contextNode = token.Parent.GetAncestor<T>();
            if (contextNode == null)
            {
                return SyntaxKind.None;
            }

            return contextNode.Kind();
        }
    }
}