|
// 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.
#nullable disable
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ExtractMethod;
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,
ExtractMethodOptions 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,
ExtractMethodOptions options,
bool selectionInExpression,
SemanticDocument document,
SyntaxAnnotation firstTokenAnnotation,
SyntaxAnnotation lastTokenAnnotation)
: base(status, originalSpan, finalSpan, options, selectionInExpression,
document, firstTokenAnnotation, lastTokenAnnotation)
{
}
protected override bool UnderAnonymousOrLocalMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken)
{
var current = token.Parent;
for (; current != null; current = current.Parent)
{
if (current is MemberDeclarationSyntax or
SimpleLambdaExpressionSyntax or
ParenthesizedLambdaExpressionSyntax or
AnonymousMethodExpressionSyntax or
LocalFunctionStatementSyntax)
{
break;
}
}
if (current is null or 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()
=> GetFirstStatement<StatementSyntax>();
public StatementSyntax GetLastStatement()
=> GetLastStatement<StatementSyntax>();
public StatementSyntax GetFirstStatementUnderContainer()
{
Contract.ThrowIfTrue(SelectionInExpression);
var firstToken = GetFirstTokenInSelection();
var statement = firstToken.Parent.GetStatementUnderContainer();
Contract.ThrowIfNull(statement);
return statement;
}
public StatementSyntax GetLastStatementUnderContainer()
{
Contract.ThrowIfTrue(SelectionInExpression);
var lastToken = GetLastTokenInSelection();
var statement = lastToken.Parent.GetStatementUnderContainer();
Contract.ThrowIfNull(statement);
var firstStatementUnderContainer = GetFirstStatementUnderContainer();
Contract.ThrowIfFalse(CSharpSyntaxFacts.Instance.AreStatementsInSameContainer(statement, firstStatementUnderContainer));
return statement;
}
public SyntaxNode GetInnermostStatementContainer()
{
Contract.ThrowIfFalse(SelectionInExpression);
var containingScope = GetContainingScope();
var statements = containingScope.GetAncestorsOrThis<StatementSyntax>();
StatementSyntax last = null;
foreach (var statement in statements)
{
if (statement.IsStatementContainerNode())
{
return statement;
}
last = statement;
}
// expression bodied member case
var expressionBodiedMember = GetContainingScopeOf<ArrowExpressionClauseSyntax>();
if (expressionBodiedMember != null)
{
// the class/struct declaration is the innermost statement container, since the
// member does not have a block body
return GetContainingScopeOf<TypeDeclarationSyntax>();
}
// constructor initializer case
var constructorInitializer = GetContainingScopeOf<ConstructorInitializerSyntax>();
if (constructorInitializer != null)
{
return constructorInitializer.Parent;
}
// field initializer case
var field = 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 = 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()
=> UnderCheckedContext<CheckedExpressionSyntax>();
public SyntaxKind UnderCheckedStatementContext()
=> UnderCheckedContext<CheckedStatementSyntax>();
private SyntaxKind UnderCheckedContext<T>() where T : SyntaxNode
{
var token = GetFirstTokenInSelection();
var contextNode = token.Parent.GetAncestor<T>();
if (contextNode == null)
{
return SyntaxKind.None;
}
return contextNode.Kind();
}
}
}
|