diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs index f816be5ce9880241da8656fae6e033d732b6558f..9a9e2e928abb518dd405e1a5718d0c409ab1f86e 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs @@ -352,5 +352,203 @@ int Bar() } }", compareTokens: false, fixAllActionEquivalenceKey: AbstractMakeMethodSynchronousCodeFixProvider.EquivalenceKey); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [WorkItem(13961, "https://github.com/dotnet/roslyn/issues/13961")] + public async Task TestRemoveAwaitFromCaller1() + { + await TestAsync( +@"using System.Threading.Tasks; + +public class Class1 +{ + async Task [|FooAsync|]() + { + } + + async void BarAsync() + { + await FooAsync(); + } +}", +@"using System.Threading.Tasks; + +public class Class1 +{ + void Foo() + { + } + + async void BarAsync() + { + Foo(); + } +}", compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [WorkItem(13961, "https://github.com/dotnet/roslyn/issues/13961")] + public async Task TestRemoveAwaitFromCaller2() + { + await TestAsync( +@"using System.Threading.Tasks; + +public class Class1 +{ + async Task [|FooAsync|]() + { + } + + async void BarAsync() + { + await FooAsync().ConfigureAwait(false); + } +}", +@"using System.Threading.Tasks; + +public class Class1 +{ + void Foo() + { + } + + async void BarAsync() + { + Foo(); + } +}", compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [WorkItem(13961, "https://github.com/dotnet/roslyn/issues/13961")] + public async Task TestRemoveAwaitFromCaller3() + { + await TestAsync( +@"using System.Threading.Tasks; + +public class Class1 +{ + async Task [|FooAsync|]() + { + } + + async void BarAsync() + { + await this.FooAsync(); + } +}", +@"using System.Threading.Tasks; + +public class Class1 +{ + void Foo() + { + } + + async void BarAsync() + { + this.Foo(); + } +}", compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [WorkItem(13961, "https://github.com/dotnet/roslyn/issues/13961")] + public async Task TestRemoveAwaitFromCaller4() + { + await TestAsync( +@"using System.Threading.Tasks; + +public class Class1 +{ + async Task [|FooAsync|]() + { + } + + async void BarAsync() + { + await this.FooAsync().ConfigureAwait(false); + } +}", +@"using System.Threading.Tasks; + +public class Class1 +{ + void Foo() + { + } + + async void BarAsync() + { + this.Foo(); + } +}", compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [WorkItem(13961, "https://github.com/dotnet/roslyn/issues/13961")] + public async Task TestRemoveAwaitFromCallerNested1() + { + await TestAsync( +@"using System.Threading.Tasks; + +public class Class1 +{ + async Task [|FooAsync|](int i) + { + } + + async void BarAsync() + { + await this.FooAsync(await this.FooAsync(0)); + } +}", +@"using System.Threading.Tasks; + +public class Class1 +{ + int Foo(int i) + { + } + + async void BarAsync() + { + this.Foo(this.Foo(0)); + } +}", compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [WorkItem(13961, "https://github.com/dotnet/roslyn/issues/13961")] + public async Task TestRemoveAwaitFromCallerNested() + { + await TestAsync( +@"using System.Threading.Tasks; + +public class Class1 +{ + async Task [|FooAsync|](int i) + { + } + + async void BarAsync() + { + await this.FooAsync(await this.FooAsync(0).ConfigureAwait(false)).ConfigureAwait(false); + } +}", +@"using System.Threading.Tasks; + +public class Class1 +{ + int Foo(int i) + { + } + + async void BarAsync() + { + this.Foo(this.Foo(0)); + } +}", compareTokens: false); + } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.vb index 8d27d05cb4e008a60fb2e55179dcaabef885fdda..3dfeaf302f62bedfcf04de3d3faa37595cb25e7a 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.vb @@ -233,5 +233,161 @@ Class C End Class", compareTokens:=False) End Function + + + + Public Async Function TestRemoveAwaitFromCaller1() As Task + Await TestAsync( +"Imports System.Threading.Tasks; + +Public Class Class1 + Async Function [|FooAsync|]() As Task + End Function + + Async Sub BarAsync() + Await FooAsync() + End Sub +End Class", +"Imports System.Threading.Tasks; + +Public Class Class1 + Sub Foo() + End Sub + + Async Sub BarAsync() + Foo() + End Sub +End Class", compareTokens:=False) + End Function + + + + Public Async Function TestRemoveAwaitFromCaller2() As Task + Await TestAsync( +"Imports System.Threading.Tasks; + +Public Class Class1 + Async Function [|FooAsync|]() As Task + End Function + + Async Sub BarAsync() + Await FooAsync().ConfigureAwait(false) + End Sub +End Class", +"Imports System.Threading.Tasks; + +Public Class Class1 + Sub Foo() + End Sub + + Async Sub BarAsync() + Foo() + End Sub +End Class", compareTokens:=False) + End Function + + + + Public Async Function TestRemoveAwaitFromCaller3() As Task + Await TestAsync( +"Imports System.Threading.Tasks; + +Public Class Class1 + Async Function [|FooAsync|]() As Task + End Function + + Async Sub BarAsync() + Await Me.FooAsync() + End Sub +End Class", +"Imports System.Threading.Tasks; + +Public Class Class1 + Sub Foo() + End Sub + + Async Sub BarAsync() + Me.Foo() + End Sub +End Class", compareTokens:=False) + End Function + + + + Public Async Function TestRemoveAwaitFromCaller4() As Task + Await TestAsync( +"Imports System.Threading.Tasks; + +Public Class Class1 + Async Function [|FooAsync|]() As Task + End Function + + Async Sub BarAsync() + Await Me.FooAsync().ConfigureAwait(false) + End Sub +End Class", +"Imports System.Threading.Tasks; + +Public Class Class1 + Sub Foo() + End Sub + + Async Sub BarAsync() + Me.Foo() + End Sub +End Class", compareTokens:=False) + End Function + + + + Public Async Function TestRemoveAwaitFromCallerNested1() As Task + Await TestAsync( +"Imports System.Threading.Tasks; + +Public Class Class1 + Async Function [|FooAsync|](i As Integer) As Task(Of Integer) + End Function + + Async Sub BarAsync() + Await FooAsync(Await FooAsync(0)) + End Sub +End Class", +"Imports System.Threading.Tasks; + +Public Class Class1 + Function Foo(i As Integer) As Integer + End Function + + Async Sub BarAsync() + Foo(Foo(0)) + End Sub +End Class", compareTokens:=False) + End Function + + + + Public Async Function TestRemoveAwaitFromCallerNested2() As Task + Await TestAsync( +"Imports System.Threading.Tasks; + +Public Class Class1 + Async Function [|FooAsync|](i As Integer) As Task(Of Integer) + End Function + + Async Sub BarAsync() + Await Me.FooAsync(Await Me.FooAsync(0).ConfigureAwait(false)).ConfigureAwait(false) + End Sub +End Class", +"Imports System.Threading.Tasks; + +Public Class Class1 + Function Foo(i As Integer) As Integer + End Function + + Async Sub BarAsync() + Me.Foo(Me.Foo(0)) + End Sub +End Class", compareTokens:=False) + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs index c574d69ec1157ea99ef0045f9a4b34d25084a8f3..73af782d20915d1666fc08472cd53e8d4d528612 100644 --- a/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs @@ -250,7 +250,7 @@ protected override bool CanAddImport(SyntaxNode node, CancellationToken cancella return false; } - if (!syntaxFacts.IsMemberAccessExpressionName(node)) + if (!syntaxFacts.IsNameOfMemberAccessExpression(node)) { return false; } diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs index c932abca67fdb531d2bf1a0ca987f8406d5318bb..75443397a632aec3153740bbeae222e33c3b8ae7 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs @@ -545,7 +545,7 @@ private async Task> GetNamespacesForCollectionInitializer // 'Black' did not bind. We want to find a type called 'Color' that will actually // allow 'Black' to bind. var syntaxFacts = this._document.GetLanguageService(); - if (!syntaxFacts.IsMemberAccessExpressionName(nameNode)) + if (!syntaxFacts.IsNameOfMemberAccessExpression(nameNode)) { return null; } diff --git a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index b608ef6fdf1829ec60a2dee2471c3db13d14adfc..33d4df106fa3657b3790f6b9eb5413e63b34d015 100644 --- a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -1,12 +1,17 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -71,7 +76,7 @@ private async Task RenameThenRemoveAsyncTokenAsync(Document document, var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); SyntaxNode newNode; - if (syntaxPath.TryResolve(newRoot, out newNode)) + if (syntaxPath.TryResolve(newRoot, out newNode)) { var semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var newMethod = (IMethodSymbol)semanticModel.GetDeclaredSymbol(newNode, cancellationToken); @@ -81,20 +86,167 @@ private async Task RenameThenRemoveAsyncTokenAsync(Document document, return newSolution; } - private async Task RemoveAsyncTokenAsync(Document document, IMethodSymbol methodSymbolOpt, SyntaxNode node, CancellationToken cancellationToken) + private async Task RemoveAsyncTokenAsync( + Document document, IMethodSymbol methodSymbolOpt, SyntaxNode node, CancellationToken cancellationToken) { var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task"); var taskOfTType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1"); + var annotation = new SyntaxAnnotation(); var newNode = RemoveAsyncTokenAndFixReturnType(methodSymbolOpt, node, taskType, taskOfTType) - .WithAdditionalAnnotations(Formatter.Annotation); + .WithAdditionalAnnotations(Formatter.Annotation, annotation); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(node, newNode); var newDocument = document.WithSyntaxRoot(newRoot); - return newDocument.Project.Solution; + var newSolution = newDocument.Project.Solution; + + if (methodSymbolOpt == null) + { + return newSolution; + } + + return await RemoveAwaitFromCallersAsync( + newDocument, annotation, cancellationToken).ConfigureAwait(false) ; + } + + private async Task RemoveAwaitFromCallersAsync( + Document document, SyntaxAnnotation annotation, CancellationToken cancellationToken) + { + var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var methodDeclaration = syntaxRoot.GetAnnotatedNodes(annotation).FirstOrDefault(); + if (methodDeclaration != null) + { + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration) as IMethodSymbol; + + if (methodSymbol != null) + { + var references = await SymbolFinder.FindRenamableReferencesAsync( + new SymbolAndProjectId(methodSymbol, document.Project.Id), + document.Project.Solution, cancellationToken).ConfigureAwait(false); + + var referencedSymbol = references.FirstOrDefault(r => Equals(r.Definition, methodSymbol)); + if (referencedSymbol != null) + { + return await RemoveAwaitFromCallersAsync( + document.Project.Solution, referencedSymbol.Locations.ToImmutableArray(), cancellationToken).ConfigureAwait(false); + } + } + } + + return document.Project.Solution; + } + + private async Task RemoveAwaitFromCallersAsync( + Solution solution, ImmutableArray locations, CancellationToken cancellationToken) + { + var currentSolution = solution; + + var groupedLocations = locations.GroupBy(loc => loc.Document); + + foreach (var group in groupedLocations) + { + currentSolution = await RemoveAwaitFromCallersAsync( + currentSolution, group, cancellationToken).ConfigureAwait(false); + } + + return currentSolution; + } + + private async Task RemoveAwaitFromCallersAsync( + Solution currentSolution, IGrouping group, CancellationToken cancellationToken) + { + var document = group.Key; + var syntaxFactsService = document.GetLanguageService(); + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var editor = new SyntaxEditor(root, currentSolution.Workspace); + + foreach (var location in group) + { + RemoveAwaitFromCallerIfPresent(editor, syntaxFactsService, root, location, cancellationToken); + } + + var newRoot = editor.GetChangedRoot(); + return currentSolution.WithDocumentSyntaxRoot(document.Id, newRoot); + } + + private void RemoveAwaitFromCallerIfPresent( + SyntaxEditor editor, ISyntaxFactsService syntaxFacts, + SyntaxNode root, ReferenceLocation referenceLocation, + CancellationToken cancellationToken) + { + if (referenceLocation.IsImplicit) + { + return; + } + + var location = referenceLocation.Location; + var token = location.FindToken(cancellationToken); + + var nameNode = token.Parent; + if (nameNode == null) + { + return; + } + + // Look for the following forms: + // await M(...) + // await .M(...) + // await M(...).ConfigureAwait(...) + // await .M(...).ConfigureAwait(...) + + var expressionNode = nameNode; + if (syntaxFacts.IsNameOfMemberAccessExpression(nameNode)) + { + expressionNode = nameNode.Parent; + } + + if (!syntaxFacts.IsExpressionOfInvocationExpression(expressionNode)) + { + return; + } + + // We now either have M(...) or .M(...) + + var invocationExpression = expressionNode.Parent; + Debug.Assert(syntaxFacts.IsInvocationExpression(invocationExpression)); + + if (syntaxFacts.IsExpressionOfAwaitExpression(invocationExpression)) + { + // Handle the case where we're directly awaited. + var awaitExpression = invocationExpression.Parent; + editor.ReplaceNode(awaitExpression, (currentAwaitExpression, generator) => + syntaxFacts.GetExpressionOfAwaitExpression(currentAwaitExpression) + .WithTriviaFrom(currentAwaitExpression)); + } + else if (syntaxFacts.IsExpressionOfMemberAccessExpression(invocationExpression)) + { + // Check for the .ConfigureAwait case. + var parentMemberAccessExpression = invocationExpression.Parent; + var parentMemberAccessExpressionNameNode = syntaxFacts.GetNameOfMemberAccessExpression( + parentMemberAccessExpression); + + var parentMemberAccessExpressionName = syntaxFacts.GetIdentifierOfSimpleName(parentMemberAccessExpressionNameNode).ValueText; + if (parentMemberAccessExpressionName == nameof(Task.ConfigureAwait)) + { + var parentExpression = parentMemberAccessExpression.Parent; + if (syntaxFacts.IsExpressionOfAwaitExpression(parentExpression)) + { + var awaitExpression = parentExpression.Parent; + editor.ReplaceNode(awaitExpression, (currentAwaitExpression, generator) => + { + var currentConfigureAwaitInvocation = syntaxFacts.GetExpressionOfAwaitExpression(currentAwaitExpression); + var currentMemberAccess = syntaxFacts.GetExpressionOfInvocationExpression(currentConfigureAwaitInvocation); + var currentInvocationExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(currentMemberAccess); + return currentInvocationExpression.WithTriviaFrom(currentAwaitExpression); + }); + } + } + } } private class MyCodeAction : CodeAction.SolutionChangeAction diff --git a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs index c4b3702ab6f1d33171a6faf4c3d748dba7467cf9..595b97eab0852f7fb04f0a74d7a6fe92d2090f67 100644 --- a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs +++ b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs @@ -94,7 +94,7 @@ private struct ReferenceReplacer _identifierName = (TIdentifierNameSyntax)nameToken.Parent; _expression = _identifierName; - if (_syntaxFacts.IsMemberAccessExpressionName(_expression)) + if (_syntaxFacts.IsNameOfMemberAccessExpression(_expression)) { _expression = _expression.Parent as TExpressionSyntax; } diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs index 32eaa2bcbb04e48e8a45ccf65fbeb7fca7f3af64..ff874663955fa9f37a96be670cd05a05fb005bfb 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs @@ -150,7 +150,7 @@ public bool IsRightSideOfQualifiedName(SyntaxNode node) return name.IsRightSideOfQualifiedName(); } - public bool IsMemberAccessExpressionName(SyntaxNode node) + public bool IsNameOfMemberAccessExpression(SyntaxNode node) { var name = node as SimpleNameSyntax; return name.IsMemberAccessExpressionName(); @@ -1839,6 +1839,31 @@ public bool AreEquivalent(SyntaxNode node1, SyntaxNode node2) return SyntaxFactory.AreEquivalent(node1, node2); } + public bool IsExpressionOfInvocationExpression(SyntaxNode node) + { + return node != null && (node.Parent as InvocationExpressionSyntax)?.Expression == node; + } + + public bool IsExpressionOfAwaitExpression(SyntaxNode node) + { + return node != null && (node.Parent as AwaitExpressionSyntax)?.Expression == node; + } + + public bool IsExpressionOfMemberAccessExpression(SyntaxNode node) + { + return node != null && (node.Parent as MemberAccessExpressionSyntax)?.Expression == node; + } + + public SyntaxNode GetExpressionOfInvocationExpression(SyntaxNode node) + { + return ((InvocationExpressionSyntax)node).Expression; + } + + public SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node) + { + return ((AwaitExpressionSyntax)node).Expression; + } + private class AddFirstMissingCloseBaceRewriter: CSharpSyntaxRewriter { private readonly SyntaxNode _contextNode; diff --git a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs index 1dc32bd5a2eb582df1a09f76eb3154484ac7eceb..22e55d08c9e2bbfb05f9a6de2b619d7c9fe06df2 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs @@ -52,6 +52,11 @@ internal interface ISyntaxFactsService : ILanguageService SyntaxNode GetObjectCreationInitializer(SyntaxNode objectCreationExpression); bool IsInvocationExpression(SyntaxNode node); + bool IsExpressionOfInvocationExpression(SyntaxNode node); + SyntaxNode GetExpressionOfInvocationExpression(SyntaxNode node); + + bool IsExpressionOfAwaitExpression(SyntaxNode node); + SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node); // Left side of = assignment. bool IsLeftSideOfAssignment(SyntaxNode node); @@ -72,7 +77,9 @@ internal interface ISyntaxFactsService : ILanguageService bool IsRightSideOfQualifiedName(SyntaxNode node); - bool IsMemberAccessExpressionName(SyntaxNode node); + bool IsNameOfMemberAccessExpression(SyntaxNode node); + bool IsExpressionOfMemberAccessExpression(SyntaxNode node); + SyntaxNode GetNameOfMemberAccessExpression(SyntaxNode memberAccessExpression); SyntaxNode GetExpressionOfMemberAccessExpression(SyntaxNode memberAccessExpression); SyntaxToken GetOperatorTokenOfMemberAccessExpression(SyntaxNode memberAccessExpression); diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb index e67942a185e6d47fd474923b37b43ba39e3eee7e..18a2c77c21864b43916b8a144b3faa5e35db4d11 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb @@ -142,7 +142,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return vbNode IsNot Nothing AndAlso vbNode.IsRightSideOfQualifiedName() End Function - Public Function IsMemberAccessExpressionName(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsMemberAccessExpressionName + Public Function IsNameOfMemberAccessExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsNameOfMemberAccessExpression Dim vbNode = TryCast(node, SimpleNameSyntax) Return vbNode IsNot Nothing AndAlso vbNode.IsMemberAccessExpressionName() End Function @@ -1516,5 +1516,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Function AreEquivalent(node1 As SyntaxNode, node2 As SyntaxNode) As Boolean Implements ISyntaxFactsService.AreEquivalent Return SyntaxFactory.AreEquivalent(node1, node2) End Function + + Public Function IsExpressionOfInvocationExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsExpressionOfInvocationExpression + Return node IsNot Nothing AndAlso TryCast(node.Parent, InvocationExpressionSyntax)?.Expression Is node + End Function + + Public Function IsExpressionOfAwaitExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsExpressionOfAwaitExpression + Return node IsNot Nothing AndAlso TryCast(node.Parent, AwaitExpressionSyntax)?.Expression Is node + End Function + + Public Function IsExpressionOfMemberAccessExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsExpressionOfMemberAccessExpression + Return node IsNot Nothing AndAlso TryCast(node.Parent, MemberAccessExpressionSyntax)?.Expression Is node + End Function + + Public Function GetExpressionOfInvocationExpression(node As SyntaxNode) As SyntaxNode Implements ISyntaxFactsService.GetExpressionOfInvocationExpression + Return DirectCast(node, InvocationExpressionSyntax).Expression + End Function + + Public Function GetExpressionOfAwaitExpression(node As SyntaxNode) As SyntaxNode Implements ISyntaxFactsService.GetExpressionOfAwaitExpression + Return DirectCast(node, AwaitExpressionSyntax).Expression + End Function End Class End Namespace \ No newline at end of file