未验证 提交 89497254 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #46999 from CyrusNajmabadi/extractMethodFix

Fix extract method crash with conditional access expressions.
......@@ -167,7 +167,8 @@ protected bool ExpressionContainsValuePatternOrReferencesInitializedSymbol(Synta
{
foreach (var subExpression in expression.DescendantNodesAndSelf().OfType<TExpressionSyntax>())
{
if (!_syntaxFacts.IsNameOfMemberAccessExpression(subExpression))
if (!_syntaxFacts.IsNameOfSimpleMemberAccessExpression(subExpression) &&
!_syntaxFacts.IsNameOfMemberBindingExpression(subExpression))
{
if (ValuePatternMatches(subExpression))
{
......
......@@ -3969,6 +3969,237 @@ private static Action<Context> NewMethod()
return context => context.ToString();
}
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess1()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?.[|ToString|]();
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static string NewMethod(List<int> b)
{
return b?.ToString();
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess2()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?.[|ToString|]().Length;
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static int? NewMethod(List<int> b)
{
return b?.ToString().Length;
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess3()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?.Count.[|ToString|]();
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static string NewMethod(List<int> b)
{
return b?.Count.ToString();
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess4()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?.[|Count|].ToString();
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static string NewMethod(List<int> b)
{
return b?.Count.ToString();
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess5()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?.[|ToString|]()?.ToString();
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static string NewMethod(List<int> b)
{
return b?.ToString()?.ToString();
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess6()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?.ToString()?.[|ToString|]();
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static string NewMethod(List<int> b)
{
return b?.ToString()?.ToString();
}
}");
}
[Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")]
public async Task TestConditionalAccess7()
{
await TestInRegularAndScript1Async(@"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = b?[|[0]|];
}
}", @"
using System;
using System.Collections.Generic;
class C
{
void Test()
{
List<int> b = null;
b?.Clear();
_ = {|Rename:NewMethod|}(b);
}
private static int? NewMethod(List<int> b)
{
return b?[0];
}
}");
}
}
......
......@@ -8074,7 +8074,7 @@ public void goo()
[WorkItem(1109319, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1109319")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithinChainOfConditionalAccesses()
public async Task WithinChainOfConditionalAccesses1()
{
var markup = @"
class Program
......@@ -8093,6 +8093,48 @@ class D { public int e; }";
await VerifyItemExistsAsync(markup, "b");
}
[WorkItem(1109319, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1109319")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithinChainOfConditionalAccesses2()
{
var markup = @"
class Program
{
static void Main(string[] args)
{
A a;
var x = a?.b?.$$c?.d.e;
}
}
class A { public B b; }
class B { public C c; }
class C { public D d; }
class D { public int e; }";
await VerifyItemExistsAsync(markup, "c");
}
[WorkItem(1109319, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1109319")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithinChainOfConditionalAccesses3()
{
var markup = @"
class Program
{
static void Main(string[] args)
{
A a;
var x = a?.b?.c?.$$d.e;
}
}
class A { public B b; }
class B { public C c; }
class C { public D d; }
class D { public int e; }";
await VerifyItemExistsAsync(markup, "d");
}
[WorkItem(843466, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/843466")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NestedAttributeAccessibleOnSelf()
......
......@@ -834,5 +834,259 @@ End Class",
End Function
End Class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess1() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?.[|ToString|]()
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As String
Return b?.ToString()
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess2() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?.[|ToString|]().Length
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As Integer?
Return b?.ToString().Length
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess3() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?.Count.[|ToString|]()
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As String
Return b?.Count.ToString()
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess4() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?.[|Count|].ToString()
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As String
Return b?.Count.ToString()
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess5() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?.[|ToString|]()?.ToString()
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As String
Return b?.ToString()?.ToString()
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess6() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?.ToString()?.[|ToString|]()
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As String
Return b?.ToString()?.ToString()
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess7() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = b?[|(0)|]
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new List(of integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As List(Of Integer)) As Integer?
Return b?(0)
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess8() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new Dictionary(of string, integer)
dim x = b?![|a|]
end sub
end class", "
imports System
imports System.Collections.Generic
class C
sub Test()
dim b as new Dictionary(of string, integer)
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As Dictionary(Of String, Integer)) As Integer?
Return b?!a
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess9() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
imports System.Xml.Linq
class C
sub Test()
dim b as XElement = nothing
dim x = b?.[|<e>|]
end sub
end class", "
imports System
imports System.Collections.Generic
imports System.Xml.Linq
class C
sub Test()
dim b as XElement = nothing
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As XElement) As IEnumerable(Of XElement)
Return b?.<e>
End Function
end class")
End Function
<Fact, WorkItem(41895, "https://github.com/dotnet/roslyn/issues/41895")>
Public Async Function TestConditionalAccess10() As Task
Await TestInRegularAndScript1Async("
imports System
imports System.Collections.Generic
imports System.Xml.Linq
class C
sub Test()
dim b as XElement = nothing
dim x = b?.[|@e|]
end sub
end class", "
imports System
imports System.Collections.Generic
imports System.Xml.Linq
class C
sub Test()
dim b as XElement = nothing
dim x = {|Rename:GetX|}(b)
end sub
Private Shared Function GetX(b As XElement) As String
Return b?.@e
End Function
end class")
End Function
End Class
End Namespace
......@@ -129,7 +129,8 @@ protected override bool CanAddImport(SyntaxNode node, CancellationToken cancella
return false;
}
if (!syntaxFacts.IsNameOfMemberAccessExpression(node))
if (!syntaxFacts.IsNameOfSimpleMemberAccessExpression(node) &&
!syntaxFacts.IsNameOfMemberBindingExpression(node))
{
return false;
}
......
......@@ -159,7 +159,8 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
newNode = newNode.WithTriviaFrom(oldNode);
return true;
}
else if (syntaxFacts.IsNameOfMemberAccessExpression(nameRef))
else if (syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameRef) ||
syntaxFacts.IsNameOfMemberBindingExpression(nameRef))
{
RoslynDebug.Assert(nameRef.Parent is object);
oldNode = nameRef.Parent;
......
......@@ -51,18 +51,7 @@ internal static async Task<DebugDataTipInfo> GetInfoAsync(Document document, int
if (expression.IsRightSideOfDotOrArrow())
{
var curr = expression;
while (true)
{
var conditionalAccess = curr.GetParentConditionalAccessExpression();
if (conditionalAccess == null)
{
break;
}
curr = conditionalAccess;
}
var curr = expression.GetRootConditionalAccessExpression() ?? expression;
if (curr == expression)
{
// NB: Parent.Span, not Span as below.
......
......@@ -161,7 +161,8 @@ where m.IsAccessibleWithin(enclosingType)
var newNameNode = matchingMember.Name.ToIdentifierName();
var newExpr = (ExpressionSyntax)newNameNode;
if (!syntaxFacts.IsNameOfMemberAccessExpression(nameNode))
if (!syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) &&
!syntaxFacts.IsNameOfMemberBindingExpression(nameNode))
{
newExpr = MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), newNameNode).WithAdditionalAnnotations(Simplifier.Annotation);
......
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -91,7 +92,7 @@ private static string GetMethodNameBasedOnExpression(string methodName, SyntaxNo
return methodName;
}
protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDefinitions()
protected override ImmutableArray<StatementSyntax> GetInitialStatementsForMethodDefinitions()
{
Contract.ThrowIfFalse(IsExtractMethodOnExpression(CSharpSelectionResult));
......@@ -113,13 +114,13 @@ protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDef
if (AnalyzerResult.HasReturnType)
{
return SpecializedCollections.SingletonEnumerable<StatementSyntax>(
return ImmutableArray.Create<StatementSyntax>(
SyntaxFactory.ReturnStatement(
WrapInCheckedExpressionIfNeeded(expression)));
}
else
{
return SpecializedCollections.SingletonEnumerable<StatementSyntax>(
return ImmutableArray.Create<StatementSyntax>(
SyntaxFactory.ExpressionStatement(
WrapInCheckedExpressionIfNeeded(expression)));
}
......@@ -188,12 +189,11 @@ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
=> GetFirstStatementOrInitializerSelectedAtCallSite();
protected override async Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(
SyntaxAnnotation callSiteAnnotation, CancellationToken cancellationToken)
protected override async Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(CancellationToken cancellationToken)
{
var enclosingStatement = GetFirstStatementOrInitializerSelectedAtCallSite();
var callSignature = CreateCallSignature().WithAdditionalAnnotations(callSiteAnnotation);
var invocation = callSignature.IsKind(SyntaxKind.AwaitExpression, out AwaitExpressionSyntax awaitExpr) ? awaitExpr.Expression : callSignature;
var callSignature = CreateCallSignature().WithAdditionalAnnotations(CallSiteAnnotation);
var sourceNode = CSharpSelectionResult.GetContainingScope();
Contract.ThrowIfTrue(
......@@ -220,28 +220,6 @@ protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
// however complexification of names is prepended, so the last annotation should be the original one.
sourceNode = updatedRoot.GetAnnotatedNodesAndTokens(sourceNodeAnnotation).Last().AsNode();
// we want to replace the old identifier with a invocation expression, but because of MakeExplicit we might have
// a member access now instead of the identifier. So more syntax fiddling is needed.
if (sourceNode.Parent.Kind() == SyntaxKind.SimpleMemberAccessExpression &&
((ExpressionSyntax)sourceNode).IsRightSideOfDot())
{
var explicitMemberAccess = (MemberAccessExpressionSyntax)sourceNode.Parent;
var replacementMemberAccess = explicitMemberAccess.CopyAnnotationsTo(
SyntaxFactory.MemberAccessExpression(
sourceNode.Parent.Kind(),
explicitMemberAccess.Expression,
(SimpleNameSyntax)((InvocationExpressionSyntax)invocation).Expression));
var newInvocation = SyntaxFactory.InvocationExpression(
replacementMemberAccess,
((InvocationExpressionSyntax)invocation).ArgumentList);
var newCallSignature = callSignature != invocation ?
callSignature.ReplaceNode(invocation, newInvocation) : invocation.CopyAnnotationsTo(newInvocation);
sourceNode = sourceNode.Parent;
callSignature = newCallSignature;
}
return newEnclosingStatement.ReplaceNode(sourceNode, callSignature);
}
}
......
......@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod
......@@ -47,13 +49,13 @@ public static bool IsExtractMethodOnMultipleStatements(SelectionResult code)
protected override SyntaxToken CreateMethodName() => GenerateMethodNameForStatementGenerators();
protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDefinitions()
protected override ImmutableArray<StatementSyntax> GetInitialStatementsForMethodDefinitions()
{
var firstSeen = false;
var firstStatementUnderContainer = CSharpSelectionResult.GetFirstStatementUnderContainer();
var lastStatementUnderContainer = CSharpSelectionResult.GetLastStatementUnderContainer();
var list = new List<StatementSyntax>();
using var _ = ArrayBuilder<StatementSyntax>.GetInstance(out var list);
foreach (var statement in GetStatementsFromContainer(firstStatementUnderContainer.Parent))
{
// reset first seen
......@@ -77,7 +79,7 @@ protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDef
}
}
return list;
return list.ToImmutable();
}
protected override SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationToken cancellationToken)
......@@ -113,11 +115,10 @@ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
=> CSharpSelectionResult.GetLastStatementUnderContainer();
protected override Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(
SyntaxAnnotation callSiteAnnotation, CancellationToken cancellationToken)
protected override Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(CancellationToken cancellationToken)
{
var statement = GetStatementContainingInvocationToExtractedMethodWorker();
return Task.FromResult<SyntaxNode>(statement.WithAdditionalAnnotations(callSiteAnnotation));
return Task.FromResult<SyntaxNode>(statement.WithAdditionalAnnotations(CallSiteAnnotation));
}
}
}
......
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Syntax;
......@@ -39,11 +40,11 @@ public static bool IsExtractMethodOnSingleStatement(SelectionResult code)
protected override SyntaxToken CreateMethodName() => GenerateMethodNameForStatementGenerators();
protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDefinitions()
protected override ImmutableArray<StatementSyntax> GetInitialStatementsForMethodDefinitions()
{
Contract.ThrowIfFalse(IsExtractMethodOnSingleStatement(CSharpSelectionResult));
return SpecializedCollections.SingletonEnumerable<StatementSyntax>(CSharpSelectionResult.GetFirstStatement());
return ImmutableArray.Create(CSharpSelectionResult.GetFirstStatement());
}
protected override SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationToken cancellationToken)
......@@ -70,11 +71,10 @@ protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
return CSharpSelectionResult.GetFirstStatement();
}
protected override Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(
SyntaxAnnotation callSiteAnnotation, CancellationToken cancellationToken)
protected override Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(CancellationToken cancellationToken)
{
var statement = GetStatementContainingInvocationToExtractedMethodWorker();
return Task.FromResult<SyntaxNode>(statement.WithAdditionalAnnotations(callSiteAnnotation));
return Task.FromResult<SyntaxNode>(statement.WithAdditionalAnnotations(CallSiteAnnotation));
}
}
}
......
......@@ -20,6 +20,7 @@
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
......@@ -143,7 +144,7 @@ protected override async Task<SyntaxNode> GenerateBodyForCallSiteContainerAsync(
return container.CopyAnnotationsTo(callSiteGenerator.Generate()).WithAdditionalAnnotations(Formatter.Annotation);
}
private async Task<IEnumerable<SyntaxNode>> CreateStatementsOrInitializerToInsertAtCallSiteAsync(CancellationToken cancellationToken)
private async Task<ImmutableArray<SyntaxNode>> CreateStatementsOrInitializerToInsertAtCallSiteAsync(CancellationToken cancellationToken)
{
var selectedNode = GetFirstStatementOrInitializerSelectedAtCallSite();
......@@ -153,8 +154,8 @@ private async Task<IEnumerable<SyntaxNode>> CreateStatementsOrInitializerToInser
IsExpressionBodiedMember(selectedNode) ||
IsExpressionBodiedAccessor(selectedNode))
{
var statement = await GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(CallSiteAnnotation, cancellationToken).ConfigureAwait(false);
return SpecializedCollections.SingletonEnumerable(statement);
var statement = await GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(cancellationToken).ConfigureAwait(false);
return ImmutableArray.Create(statement);
}
// regular case
......@@ -168,7 +169,7 @@ private async Task<IEnumerable<SyntaxNode>> CreateStatementsOrInitializerToInser
statements = await AddInvocationAtCallSiteAsync(statements, cancellationToken).ConfigureAwait(false);
statements = AddReturnIfUnreachable(statements);
return statements;
return statements.CastArray<SyntaxNode>();
}
private static bool IsExpressionBodiedMember(SyntaxNode node)
......@@ -279,7 +280,7 @@ private IEnumerable<StatementSyntax> WrapInCheckStatementIfNeeded(IEnumerable<St
return SpecializedCollections.SingletonEnumerable<StatementSyntax>(SyntaxFactory.CheckedStatement(kind, SyntaxFactory.Block(statements)));
}
private static IEnumerable<StatementSyntax> CleanupCode(IEnumerable<StatementSyntax> statements)
private static ImmutableArray<StatementSyntax> CleanupCode(ImmutableArray<StatementSyntax> statements)
{
statements = PostProcessor.RemoveRedundantBlock(statements);
statements = PostProcessor.RemoveDeclarationAssignmentPattern(statements);
......@@ -324,20 +325,22 @@ private static OperationStatus CheckActiveStatements(IEnumerable<StatementSyntax
return OperationStatus.NoActiveStatement;
}
private IEnumerable<StatementSyntax> MoveDeclarationOutFromMethodDefinition(
IEnumerable<StatementSyntax> statements, CancellationToken cancellationToken)
private ImmutableArray<StatementSyntax> MoveDeclarationOutFromMethodDefinition(
ImmutableArray<StatementSyntax> statements, CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<StatementSyntax>.GetInstance(out var result);
var variableToRemoveMap = CreateVariableDeclarationToRemoveMap(
AnalyzerResult.GetVariablesToMoveOutToCallSiteOrDelete(cancellationToken), cancellationToken);
statements = statements.Select(s => FixDeclarationExpressionsAndDeclarationPatterns(s, variableToRemoveMap));
statements = statements.SelectAsArray(s => FixDeclarationExpressionsAndDeclarationPatterns(s, variableToRemoveMap));
foreach (var statement in statements)
{
if (!(statement is LocalDeclarationStatementSyntax declarationStatement) || declarationStatement.Declaration.Variables.FullSpan.IsEmpty)
{
// if given statement is not decl statement.
yield return statement;
result.Add(statement);
continue;
}
......@@ -394,28 +397,27 @@ private static OperationStatus CheckActiveStatements(IEnumerable<StatementSyntax
// trivia to the statement
// TODO : think about a way to trivia attached to next token
yield return SyntaxFactory.EmptyStatement(SyntaxFactory.Token(SyntaxFactory.TriviaList(triviaList), SyntaxKind.SemicolonToken, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker)));
result.Add(SyntaxFactory.EmptyStatement(SyntaxFactory.Token(SyntaxFactory.TriviaList(triviaList), SyntaxKind.SemicolonToken, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker))));
triviaList.Clear();
}
// return survived var decls
if (list.Count > 0)
{
yield return SyntaxFactory.LocalDeclarationStatement(
result.Add(SyntaxFactory.LocalDeclarationStatement(
declarationStatement.Modifiers,
SyntaxFactory.VariableDeclaration(
declarationStatement.Declaration.Type,
SyntaxFactory.SeparatedList(list)),
declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList)));
triviaList.Clear();
}
// return any expression statement if there was any
foreach (var expressionStatement in expressionStatements)
{
yield return expressionStatement;
}
result.AddRange(expressionStatements);
}
return result.ToImmutable();
}
/// <summary>
......@@ -512,8 +514,8 @@ private static SyntaxToken ApplyTriviaFromDeclarationToAssignmentIdentifier(Loca
return identifier;
}
private IEnumerable<StatementSyntax> SplitOrMoveDeclarationIntoMethodDefinition(
IEnumerable<StatementSyntax> statements,
private ImmutableArray<StatementSyntax> SplitOrMoveDeclarationIntoMethodDefinition(
ImmutableArray<StatementSyntax> statements,
CancellationToken cancellationToken)
{
var semanticModel = SemanticDocument.SemanticModel;
......
......@@ -3,8 +3,10 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
......@@ -25,7 +27,7 @@ public PostProcessor(SemanticModel semanticModel, int contextPosition)
_contextPosition = contextPosition;
}
public static IEnumerable<StatementSyntax> RemoveRedundantBlock(IEnumerable<StatementSyntax> statements)
public static ImmutableArray<StatementSyntax> RemoveRedundantBlock(ImmutableArray<StatementSyntax> statements)
{
// it must have only one statement
if (statements.Count() != 1)
......@@ -43,15 +45,16 @@ public static IEnumerable<StatementSyntax> RemoveRedundantBlock(IEnumerable<Stat
return RemoveRedundantBlock(block);
}
private static IEnumerable<StatementSyntax> RemoveRedundantBlock(BlockSyntax block)
private static ImmutableArray<StatementSyntax> RemoveRedundantBlock(BlockSyntax block)
{
// if block doesn't have any statement
if (block.Statements.Count == 0)
{
// either remove the block if it doesn't have any trivia, or return as it is if
// there are trivia attached to block
return (block.OpenBraceToken.GetAllTrivia().IsEmpty() && block.CloseBraceToken.GetAllTrivia().IsEmpty()) ?
SpecializedCollections.EmptyEnumerable<StatementSyntax>() : SpecializedCollections.SingletonEnumerable<StatementSyntax>(block);
return (block.OpenBraceToken.GetAllTrivia().IsEmpty() && block.CloseBraceToken.GetAllTrivia().IsEmpty())
? ImmutableArray<StatementSyntax>.Empty
: ImmutableArray.Create<StatementSyntax>(block);
}
// okay transfer asset attached to block to statements
......@@ -67,10 +70,10 @@ private static IEnumerable<StatementSyntax> RemoveRedundantBlock(BlockSyntax blo
block = block.ReplaceTokens(new[] { firstToken, lastToken }, (o, c) => (o == firstToken) ? firstTokenWithAsset : lastTokenWithAsset);
// return only statements without the wrapping block
return block.Statements;
return ImmutableArray.CreateRange(block.Statements);
}
public IEnumerable<StatementSyntax> MergeDeclarationStatements(IEnumerable<StatementSyntax> statements)
public ImmutableArray<StatementSyntax> MergeDeclarationStatements(ImmutableArray<StatementSyntax> statements)
{
if (statements.FirstOrDefault() == null)
{
......@@ -80,19 +83,17 @@ public IEnumerable<StatementSyntax> MergeDeclarationStatements(IEnumerable<State
return MergeDeclarationStatementsWorker(statements);
}
private IEnumerable<StatementSyntax> MergeDeclarationStatementsWorker(IEnumerable<StatementSyntax> statements)
private ImmutableArray<StatementSyntax> MergeDeclarationStatementsWorker(ImmutableArray<StatementSyntax> statements)
{
using var _ = ArrayBuilder<StatementSyntax>.GetInstance(out var result);
var map = new Dictionary<ITypeSymbol, List<LocalDeclarationStatementSyntax>>();
foreach (var statement in statements)
{
if (!IsDeclarationMergable(statement))
{
foreach (var declStatement in GetMergedDeclarationStatements(map))
{
yield return declStatement;
}
yield return statement;
result.AddRange(GetMergedDeclarationStatements(map));
result.Add(statement);
continue;
}
......@@ -100,15 +101,10 @@ private IEnumerable<StatementSyntax> MergeDeclarationStatementsWorker(IEnumerabl
}
// merge leftover
if (map.Count <= 0)
{
yield break;
}
if (map.Count > 0)
result.AddRange(GetMergedDeclarationStatements(map));
foreach (var declStatement in GetMergedDeclarationStatements(map))
{
yield return declStatement;
}
return result.ToImmutable();
}
private void AppendDeclarationStatementToMap(
......@@ -223,7 +219,7 @@ private static bool ContainsOnlyWhitespaceTrivia(StatementSyntax statement)
return true;
}
public static IEnumerable<StatementSyntax> RemoveInitializedDeclarationAndReturnPattern(IEnumerable<StatementSyntax> statements)
public static ImmutableArray<StatementSyntax> RemoveInitializedDeclarationAndReturnPattern(ImmutableArray<StatementSyntax> statements)
{
// if we have inline temp variable as service, we could just use that service here.
// since it is not a service right now, do very simple clean up
......@@ -258,10 +254,10 @@ public static IEnumerable<StatementSyntax> RemoveInitializedDeclarationAndReturn
return statements;
}
return SpecializedCollections.SingletonEnumerable<StatementSyntax>(SyntaxFactory.ReturnStatement(declaration.Declaration.Variables[0].Initializer.Value));
return ImmutableArray.Create<StatementSyntax>(SyntaxFactory.ReturnStatement(declaration.Declaration.Variables[0].Initializer.Value));
}
public static IEnumerable<StatementSyntax> RemoveDeclarationAssignmentPattern(IEnumerable<StatementSyntax> statements)
public static ImmutableArray<StatementSyntax> RemoveDeclarationAssignmentPattern(ImmutableArray<StatementSyntax> statements)
{
if (!(statements.ElementAtOrDefault(0) is LocalDeclarationStatementSyntax declaration) || !(statements.ElementAtOrDefault(1) is ExpressionStatementSyntax assignment))
{
......@@ -294,10 +290,14 @@ public static IEnumerable<StatementSyntax> RemoveDeclarationAssignmentPattern(IE
}
var variable = declaration.Declaration.Variables[0].WithInitializer(SyntaxFactory.EqualsValueClause(assignmentExpression.Right));
return SpecializedCollections.SingletonEnumerable<StatementSyntax>(
declaration.WithDeclaration(
declaration.Declaration.WithVariables(
SyntaxFactory.SingletonSeparatedList(variable)))).Concat(statements.Skip(2));
using var _ = ArrayBuilder<StatementSyntax>.GetInstance(out var result);
result.Add(declaration.WithDeclaration(
declaration.Declaration.WithVariables(
SyntaxFactory.SingletonSeparatedList(variable))));
result.AddRange(statements.Skip(2));
return result.ToImmutable();
}
}
}
......
......@@ -6,9 +6,11 @@
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
......@@ -43,7 +45,11 @@ public override bool ContainingScopeHasAsyncKeyword()
var firstToken = GetFirstTokenInSelection();
var lastToken = GetLastTokenInSelection();
return firstToken.GetCommonRoot(lastToken).GetAncestorOrThis<ExpressionSyntax>();
var scope = firstToken.GetCommonRoot(lastToken).GetAncestorOrThis<ExpressionSyntax>();
if (scope == null)
return null;
return CSharpSyntaxFacts.Instance.GetRootStandaloneExpression(scope);
}
public override ITypeSymbol? GetContainingScopeType()
......
......@@ -100,7 +100,7 @@ protected override bool CheckMemberCanBeConverted(ISymbol member)
if (identifierName == null || !syntaxFacts.IsIdentifierName(identifierName))
return;
var node = syntaxFacts.IsNameOfMemberAccessExpression(identifierName) || syntaxFacts.IsMemberBindingExpression(identifierName.Parent)
var node = syntaxFacts.IsNameOfSimpleMemberAccessExpression(identifierName) || syntaxFacts.IsNameOfMemberBindingExpression(identifierName)
? identifierName.Parent
: identifierName;
......
......@@ -302,7 +302,8 @@ private async Task<ImmutableArray<SymbolReference>> GetReferencesForMatchingType
// 'Black' did not bind. We want to find a type called 'Color' that will actually
// allow 'Black' to bind.
var syntaxFacts = _document.GetLanguageService<ISyntaxFactsService>();
if (syntaxFacts.IsNameOfMemberAccessExpression(nameNode))
if (syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) ||
syntaxFacts.IsNameOfMemberBindingExpression(nameNode))
{
var expression =
syntaxFacts.GetExpressionOfMemberAccessExpression(nameNode.Parent, allowImplicitTarget: true) ??
......
......@@ -170,7 +170,8 @@ private async Task<Document> ConvertToClassAsync(Document document, TextSpan spa
foreach (var identifier in identifiers)
{
if (!syntaxFacts.IsNameOfMemberAccessExpression(identifier))
if (!syntaxFacts.IsNameOfSimpleMemberAccessExpression(identifier) &&
!syntaxFacts.IsNameOfMemberBindingExpression(identifier))
{
continue;
}
......
......@@ -110,15 +110,9 @@ private int GetFirstEndOfLineIndex(List<SyntaxTrivia> list)
location => location,
location => resolver(root, location, _annotations[location]));
// check variable assumption. ordering of two pairs can't be changed
Contract.ThrowIfFalse(
tokens[TriviaLocation.BeforeBeginningOfSpan].RawKind == 0 /* && don't care */ ||
/* don't care && */ tokens[TriviaLocation.AfterEndOfSpan].RawKind == 0 ||
tokens[TriviaLocation.BeforeBeginningOfSpan].Span.End <= tokens[TriviaLocation.AfterEndOfSpan].SpanStart);
Contract.ThrowIfFalse(
tokens[TriviaLocation.AfterBeginningOfSpan].RawKind == 0 /* && don't care */ ||
/* don't care && */ tokens[TriviaLocation.BeforeEndOfSpan].RawKind == 0 ||
tokens[TriviaLocation.AfterBeginningOfSpan].RawKind == 0 /* don't care */ ||
tokens[TriviaLocation.BeforeEndOfSpan].RawKind == 0 /* don't care */ ||
tokens[TriviaLocation.AfterBeginningOfSpan] == tokens[TriviaLocation.BeforeEndOfSpan] ||
tokens[TriviaLocation.AfterBeginningOfSpan].GetPreviousToken(includeZeroWidth: true) == tokens[TriviaLocation.BeforeEndOfSpan] ||
tokens[TriviaLocation.AfterBeginningOfSpan].Span.End <= tokens[TriviaLocation.BeforeEndOfSpan].SpanStart);
......
......@@ -6,11 +6,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
......@@ -229,7 +231,7 @@ private void WrapReturnTypeInTask(SemanticModel model, ref ITypeSymbol returnTyp
}
}
private (IList<VariableInfo> parameters, ITypeSymbol returnType, VariableInfo? variableToUseAsReturnValue, bool unsafeAddressTakenUsed)
private (ImmutableArray<VariableInfo> parameters, ITypeSymbol returnType, VariableInfo? variableToUseAsReturnValue, bool unsafeAddressTakenUsed)
GetSignatureInformation(
DataFlowAnalysis dataFlowAnalysisData,
IDictionary<ISymbol, VariableInfo> variableInfoMap,
......@@ -384,22 +386,22 @@ private bool IsEndOfSelectionReachable(SemanticModel model)
return analysis.EndPointIsReachable;
}
private IList<VariableInfo> MarkVariableInfoToUseAsReturnValueIfPossible(IList<VariableInfo> variableInfo)
private ImmutableArray<VariableInfo> MarkVariableInfoToUseAsReturnValueIfPossible(ImmutableArray<VariableInfo> variableInfo)
{
var variableToUseAsReturnValueIndex = GetIndexOfVariableInfoToUseAsReturnValue(variableInfo);
if (variableToUseAsReturnValueIndex >= 0)
{
variableInfo[variableToUseAsReturnValueIndex] = VariableInfo.CreateReturnValue(variableInfo[variableToUseAsReturnValueIndex]);
}
var index = GetIndexOfVariableInfoToUseAsReturnValue(variableInfo);
if (index < 0)
return variableInfo;
return variableInfo;
return variableInfo.SetItem(index, VariableInfo.CreateReturnValue(variableInfo[index]));
}
private IList<VariableInfo> GetMethodParameters(ICollection<VariableInfo> variableInfo)
private ImmutableArray<VariableInfo> GetMethodParameters(ICollection<VariableInfo> variableInfo)
{
var list = new List<VariableInfo>(variableInfo);
using var _ = ArrayBuilder<VariableInfo>.GetInstance(variableInfo.Count, out var list);
list.AddRange(variableInfo);
VariableInfo.SortVariables(_semanticDocument.SemanticModel.Compilation, list);
return list;
return list.ToImmutable();
}
/// <param name="bestEffort">When false, variables whose data flow is not understood
......
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
......@@ -16,14 +17,14 @@ protected class AnalyzerResult
{
private readonly IList<ITypeParameterSymbol> _typeParametersInDeclaration;
private readonly IList<ITypeParameterSymbol> _typeParametersInConstraintList;
private readonly IList<VariableInfo> _variables;
private readonly ImmutableArray<VariableInfo> _variables;
private readonly VariableInfo _variableToUseAsReturnValue;
public AnalyzerResult(
SemanticDocument document,
IEnumerable<ITypeParameterSymbol> typeParametersInDeclaration,
IEnumerable<ITypeParameterSymbol> typeParametersInConstraintList,
IList<VariableInfo> variables,
ImmutableArray<VariableInfo> variables,
VariableInfo variableToUseAsReturnValue,
ITypeSymbol returnType,
bool awaitTaskReturn,
......@@ -152,11 +153,11 @@ public IEnumerable<VariableInfo> MethodParameters
}
}
public IEnumerable<VariableInfo> GetVariablesToSplitOrMoveIntoMethodDefinition(CancellationToken cancellationToken)
public ImmutableArray<VariableInfo> GetVariablesToSplitOrMoveIntoMethodDefinition(CancellationToken cancellationToken)
{
return _variables
.Where(v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.SplitIn ||
v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveIn);
return _variables.WhereAsArray(
v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.SplitIn ||
v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveIn);
}
public IEnumerable<VariableInfo> GetVariablesToMoveIntoMethodDefinition(CancellationToken cancellationToken)
......
......@@ -68,14 +68,14 @@ protected CodeGenerator(InsertionPoint insertionPoint, SelectionResult selection
protected abstract TNodeUnderContainer GetFirstStatementOrInitializerSelectedAtCallSite();
protected abstract TNodeUnderContainer GetLastStatementOrInitializerSelectedAtCallSite();
protected abstract Task<TNodeUnderContainer> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(SyntaxAnnotation callsiteAnnotation, CancellationToken cancellationToken);
protected abstract Task<TNodeUnderContainer> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(CancellationToken cancellationToken);
protected abstract TExpression CreateCallSignature();
protected abstract TStatement CreateDeclarationStatement(VariableInfo variable, TExpression initialValue, CancellationToken cancellationToken);
protected abstract TStatement CreateAssignmentExpressionStatement(SyntaxToken identifier, TExpression rvalue);
protected abstract TStatement CreateReturnStatement(string identifierName = null);
protected abstract IEnumerable<TStatement> GetInitialStatementsForMethodDefinitions();
protected abstract ImmutableArray<TStatement> GetInitialStatementsForMethodDefinitions();
#endregion
public async Task<GeneratedCode> GenerateAsync(CancellationToken cancellationToken)
......@@ -111,7 +111,8 @@ public async Task<GeneratedCode> GenerateAsync(CancellationToken cancellationTok
cancellationToken);
}
var newDocument = callSiteDocument.Document.WithSyntaxRoot(newCallSiteRoot.ReplaceNode(destination, newContainer));
var newSyntaxRoot = newCallSiteRoot.ReplaceNode(destination, newContainer);
var newDocument = callSiteDocument.Document.WithSyntaxRoot(newSyntaxRoot);
newDocument = await Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, null, cancellationToken).ConfigureAwait(false);
var generatedDocument = await SemanticDocument.CreateAsync(newDocument, cancellationToken).ConfigureAwait(false);
......@@ -165,17 +166,16 @@ protected virtual Task<GeneratedCode> CreateGeneratedCodeAsync(OperationStatus s
protected VariableInfo GetOutermostVariableToMoveIntoMethodDefinition(CancellationToken cancellationToken)
{
var variables = new List<VariableInfo>(AnalyzerResult.GetVariablesToMoveIntoMethodDefinition(cancellationToken));
using var _ = ArrayBuilder<VariableInfo>.GetInstance(out var variables);
variables.AddRange(AnalyzerResult.GetVariablesToMoveIntoMethodDefinition(cancellationToken));
if (variables.Count <= 0)
{
return null;
}
VariableInfo.SortVariables(SemanticDocument.SemanticModel.Compilation, variables);
return variables[0];
}
protected IEnumerable<TStatement> AddReturnIfUnreachable(IEnumerable<TStatement> statements)
protected ImmutableArray<TStatement> AddReturnIfUnreachable(ImmutableArray<TStatement> statements)
{
if (AnalyzerResult.EndOfSelectionReachable)
{
......@@ -197,8 +197,8 @@ protected IEnumerable<TStatement> AddReturnIfUnreachable(IEnumerable<TStatement>
return statements.Concat(CreateReturnStatement());
}
protected async Task<IEnumerable<TStatement>> AddInvocationAtCallSiteAsync(
IEnumerable<TStatement> statements, CancellationToken cancellationToken)
protected async Task<ImmutableArray<TStatement>> AddInvocationAtCallSiteAsync(
ImmutableArray<TStatement> statements, CancellationToken cancellationToken)
{
if (AnalyzerResult.HasVariableToUseAsReturnValue)
{
......@@ -209,11 +209,11 @@ protected IEnumerable<TStatement> AddReturnIfUnreachable(IEnumerable<TStatement>
// add invocation expression
return statements.Concat(
(TStatement)(SyntaxNode)await GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(CallSiteAnnotation, cancellationToken).ConfigureAwait(false));
(TStatement)(SyntaxNode)await GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(cancellationToken).ConfigureAwait(false));
}
protected IEnumerable<TStatement> AddAssignmentStatementToCallSite(
IEnumerable<TStatement> statements,
protected ImmutableArray<TStatement> AddAssignmentStatementToCallSite(
ImmutableArray<TStatement> statements,
CancellationToken cancellationToken)
{
if (!AnalyzerResult.HasVariableToUseAsReturnValue)
......@@ -239,42 +239,31 @@ protected IEnumerable<TStatement> AddReturnIfUnreachable(IEnumerable<TStatement>
CreateAssignmentExpressionStatement(CreateIdentifier(variable.Name), CreateCallSignature()).WithAdditionalAnnotations(CallSiteAnnotation));
}
protected IEnumerable<TStatement> CreateDeclarationStatements(
IEnumerable<VariableInfo> variables, CancellationToken cancellationToken)
protected ImmutableArray<TStatement> CreateDeclarationStatements(
ImmutableArray<VariableInfo> variables, CancellationToken cancellationToken)
{
var list = new List<TStatement>();
foreach (var variable in variables)
{
var declaration = CreateDeclarationStatement(
variable, initialValue: null, cancellationToken: cancellationToken);
list.Add(declaration);
}
return list;
return variables.SelectAsArray(v => CreateDeclarationStatement(v, initialValue: null, cancellationToken));
}
protected IEnumerable<TStatement> AddSplitOrMoveDeclarationOutStatementsToCallSite(
protected ImmutableArray<TStatement> AddSplitOrMoveDeclarationOutStatementsToCallSite(
CancellationToken cancellationToken)
{
var list = new List<TStatement>();
using var _ = ArrayBuilder<TStatement>.GetInstance(out var list);
foreach (var variable in AnalyzerResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken))
{
if (variable.UseAsReturnValue)
{
continue;
}
var declaration = CreateDeclarationStatement(
variable, initialValue: null, cancellationToken: cancellationToken);
list.Add(declaration);
}
return list;
return list.ToImmutable();
}
protected IEnumerable<TStatement> AppendReturnStatementIfNeeded(IEnumerable<TStatement> statements)
protected ImmutableArray<TStatement> AppendReturnStatementIfNeeded(ImmutableArray<TStatement> statements)
{
if (!AnalyzerResult.HasVariableToUseAsReturnValue)
{
......
......@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ExtractMethod
......@@ -120,10 +121,10 @@ public SyntaxToken GetIdentifierTokenAtDeclaration(SemanticDocument document)
public SyntaxToken GetIdentifierTokenAtDeclaration(SyntaxNode node)
=> node.GetAnnotatedTokens(_variableSymbol.IdentifierTokenAnnotation).SingleOrDefault();
public static void SortVariables(Compilation compilation, List<VariableInfo> list)
public static void SortVariables(Compilation compilation, ArrayBuilder<VariableInfo> variables)
{
var cancellationTokenType = compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName);
list.Sort((v1, v2) => Compare(v1, v2, cancellationTokenType));
variables.Sort((v1, v2) => Compare(v1, v2, cancellationTokenType));
}
private static int Compare(VariableInfo left, VariableInfo right, INamedTypeSymbol cancellationTokenType)
......
......@@ -199,7 +199,8 @@ private async Task<Solution> RenameThenRemoveAsyncTokenAsync(Document document,
// await <expr>.M(...).ConfigureAwait(...)
var expressionNode = nameNode;
if (syntaxFacts.IsNameOfMemberAccessExpression(nameNode))
if (syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) ||
syntaxFacts.IsNameOfMemberBindingExpression(nameNode))
{
expressionNode = nameNode.Parent;
}
......
......@@ -115,7 +115,8 @@ protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldS
_expression = _identifierName;
_cref = _service.TryGetCrefSyntax(_identifierName);
if (_syntaxFacts.IsNameOfMemberAccessExpression(_expression))
if (_syntaxFacts.IsNameOfSimpleMemberAccessExpression(_expression) ||
_syntaxFacts.IsNameOfMemberBindingExpression(_expression))
{
_expression = (TExpressionSyntax)_expression.Parent!;
}
......
......@@ -41,7 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeNamespace
[new] = [new].WithTriviaFrom(old)
ElseIf syntaxFacts.IsNameOfMemberAccessExpression(nameRef) Then
ElseIf syntaxFacts.IsNameOfsimpleMemberAccessExpression(nameRef) Then
old = nameRef.Parent
If IsGlobalNamespace(newNamespaceParts) Then
[new] = SyntaxFactory.SimpleMemberAccessExpression(SyntaxFactory.GlobalName(), nameRef.WithoutTrivia())
......
......@@ -40,16 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Debugging
Dim conditionalAccess As ExpressionSyntax = Nothing
If expression.IsRightSideOfDotOrBang() Then
expression = DirectCast(expression.Parent, ExpressionSyntax)
Dim curr = expression
While True
curr = curr.GetCorrespondingConditionalAccessExpression()
If curr Is Nothing Then
Exit While
End If
conditionalAccess = curr
End While
conditionalAccess = If(expression.GetRootConditionalAccessExpression(), expression)
End If
If expression.Parent.IsKind(SyntaxKind.InvocationExpression) Then
......
......@@ -2,6 +2,7 @@
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
......@@ -19,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Me._contextPosition = contextPosition
End Sub
Public Function MergeDeclarationStatements(statements As IEnumerable(Of StatementSyntax)) As IEnumerable(Of StatementSyntax)
Public Function MergeDeclarationStatements(statements As ImmutableArray(Of StatementSyntax)) As ImmutableArray(Of StatementSyntax)
If statements.FirstOrDefault() Is Nothing Then
Return statements
End If
......@@ -27,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return MergeDeclarationStatementsWorker(statements)
End Function
Private Function MergeDeclarationStatementsWorker(statements As IEnumerable(Of StatementSyntax)) As IEnumerable(Of StatementSyntax)
Private Function MergeDeclarationStatementsWorker(statements As ImmutableArray(Of StatementSyntax)) As ImmutableArray(Of StatementSyntax)
Dim declarationStatements = New List(Of StatementSyntax)()
Dim map = New Dictionary(Of ITypeSymbol, List(Of LocalDeclarationStatementSyntax))()
......@@ -51,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Next declStatement
End If
Return declarationStatements
Return declarationStatements.ToImmutableArray()
End Function
Private Sub AppendDeclarationStatementToMap(statement As LocalDeclarationStatementSyntax, map As Dictionary(Of ITypeSymbol, List(Of LocalDeclarationStatementSyntax)))
......@@ -171,7 +172,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return True
End Function
Public Shared Function RemoveDeclarationAssignmentPattern(statements As IEnumerable(Of StatementSyntax)) As IEnumerable(Of StatementSyntax)
Public Shared Function RemoveDeclarationAssignmentPattern(statements As ImmutableArray(Of StatementSyntax)) As ImmutableArray(Of StatementSyntax)
If statements.Count() < 2 Then
Return statements
End If
......@@ -207,10 +208,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Dim variable = declaration.Declarators(0).WithoutTrailingTrivia().WithInitializer(SyntaxFactory.EqualsValue(assignment.Right))
Dim newDeclaration = declaration.WithDeclarators(SyntaxFactory.SingletonSeparatedList(variable))
Return SpecializedCollections.SingletonEnumerable(Of StatementSyntax)(newDeclaration).Concat(statements.Skip(2))
Return SpecializedCollections.SingletonEnumerable(Of StatementSyntax)(newDeclaration).Concat(statements.Skip(2)).ToImmutableArray()
End Function
Public Shared Function RemoveInitializedDeclarationAndReturnPattern(statements As IEnumerable(Of StatementSyntax)) As IEnumerable(Of StatementSyntax)
Public Shared Function RemoveInitializedDeclarationAndReturnPattern(statements As ImmutableArray(Of StatementSyntax)) As ImmutableArray(Of StatementSyntax)
' if we have inline temp variable as service, we could just use that service here.
' since it is not a service right now, do very simple clean up
If statements.Count() <> 2 Then
......@@ -241,7 +242,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
End If
Return SpecializedCollections.SingletonEnumerable(Of StatementSyntax)(
SyntaxFactory.ReturnStatement(declaration.Declarators(0).Initializer.Value)).Concat(statements.Skip(2))
SyntaxFactory.ReturnStatement(declaration.Declarators(0).Initializer.Value)).Concat(statements.Skip(2)).ToImmutableArray()
End Function
End Class
End Class
......
......@@ -2,6 +2,7 @@
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.ExtractMethod
......@@ -24,11 +25,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Protected Overrides Function CreateMethodName() As SyntaxToken
Dim methodName = "NewMethod"
Dim containingScope = CType(VBSelectionResult.GetContainingScope(), SyntaxNode)
Dim containingScope = VBSelectionResult.GetContainingScope()
methodName = GetMethodNameBasedOnExpression(methodName, containingScope)
Dim semanticModel = CType(SemanticDocument.SemanticModel, SemanticModel)
Dim semanticModel = SemanticDocument.SemanticModel
Dim nameGenerator = New UniqueNameGenerator(semanticModel)
Return SyntaxFactory.Identifier(
nameGenerator.CreateUniqueMethodName(containingScope, methodName))
......@@ -65,7 +66,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return methodName
End Function
Protected Overrides Function GetInitialStatementsForMethodDefinitions() As IEnumerable(Of StatementSyntax)
Protected Overrides Function GetInitialStatementsForMethodDefinitions() As ImmutableArray(Of StatementSyntax)
Contract.ThrowIfFalse(IsExtractMethodOnExpression(VBSelectionResult))
Dim expression = DirectCast(VBSelectionResult.GetContainingScope(), ExpressionSyntax)
......@@ -80,13 +81,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
' return error code
If expression.Kind <> SyntaxKind.InvocationExpression AndAlso
expression.Kind <> SyntaxKind.SimpleMemberAccessExpression Then
Return SpecializedCollections.EmptyEnumerable(Of StatementSyntax)()
Return ImmutableArray(Of StatementSyntax).Empty
End If
statement = SyntaxFactory.ExpressionStatement(expression:=expression)
End If
Return SpecializedCollections.SingletonEnumerable(Of StatementSyntax)(statement)
Return ImmutableArray.Create(statement)
End Function
Protected Overrides Function GetOutermostCallSiteContainerToProcess(cancellationToken As CancellationToken) As SyntaxNode
......@@ -113,13 +114,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return GetFirstStatementOrInitializerSelectedAtCallSite()
End Function
Protected Overrides Async Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(callSiteAnnotation As SyntaxAnnotation, cancellationToken As CancellationToken) As Task(Of StatementSyntax)
Protected Overrides Async Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(cancellationToken As CancellationToken) As Task(Of StatementSyntax)
Dim enclosingStatement = GetFirstStatementOrInitializerSelectedAtCallSite()
Dim callSignature = CreateCallSignature().WithAdditionalAnnotations(callSiteAnnotation)
Dim invocation = If(TypeOf callSignature Is AwaitExpressionSyntax,
DirectCast(callSignature, AwaitExpressionSyntax).Expression, callSignature)
Dim callSignature = CreateCallSignature().WithAdditionalAnnotations(CallSiteAnnotation)
Dim sourceNode = DirectCast(VBSelectionResult.GetContainingScope(), SyntaxNode)
Dim sourceNode = VBSelectionResult.GetContainingScope()
Contract.ThrowIfTrue(
sourceNode.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) AndAlso
DirectCast(sourceNode.Parent, MemberAccessExpressionSyntax).Name Is sourceNode,
......@@ -142,32 +141,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
' because of the complexification we cannot guarantee that there is only one annotation.
' however complexification of names is prepended, so the last annotation should be the original one.
sourceNode = DirectCast(updatedRoot.GetAnnotatedNodesAndTokens(sourceNodeAnnotation).Last().AsNode(), SyntaxNode)
' we want to replace the old identifier with a invocation expression, but because of MakeExplicit we might have
' a member access now instead of the identifier. So more syntax fiddling is needed.
If sourceNode.Parent.Kind = SyntaxKind.SimpleMemberAccessExpression AndAlso
DirectCast(sourceNode, ExpressionSyntax).IsRightSideOfDot() Then
Dim explicitMemberAccess = DirectCast(sourceNode.Parent, MemberAccessExpressionSyntax)
Dim replacementMemberAccess = SyntaxFactory.MemberAccessExpression(
sourceNode.Parent.Kind(),
explicitMemberAccess.Expression,
SyntaxFactory.Token(SyntaxKind.DotToken),
DirectCast(DirectCast(invocation, InvocationExpressionSyntax).Expression, SimpleNameSyntax))
replacementMemberAccess = explicitMemberAccess.CopyAnnotationsTo(replacementMemberAccess)
Dim newInvocation = SyntaxFactory.InvocationExpression(
replacementMemberAccess,
DirectCast(invocation, InvocationExpressionSyntax).ArgumentList) _
.WithTrailingTrivia(sourceNode.GetTrailingTrivia())
Dim newCallSignature = If(callSignature IsNot invocation,
callSignature.ReplaceNode(invocation, newInvocation), invocation.CopyAnnotationsTo(newInvocation))
sourceNode = sourceNode.Parent
callSignature = newCallSignature
End If
sourceNode = updatedRoot.GetAnnotatedNodesAndTokens(sourceNodeAnnotation).Last().AsNode()
Return newEnclosingStatement.ReplaceNode(sourceNode, callSignature)
End Function
......
......@@ -2,6 +2,7 @@
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.ExtractMethod
......@@ -39,7 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return SyntaxFactory.Identifier(nameGenerator.CreateUniqueMethodName(containingScope, "NewMethod"))
End Function
Protected Overrides Function GetInitialStatementsForMethodDefinitions() As IEnumerable(Of StatementSyntax)
Protected Overrides Function GetInitialStatementsForMethodDefinitions() As ImmutableArray(Of StatementSyntax)
Dim firstStatementUnderContainer = Me.VBSelectionResult.GetFirstStatementUnderContainer()
Dim lastStatementUnderContainer = Me.VBSelectionResult.GetLastStatementUnderContainer()
......@@ -55,7 +56,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Skip(firstStatementIndex).
Take(lastStatementIndex - firstStatementIndex + 1)
Return nodes
Return nodes.ToImmutableArray()
End Function
Protected Overrides Function GetOutermostCallSiteContainerToProcess(cancellationToken As CancellationToken) As SyntaxNode
......@@ -71,8 +72,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return Me.VBSelectionResult.GetLastStatementUnderContainer()
End Function
Protected Overrides Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(callSiteAnnotation As SyntaxAnnotation, cancellationToken As CancellationToken) As Task(Of StatementSyntax)
Return Task.FromResult(GetStatementContainingInvocationToExtractedMethodWorker().WithAdditionalAnnotations(callSiteAnnotation))
Protected Overrides Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(cancellationToken As CancellationToken) As Task(Of StatementSyntax)
Return Task.FromResult(GetStatementContainingInvocationToExtractedMethodWorker().WithAdditionalAnnotations(CallSiteAnnotation))
End Function
End Class
End Class
......
......@@ -2,6 +2,7 @@
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.ExtractMethod
......@@ -34,10 +35,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
nameGenerator.CreateUniqueMethodName(containingScope, "NewMethod"))
End Function
Protected Overrides Function GetInitialStatementsForMethodDefinitions() As IEnumerable(Of StatementSyntax)
Protected Overrides Function GetInitialStatementsForMethodDefinitions() As ImmutableArray(Of StatementSyntax)
Contract.ThrowIfFalse(IsExtractMethodOnSingleStatement(VBSelectionResult))
Return SpecializedCollections.SingletonEnumerable(Of StatementSyntax)(VBSelectionResult.GetFirstStatement())
Return ImmutableArray.Create(Of StatementSyntax)(VBSelectionResult.GetFirstStatement())
End Function
Protected Overrides Function GetOutermostCallSiteContainerToProcess(cancellationToken As CancellationToken) As SyntaxNode
......@@ -60,8 +61,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return VBSelectionResult.GetFirstStatement()
End Function
Protected Overrides Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(callSiteAnnotation As SyntaxAnnotation, cancellationToken As CancellationToken) As Task(Of StatementSyntax)
Return Task.FromResult(GetStatementContainingInvocationToExtractedMethodWorker().WithAdditionalAnnotations(callSiteAnnotation))
Protected Overrides Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(cancellationToken As CancellationToken) As Task(Of StatementSyntax)
Return Task.FromResult(GetStatementContainingInvocationToExtractedMethodWorker().WithAdditionalAnnotations(CallSiteAnnotation))
End Function
End Class
End Class
......
......@@ -161,27 +161,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return New DeclarationModifiers(isStatic:=isShared, isAsync:=isAsync)
End Function
Private Function CreateMethodBody(cancellationToken As CancellationToken) As OperationStatus(Of IEnumerable(Of StatementSyntax))
Private Function CreateMethodBody(cancellationToken As CancellationToken) As OperationStatus(Of ImmutableArray(Of StatementSyntax))
Dim statements = GetInitialStatementsForMethodDefinitions()
statements = SplitOrMoveDeclarationIntoMethodDefinition(statements, cancellationToken)
statements = MoveDeclarationOutFromMethodDefinition(statements, cancellationToken)
Dim emptyStatements = SpecializedCollections.EmptyEnumerable(Of StatementSyntax)()
Dim emptyStatements = ImmutableArray(Of StatementSyntax).Empty
Dim returnStatements = AppendReturnStatementIfNeeded(emptyStatements)
statements = statements.Concat(returnStatements)
Dim semanticModel = SemanticDocument.SemanticModel
Dim context = Me.InsertionPoint.GetContext()
Dim postProcessor = New PostProcessor(semanticModel, context.SpanStart)
statements = postProcessor.RemoveDeclarationAssignmentPattern(statements)
statements = postProcessor.RemoveInitializedDeclarationAndReturnPattern(statements)
statements = PostProcessor.RemoveDeclarationAssignmentPattern(statements)
statements = PostProcessor.RemoveInitializedDeclarationAndReturnPattern(statements)
' assign before checking issues so that we can do negative preview
Return CheckActiveStatements(statements).With(statements)
End Function
Private Shared Function CheckActiveStatements(statements As IEnumerable(Of StatementSyntax)) As OperationStatus
Private Shared Function CheckActiveStatements(statements As ImmutableArray(Of StatementSyntax)) As OperationStatus
Dim count = statements.Count()
If count = 0 Then
Return OperationStatus.NoActiveStatement
......@@ -215,7 +215,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return OperationStatus.NoActiveStatement
End Function
Private Function MoveDeclarationOutFromMethodDefinition(statements As IEnumerable(Of StatementSyntax), cancellationToken As CancellationToken) As IEnumerable(Of StatementSyntax)
Private Function MoveDeclarationOutFromMethodDefinition(statements As ImmutableArray(Of StatementSyntax), cancellationToken As CancellationToken) As ImmutableArray(Of StatementSyntax)
Dim variableToRemoveMap = CreateVariableDeclarationToRemoveMap(
Me.AnalyzerResult.GetVariablesToMoveOutToCallSiteOrDelete(cancellationToken), cancellationToken)
......@@ -271,10 +271,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Next expressionStatement
Next
Return declarationStatements
Return declarationStatements.ToImmutableArray()
End Function
Private Function SplitOrMoveDeclarationIntoMethodDefinition(statements As IEnumerable(Of StatementSyntax), cancellationToken As CancellationToken) As IEnumerable(Of StatementSyntax)
Private Function SplitOrMoveDeclarationIntoMethodDefinition(statements As ImmutableArray(Of StatementSyntax), cancellationToken As CancellationToken) As ImmutableArray(Of StatementSyntax)
Dim semanticModel = CType(Me.SemanticDocument.SemanticModel, SemanticModel)
Dim context = Me.InsertionPoint.GetContext()
Dim postProcessor = New PostProcessor(semanticModel, context.SpanStart)
......
......@@ -5,9 +5,11 @@
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.ExtractMethod
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
......@@ -94,7 +96,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return False
End If
Dim node = CType(Me.GetContainingScope(), SyntaxNode)
Dim node = Me.GetContainingScope()
If TypeOf node Is MethodBlockBaseSyntax Then
Dim methodBlock = DirectCast(node, MethodBlockBaseSyntax)
If methodBlock.BlockStatement IsNot Nothing Then
......@@ -120,15 +122,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
If SelectionInExpression Then
Dim last = GetLastTokenInSelection()
Return first.GetCommonRoot(last).GetAncestorOrThis(Of ExpressionSyntax)()
End If
Dim scope = first.GetCommonRoot(last).GetAncestorOrThis(Of ExpressionSyntax)()
Contract.ThrowIfNull(scope, "Should always find an expression given that SelectionInExpression was true")
' it contains statements
Return first.GetAncestors(Of SyntaxNode).FirstOrDefault(Function(n) TypeOf n Is MethodBlockBaseSyntax OrElse TypeOf n Is LambdaExpressionSyntax)
Return VisualBasicSyntaxFacts.Instance.GetRootStandaloneExpression(scope)
Else
' it contains statements
Return first.GetAncestors(Of SyntaxNode).FirstOrDefault(Function(n) TypeOf n Is MethodBlockBaseSyntax OrElse TypeOf n Is LambdaExpressionSyntax)
End If
End Function
Public Overrides Function GetContainingScopeType() As ITypeSymbol
Dim node = CType(Me.GetContainingScope(), SyntaxNode)
Dim node = Me.GetContainingScope()
Dim semanticModel = Me.SemanticDocument.SemanticModel
' special case for collection initializer and explicit cast
......@@ -286,11 +291,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
' Contract.ThrowIfFalse(last.IsParentKind(SyntaxKind.GlobalStatement))
' Contract.ThrowIfFalse(last.Parent.IsParentKind(SyntaxKind.CompilationUnit))
' Return last.Parent.Parent
throw ExceptionUtilities.Unreachable
Throw ExceptionUtilities.Unreachable
End Function
Public Function IsUnderModuleBlock() As Boolean
Dim currentScope = CType(GetContainingScope(), SyntaxNode)
Dim currentScope = GetContainingScope()
Dim types = currentScope.GetAncestors(Of TypeBlockSyntax)()
Return types.Any(Function(t) t.BlockStatement.Kind = SyntaxKind.ModuleStatement)
......
......@@ -72,10 +72,11 @@ private static bool CouldBeGenericType(SyntaxToken identifier)
}
// ?.X.Identifier or ?.X.Y.Identifier is never a generic type.
if (identifierName.IsMemberAccessExpressionName() &&
identifier.Parent.IsParentKind(SyntaxKind.ConditionalAccessExpression))
if (identifierName.IsSimpleMemberAccessExpressionName() ||
identifierName.IsMemberBindingExpressionName())
{
return false;
if (identifier.Parent.IsParentKind(SyntaxKind.ConditionalAccessExpression))
return false;
}
// Add more cases as necessary.
......
......@@ -1067,7 +1067,7 @@ public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax
// Bail out on extension method invocations in conditional access expression.
// Note that this is a temporary workaround for https://github.com/dotnet/roslyn/issues/2593.
// Issue https://github.com/dotnet/roslyn/issues/3260 tracks fixing this workaround.
if (originalMemberAccess.GetParentConditionalAccessExpression() == null)
if (originalMemberAccess.GetRootConditionalAccessExpression() == null)
{
var speculationPosition = originalNode.SpanStart;
var expression = RewriteExtensionMethodInvocation(speculationPosition, rewrittenNode, thisExpression, reducedExtensionMethod);
......
......@@ -793,7 +793,9 @@ private static bool IsNodeOrAnyAncestorLeftSideOfDot(SyntaxNode node, ISyntaxFac
return true;
}
if (syntaxFacts.IsRightSideOfQualifiedName(node) || syntaxFacts.IsNameOfMemberAccessExpression(node))
if (syntaxFacts.IsRightSideOfQualifiedName(node) ||
syntaxFacts.IsNameOfSimpleMemberAccessExpression(node) ||
syntaxFacts.IsNameOfMemberBindingExpression(node))
{
return syntaxFacts.IsLeftSideOfDot(node.Parent);
}
......
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
......@@ -33,9 +34,8 @@ public static ExpressionSyntax WalkDownParentheses(this ExpressionSyntax express
public static bool IsQualifiedCrefName(this ExpressionSyntax expression)
=> expression.IsParentKind(SyntaxKind.NameMemberCref) && expression.Parent.IsParentKind(SyntaxKind.QualifiedCref);
public static bool IsMemberAccessExpressionName(this ExpressionSyntax expression)
=> (expression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression, out MemberAccessExpressionSyntax memberAccess) && memberAccess.Name == expression) ||
IsMemberBindingExpressionName(expression);
public static bool IsSimpleMemberAccessExpressionName(this ExpressionSyntax expression)
=> expression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression, out MemberAccessExpressionSyntax memberAccess) && memberAccess.Name == expression;
public static bool IsAnyMemberAccessExpressionName(this ExpressionSyntax expression)
{
......@@ -48,7 +48,7 @@ public static bool IsAnyMemberAccessExpressionName(this ExpressionSyntax express
expression.IsMemberBindingExpressionName();
}
private static bool IsMemberBindingExpressionName(this ExpressionSyntax expression)
public static bool IsMemberBindingExpressionName(this ExpressionSyntax expression)
=> expression.IsParentKind(SyntaxKind.MemberBindingExpression, out MemberBindingExpressionSyntax memberBinding) &&
memberBinding.Name == expression;
......@@ -59,7 +59,7 @@ public static bool IsRightSideOfColonColon(this ExpressionSyntax expression)
=> expression.IsParentKind(SyntaxKind.AliasQualifiedName, out AliasQualifiedNameSyntax aliasName) && aliasName.Name == expression;
public static bool IsRightSideOfDot(this ExpressionSyntax name)
=> IsMemberAccessExpressionName(name) || IsRightSideOfQualifiedName(name) || IsQualifiedCrefName(name);
=> IsSimpleMemberAccessExpressionName(name) || IsMemberBindingExpressionName(name) || IsRightSideOfQualifiedName(name) || IsQualifiedCrefName(name);
public static bool IsRightSideOfDotOrArrow(this ExpressionSyntax name)
=> IsAnyMemberAccessExpressionName(name) || IsRightSideOfQualifiedName(name);
......@@ -67,7 +67,7 @@ public static bool IsRightSideOfDotOrArrow(this ExpressionSyntax name)
public static bool IsRightSideOfDotOrColonColon(this ExpressionSyntax name)
=> IsRightSideOfDot(name) || IsRightSideOfColonColon(name);
public static bool IsRightSideOfDotOrArrowOrColonColon(this ExpressionSyntax name)
public static bool IsRightSideOfDotOrArrowOrColonColon([NotNullWhen(true)] this ExpressionSyntax name)
=> IsRightSideOfDotOrArrow(name) || IsRightSideOfColonColon(name);
public static bool IsRightOfCloseParen(this ExpressionSyntax expression)
......
......@@ -11,18 +11,14 @@ internal static class SimpleNameSyntaxExtensions
{
public static ExpressionSyntax GetLeftSideOfDot(this SimpleNameSyntax name)
{
Debug.Assert(name.IsMemberAccessExpressionName() || name.IsRightSideOfQualifiedName() || name.IsParentKind(SyntaxKind.NameMemberCref));
if (name.IsMemberAccessExpressionName())
Debug.Assert(name.IsSimpleMemberAccessExpressionName() || name.IsMemberBindingExpressionName() || name.IsRightSideOfQualifiedName() || name.IsParentKind(SyntaxKind.NameMemberCref));
if (name.IsSimpleMemberAccessExpressionName())
{
var conditionalAccess = name.GetParentConditionalAccessExpression();
if (conditionalAccess != null)
{
return conditionalAccess.Expression;
}
else
{
return ((MemberAccessExpressionSyntax)name.Parent).Expression;
}
return ((MemberAccessExpressionSyntax)name.Parent).Expression;
}
else if (name.IsMemberBindingExpressionName())
{
return name.GetParentConditionalAccessExpression().Expression;
}
else if (name.IsRightSideOfQualifiedName())
{
......
......@@ -14,6 +14,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -271,21 +272,100 @@ public static SyntaxList<AttributeListSyntax> GetAttributeLists(this SyntaxNode
_ => default,
};
public static ConditionalAccessExpressionSyntax? GetParentConditionalAccessExpression(this SyntaxNode node)
public static ConditionalAccessExpressionSyntax? GetParentConditionalAccessExpression(this SyntaxNode? node)
{
// Walk upwards based on the grammar/parser rules around ?. expressions (can be seen in
// LanguageParser.ParseConsequenceSyntax).
// These are the parts of the expression that the ?... expression can end with. Specifically:
//
// 1. x?.y.M() // invocation
// 2. x?.y[...]; // element access
// 3. x?.y.z // member access
// 4. x?.y // member binding
// 5. x?[y] // element binding
var current = node;
while (current?.Parent != null)
if ((current.IsParentKind(SyntaxKind.SimpleMemberAccessExpression, out MemberAccessExpressionSyntax? memberAccess) && memberAccess.Name == current) ||
(current.IsParentKind(SyntaxKind.MemberBindingExpression, out MemberBindingExpressionSyntax? memberBinding) && memberBinding.Name == current))
{
if (current.IsParentKind(SyntaxKind.ConditionalAccessExpression, out ConditionalAccessExpressionSyntax? conditional) &&
conditional.WhenNotNull == current)
{
return conditional;
}
current = current.Parent;
}
// Effectively, if we're on the RHS of the ? we have to walk up the RHS spine first until we hit the first
// conditional access.
while (current.IsKind(
SyntaxKind.InvocationExpression,
SyntaxKind.ElementAccessExpression,
SyntaxKind.SimpleMemberAccessExpression,
SyntaxKind.MemberBindingExpression,
SyntaxKind.ElementBindingExpression) &&
current.Parent is not ConditionalAccessExpressionSyntax)
{
current = current.Parent;
}
return null;
// Two cases we have to care about:
//
// 1. a?.b.$$c.d and
// 2. a?.b.$$c.d?.e...
//
// Note that `a?.b.$$c.d?.e.f?.g.h.i` falls into the same bucket as two. i.e. the parts after `.e` are
// lower in the tree and are not seen as we walk upwards.
//
//
// To get the root ?. (the one after the `a`) we have to potentially consume the first ?. on the RHS of the
// right spine (i.e. the one after `d`). Once we do this, we then see if that itself is on the RHS of a
// another conditional, and if so we hten return the one on the left. i.e. for '2' this goes in this direction:
//
// a?.b.$$c.d?.e // it will do:
// ----->
// <---------
//
// Note that this only one CAE consumption on both sides. GetRootConditionalAccessExpression can be used to
// get the root parent in a case like:
//
// x?.y?.z?.a?.b.$$c.d?.e.f?.g.h.i // it will do:
// ----->
// <---------
// <---
// <---
// <---
if (current.IsParentKind(SyntaxKind.ConditionalAccessExpression, out ConditionalAccessExpressionSyntax? conditional) &&
conditional.Expression == current)
{
current = conditional;
}
if (current.IsParentKind(SyntaxKind.ConditionalAccessExpression, out conditional) &&
conditional.WhenNotNull == current)
{
current = conditional;
}
return current as ConditionalAccessExpressionSyntax;
}
/// <summary>
/// <inheritdoc cref="ISyntaxFacts.GetRootConditionalAccessExpression(SyntaxNode)"/>
/// </summary>>
public static ConditionalAccessExpressionSyntax? GetRootConditionalAccessExpression(this SyntaxNode? node)
{
// Once we've walked up the entire RHS, now we continually walk up the conditional accesses until we're at
// the root. For example, if we have `a?.b` and we're on the `.b`, this will give `a?.b`. Similarly with
// `a?.b?.c` if we're on either `.b` or `.c` this will result in `a?.b?.c` (i.e. the root of this CAE
// sequence).
var current = node.GetParentConditionalAccessExpression();
while (current.IsParentKind(SyntaxKind.ConditionalAccessExpression, out ConditionalAccessExpressionSyntax? conditional) &&
conditional.WhenNotNull == current)
{
current = conditional;
}
return current;
}
public static ConditionalAccessExpressionSyntax? GetInnerMostConditionalAccessExpression(this SyntaxNode node)
......
......@@ -147,11 +147,28 @@ public bool IsRightSideOfQualifiedName(SyntaxNode node)
}
#nullable enable
public bool IsNameOfMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node)
public bool IsNameOfSimpleMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node)
{
var name = node as SimpleNameSyntax;
return name.IsMemberAccessExpressionName();
return name.IsSimpleMemberAccessExpressionName();
}
public bool IsNameOfAnyMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node)
=> node?.Parent is MemberAccessExpressionSyntax memberAccess && memberAccess.Name == node;
public bool IsNameOfMemberBindingExpression([NotNullWhen(true)] SyntaxNode? node)
{
var name = node as SimpleNameSyntax;
return name.IsMemberBindingExpressionName();
}
public SyntaxNode? GetStandaloneExpression(SyntaxNode? node)
=> node is ExpressionSyntax expression ? SyntaxFactory.GetStandaloneExpression(expression) : node;
public SyntaxNode? GetRootConditionalAccessExpression(SyntaxNode? node)
=> node.GetRootConditionalAccessExpression();
#nullable restore
public bool IsObjectCreationExpressionType(SyntaxNode node)
......
......@@ -185,10 +185,29 @@ internal partial interface ISyntaxFacts
bool IsLeftSideOfExplicitInterfaceSpecifier(SyntaxNode node);
#nullable enable
bool IsNameOfMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node);
bool IsNameOfSimpleMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node);
bool IsNameOfAnyMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node);
bool IsNameOfMemberBindingExpression([NotNullWhen(true)] SyntaxNode? node);
#nullable restore
bool IsExpressionOfMemberAccessExpression(SyntaxNode node);
/// <summary>
/// Gets the containing expression that is actually a language expression and not just typed
/// as an ExpressionSyntax for convenience. For example, NameSyntax nodes on the right side
/// of qualified names and member access expressions are not language expressions, yet the
/// containing qualified names or member access expressions are indeed expressions.
/// </summary>
SyntaxNode GetStandaloneExpression(SyntaxNode node);
/// <summary>
/// Call on the `.y` part of a `x?.y` to get the entire `x?.y` conditional access expression. This also works
/// when there are multiple chained conditional accesses. For example, calling this on '.y' or '.z' in
/// `x?.y?.z` will both return the full `x?.y?.z` node. This can be used to effectively get 'out' of the RHS of
/// a conditional access, and commonly represents the full standalone expression that can be operated on
/// atomically.
/// </summary>
SyntaxNode GetRootConditionalAccessExpression(SyntaxNode node);
bool IsExpressionOfMemberAccessExpression(SyntaxNode node);
SyntaxNode GetNameOfMemberAccessExpression(SyntaxNode node);
/// <summary>
......
......@@ -257,6 +257,26 @@ public static bool IsOnTypeHeader(this ISyntaxFacts syntaxFacts, SyntaxNode root
return null;
}
/// <summary>
/// Similar to <see cref="ISyntaxFacts.GetStandaloneExpression(SyntaxNode)"/>, this gets the containing
/// expression that is actually a language expression and not just typed as an ExpressionSyntax for convenience.
/// However, this goes beyond that that method in that if this expression is the RHS of a conditional access
/// (i.e. <c>a?.b()</c>) it will also return the root of the conditional access expression tree.
/// <para/> The intuition here is that this will give the topmost expression node that could realistically be
/// replaced with any other expression. For example, with <c>a?.b()</c> technically <c>.b()</c> is an
/// expression. But that cannot be replaced with something like <c>(1 + 1)</c> (as <c>a?.(1 + 1)</c> is not
/// legal). However, in <c>a?.b()</c>, then <c>a</c> itself could be replaced with <c>(1 + 1)?.b()</c> to form
/// a legal expression.
/// </summary>
public static SyntaxNode GetRootStandaloneExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
{
// First, make sure we're on a construct the language things is a standalone expression.
var standalone = syntaxFacts.GetStandaloneExpression(node);
// Then, if this is the RHS of a `?`, walk up to the top of that tree to get the final standalone expression.
return syntaxFacts.GetRootConditionalAccessExpression(standalone) ?? standalone;
}
#region ISyntaxKinds forwarding methods
#region trivia
......
......@@ -53,15 +53,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
End Function
<Extension()>
Public Function IsMemberAccessExpressionName(expression As ExpressionSyntax) As Boolean
Public Function IsSimpleMemberAccessExpressionName(expression As ExpressionSyntax) As Boolean
Return expression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) AndAlso
DirectCast(expression.Parent, MemberAccessExpressionSyntax).Name Is expression
End Function
<Extension()>
Public Function IsAnyMemberAccessExpressionName(expression As ExpressionSyntax) As Boolean
Return expression IsNot Nothing AndAlso
TypeOf expression.Parent Is MemberAccessExpressionSyntax AndAlso
Return TypeOf expression?.Parent Is MemberAccessExpressionSyntax AndAlso
DirectCast(expression.Parent, MemberAccessExpressionSyntax).Name Is expression
End Function
......@@ -72,7 +71,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
<Extension()>
Public Function IsRightSideOfDot(expression As ExpressionSyntax) As Boolean
Return expression.IsMemberAccessExpressionName() OrElse expression.IsRightSideOfQualifiedName()
Return expression.IsSimpleMemberAccessExpressionName() OrElse expression.IsRightSideOfQualifiedName()
End Function
<Extension()>
......
......@@ -83,7 +83,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
' 3) new T() With { .a = 1, .b = .a <-- 'a refers to the T type
If allowImplicitTarget Then
Dim conditional = memberAccessExpression.GetCorrespondingConditionalAccessExpression()
Dim conditional = memberAccessExpression.GetRootConditionalAccessExpression()
If conditional IsNot Nothing Then
If conditional.Expression Is Nothing Then
......
......@@ -6,6 +6,7 @@ Imports System.Collections.Immutable
Imports System.Runtime.CompilerServices
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
......@@ -205,7 +206,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Return multiLineLambdaExpression.Statements
End If
throw ExceptionUtilities.UnexpectedValue(node)
Throw ExceptionUtilities.UnexpectedValue(node)
End Function
<Extension()>
......@@ -997,58 +998,96 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Next
End Function
''' <summary>
''' Given an expression within a tree of <see cref="ConditionalAccessExpressionSyntax"/>s,
''' finds the <see cref="ConditionalAccessExpressionSyntax"/> that it is part of.
''' </summary>
''' <param name="node"></param>
''' <returns></returns>
<Extension>
Friend Function GetCorrespondingConditionalAccessExpression(node As ExpressionSyntax) As ConditionalAccessExpressionSyntax
Dim access As SyntaxNode = node
Dim parent As SyntaxNode = access.Parent
While parent IsNot Nothing
Select Case parent.Kind
Case SyntaxKind.DictionaryAccessExpression,
SyntaxKind.SimpleMemberAccessExpression
Friend Function GetParentConditionalAccessExpression(node As ExpressionSyntax) As ConditionalAccessExpressionSyntax
' Walk upwards based on the grammar/parser rules around ?. expressions (can be seen in
' ParseExpression.vb (.ParsePostFixExpression).
'
' These are the parts of the expression that the ?... expression can end with. Specifically
'
' 1. x?.y.M() // invocation
' 2. x?.y and x?.y.z // member access (covered under MemberAccessExpressionSyntax below)
' 3. x?!y // dictionary access (covered under MemberAccessExpressionSyntax below)
' 4. x?.y<...> // xml access
If node.IsAnyMemberAccessExpressionName() Then
node = DirectCast(node.Parent, ExpressionSyntax)
End If
If DirectCast(parent, MemberAccessExpressionSyntax).Expression IsNot access Then
Return Nothing
End If
' Effectively, if we're on the RHS of the ? we have to walk up the RHS spine first until we hit the first
' conditional access.
Case SyntaxKind.XmlElementAccessExpression,
SyntaxKind.XmlDescendantAccessExpression,
SyntaxKind.XmlAttributeAccessExpression
While (TypeOf node Is InvocationExpressionSyntax OrElse
TypeOf node Is MemberAccessExpressionSyntax OrElse
TypeOf node Is XmlMemberAccessExpressionSyntax) AndAlso
TypeOf node.Parent IsNot ConditionalAccessExpressionSyntax
If DirectCast(parent, XmlMemberAccessExpressionSyntax).Base IsNot access Then
Return Nothing
End If
Case SyntaxKind.InvocationExpression
node = TryCast(node.Parent, ExpressionSyntax)
End While
If DirectCast(parent, InvocationExpressionSyntax).Expression IsNot access Then
Return Nothing
End If
' Two cases we have to care about
'
' 1. a?.b.$$c.d And
' 2. a?.b.$$c.d?.e...
'
' Note that `a?.b.$$c.d?.e.f?.g.h.i` falls into the same bucket as two. i.e. the parts after `.e` are
' lower in the tree And are Not seen as we walk upwards.
'
'
' To get the root ?. (the one after the `a`) we have to potentially consume the first ?. on the RHS of the
' right spine (i.e. the one after `d`). Once we do this, we then see if that itself Is on the RHS of a
' another conditional, And if so we hten return the one on the left. i.e. for '2' this goes in this direction:
'
' a?.b.$$c.d?.e // it will do:
' ----->
' <---------
'
' Note that this only one CAE consumption on both sides. GetRootConditionalAccessExpression can be used to
' get the root parent in a case Like:
'
' x?.y?.z?.a?.b.$$c.d?.e.f?.g.h.i // It will do:
' ----->
' <---------
' <---
' <---
' <---
If TypeOf node?.Parent Is ConditionalAccessExpressionSyntax AndAlso
DirectCast(node.Parent, ConditionalAccessExpressionSyntax).Expression Is node Then
node = DirectCast(node.Parent, ExpressionSyntax)
End If
Case SyntaxKind.ConditionalAccessExpression
If TypeOf node?.Parent Is ConditionalAccessExpressionSyntax AndAlso
DirectCast(node.Parent, ConditionalAccessExpressionSyntax).WhenNotNull Is node Then
Dim conditional = DirectCast(parent, ConditionalAccessExpressionSyntax)
If conditional.WhenNotNull Is access Then
Return conditional
ElseIf conditional.Expression IsNot access Then
Return Nothing
End If
node = DirectCast(node.Parent, ExpressionSyntax)
End If
Case Else
Return Nothing
End Select
Return TryCast(node, ConditionalAccessExpressionSyntax)
End Function
access = parent
parent = access.Parent
''' <summary>
''' <see cref="ISyntaxFacts.GetRootConditionalAccessExpression"/>
''' </summary>
<Extension>
Friend Function GetRootConditionalAccessExpression(node As ExpressionSyntax) As ConditionalAccessExpressionSyntax
' Once we've walked up the entire RHS, now we continually walk up the conditional accesses until we're at
' the root. For example, if we have `a?.b` And we're on the `.b`, this will give `a?.b`. Similarly with
' `a?.b?.c` if we're on either `.b` or `.c` this will result in `a?.b?.c` (i.e. the root of this CAE
' sequence).
node = node.GetParentConditionalAccessExpression()
While TypeOf node?.Parent Is ConditionalAccessExpressionSyntax
Dim conditionalParent = DirectCast(node.Parent, ConditionalAccessExpressionSyntax)
If conditionalParent.WhenNotNull Is node Then
node = conditionalParent
Else
Exit While
End If
End While
Return Nothing
Return TryCast(node, ConditionalAccessExpressionSyntax)
End Function
<Extension>
......
......@@ -169,9 +169,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Return vbNode IsNot Nothing AndAlso vbNode.IsRightSideOfQualifiedName()
End Function
Public Function IsNameOfMemberAccessExpression(node As SyntaxNode) As Boolean Implements ISyntaxFacts.IsNameOfMemberAccessExpression
Dim vbNode = TryCast(node, SimpleNameSyntax)
Return vbNode IsNot Nothing AndAlso vbNode.IsMemberAccessExpressionName()
Public Function IsNameOfSimpleMemberAccessExpression(node As SyntaxNode) As Boolean Implements ISyntaxFacts.IsNameOfSimpleMemberAccessExpression
Dim vbNode = TryCast(node, ExpressionSyntax)
Return vbNode IsNot Nothing AndAlso vbNode.IsSimpleMemberAccessExpressionName()
End Function
Public Function IsNameOfAnyMemberAccessExpression(node As SyntaxNode) As Boolean Implements ISyntaxFacts.IsNameOfAnyMemberAccessExpression
Dim memberAccess = TryCast(node?.Parent, MemberAccessExpressionSyntax)
Return memberAccess IsNot Nothing AndAlso memberAccess.Name Is node
End Function
Public Function GetStandaloneExpression(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetStandaloneExpression
Return SyntaxFactory.GetStandaloneExpression(TryCast(node, ExpressionSyntax))
End Function
Public Function GetRootConditionalAccessExpression(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetRootConditionalAccessExpression
Return TryCast(node, ExpressionSyntax).GetRootConditionalAccessExpression()
End Function
Public Sub GetPartsOfConditionalAccessExpression(node As SyntaxNode, ByRef expression As SyntaxNode, ByRef operatorToken As SyntaxToken, ByRef whenNotNull As SyntaxNode) Implements ISyntaxFacts.GetPartsOfConditionalAccessExpression
......@@ -1879,7 +1892,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices
End Function
Public Function IsMemberBindingExpression(node As SyntaxNode) As Boolean Implements ISyntaxFacts.IsMemberBindingExpression
' Does not exist in VB.
' Does not exist in VB. VB represents a member binding as a MemberAccessExpression with null target.
Return False
End Function
Public Function IsNameOfMemberBindingExpression(node As SyntaxNode) As Boolean Implements ISyntaxFacts.IsNameOfMemberBindingExpression
' Does not exist in VB. VB represents a member binding as a MemberAccessExpression with null target.
Return False
End Function
......
......@@ -10,8 +10,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Friend Module SimpleNameSyntaxExtensions
<Extension()>
Public Function GetLeftSideOfDot(name As SimpleNameSyntax) As ExpressionSyntax
Debug.Assert(IsMemberAccessExpressionName(name) OrElse IsRightSideOfQualifiedName(name))
If IsMemberAccessExpressionName(name) Then
Debug.Assert(IsSimpleMemberAccessExpressionName(name) OrElse IsRightSideOfQualifiedName(name))
If IsSimpleMemberAccessExpressionName(name) Then
Return DirectCast(name.Parent, MemberAccessExpressionSyntax).Expression
Else
Return DirectCast(name.Parent, QualifiedNameSyntax).Left
......
......@@ -224,7 +224,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification
reducedExtensionMethod As IMethodSymbol) As InvocationExpressionSyntax
Dim originalMemberAccess = DirectCast(originalNode.Expression, MemberAccessExpressionSyntax)
If originalMemberAccess.GetCorrespondingConditionalAccessExpression IsNot Nothing Then
If originalMemberAccess.GetRootConditionalAccessExpression() IsNot Nothing Then
' Bail out on extension method invocations in conditional access expression.
' Note that this is a temporary workaround for https://github.com/dotnet/roslyn/issues/2593.
' Issue https//github.com/dotnet/roslyn/issues/3260 tracks fixing this workaround.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册