diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb index bc954cc19d317..c69aebf65a57d 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb @@ -53,6 +53,83 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Await TestAPIAndFeature(input) End Function + + + Public Async Function FindReferences_GetAwaiter() As Task + Dim input = + + + {|Definition:Get$$Awaiter|}() => Task.FromResult(true).GetAwaiter(); + + static async void M(C c) + { + [|await|] c; + [|await|] c; + } +} + ]]> + + + Await TestAPIAndFeature(input) + End Function + + + + Public Async Function FindReferences_GetAwaiter_VB() As Task + Dim input = + + + + + + Await TestAPIAndFeature(input) + End Function + + + + Public Async Function FindReferences_GetAwaiterInAnotherDocument() As Task + Dim input = + + + {|Definition:Get$$Awaiter|}() => Task.FromResult(true).GetAwaiter(); +} + ]]> + + + + Await TestAPIAndFeature(input) + End Function + Public Async Function FindReferences_Deconstruction() As Task diff --git a/src/EditorFeatures/Test2/Rename/CSharp/ImplicitReferenceConflictTests.vb b/src/EditorFeatures/Test2/Rename/CSharp/ImplicitReferenceConflictTests.vb index 199f7f6a9677f..529e41cce0a1c 100644 --- a/src/EditorFeatures/Test2/Rename/CSharp/ImplicitReferenceConflictTests.vb +++ b/src/EditorFeatures/Test2/Rename/CSharp/ImplicitReferenceConflictTests.vb @@ -98,6 +98,31 @@ class C End Using End Sub + + Public Sub RenameGetAwaiterCausesConflict() + Using result = RenameEngineResult.Create(_outputHelper, + + + [|Get$$Awaiter|]() => Task.FromResult(true).GetAwaiter(); + + static async void M(C c) + { + {|awaitconflict:await|} c; + } +} + ]]> + + , renameTo:="GetAwaiter2") + + result.AssertLabeledSpansAre("awaitconflict", type:=RelatedLocationType.UnresolvedConflict) + End Using + End Sub + Public Sub RenameMoveNextInVBCausesConflictInForEach() diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSemanticFactsService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSemanticFactsService.cs index 863a0c861480c..230dbc9f91275 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSemanticFactsService.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSemanticFactsService.cs @@ -231,6 +231,17 @@ public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode } } + public IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node) + { + if (node is AwaitExpressionSyntax awaitExpression) + { + var info = semanticModel.GetAwaitExpressionInfo(awaitExpression); + return info.GetAwaiterMethod; + } + + return null; + } + public ImmutableArray GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node) { if (node is AssignmentExpressionSyntax assignment && assignment.IsDeconstruction()) diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs index 2fb617c39a602..57e4cebbaf8db 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs @@ -1585,6 +1585,9 @@ public bool IsExpressionOfInvocationExpression(SyntaxNode node) return node != null && (node.Parent as InvocationExpressionSyntax)?.Expression == node; } + public bool IsAwaitExpression(SyntaxNode node) + => node.IsKind(SyntaxKind.AwaitExpression); + public bool IsExpressionOfAwaitExpression(SyntaxNode node) { return node != null && (node.Parent as AwaitExpressionSyntax)?.Expression == node; diff --git a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs index 02b087cae4db7..62d696f667923 100644 --- a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs @@ -996,6 +996,7 @@ public async Task> ComputeImplicitReferenceConflictsAsy (renameSymbol.Kind == SymbolKind.Method && (string.Compare(renameSymbol.Name, WellKnownMemberNames.MoveNextMethodName, StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(renameSymbol.Name, WellKnownMemberNames.GetEnumeratorMethodName, StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(renameSymbol.Name, WellKnownMemberNames.GetAwaiter, StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(renameSymbol.Name, WellKnownMemberNames.DeconstructMethodName, StringComparison.OrdinalIgnoreCase) == 0)); // TODO: handle Dispose for using statement and Add methods for collection initializers. @@ -1013,6 +1014,8 @@ public async Task> ComputeImplicitReferenceConflictsAsy { case SyntaxKind.ForEachKeyword: return ImmutableArray.Create(((CommonForEachStatementSyntax)token.Parent).Expression.GetLocation()); + case SyntaxKind.AwaitKeyword: + return ImmutableArray.Create(token.GetLocation()); } if (token.Parent.IsInDeconstructionLeft(out var deconstructionLeft)) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index d60cce23d4224..9d4aa35895a16 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -395,33 +395,42 @@ private static async Task> FindReferencesThrou return allAliasReferences.ToImmutableAndFree(); } - protected Task> FindDocumentsWithForEachStatementsAsync(Project project, IImmutableSet documents, CancellationToken cancellationToken) + private Task> FindDocumentsWithPredicateAsync(Project project, IImmutableSet documents, Func predicate, CancellationToken cancellationToken) { return FindDocumentsAsync(project, documents, async (d, c) => { var info = await SyntaxTreeIndex.GetIndexAsync(d, c).ConfigureAwait(false); - return info.ContainsForEachStatement; + return predicate(info); }, cancellationToken); } + protected Task> FindDocumentsWithForEachStatementsAsync(Project project, IImmutableSet documents, CancellationToken cancellationToken) + => FindDocumentsWithPredicateAsync(project, documents, predicate: sti => sti.ContainsForEachStatement, cancellationToken); + protected Task> FindDocumentsWithDeconstructionAsync(Project project, IImmutableSet documents, CancellationToken cancellationToken) - { - return FindDocumentsAsync(project, documents, async (d, c) => - { - var info = await SyntaxTreeIndex.GetIndexAsync(d, c).ConfigureAwait(false); - return info.ContainsDeconstruction; - }, cancellationToken); - } + => FindDocumentsWithPredicateAsync(project, documents, predicate: sti => sti.ContainsDeconstruction, cancellationToken); + + protected Task> FindDocumentsWithAwaitExpressionAsync(Project project, IImmutableSet documents, CancellationToken cancellationToken) + => FindDocumentsWithPredicateAsync(project, documents, predicate: sti => sti.ContainsAwait, cancellationToken); - protected async Task> FindReferencesInForEachStatementsAsync( + /// + /// If the `node` implicitly matches the `symbol`, then it will be added to `locations`. + /// + private delegate void CollectMatchingReferences(ISymbol symbol, SyntaxNode node, + ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations); + + private async Task> FindReferencesInDocumentAsync( ISymbol symbol, Document document, SemanticModel semanticModel, + Func isRelevantDocument, + CollectMatchingReferences collectMatchingReferences, CancellationToken cancellationToken) { var syntaxTreeInfo = await SyntaxTreeIndex.GetIndexAsync(document, cancellationToken).ConfigureAwait(false); - if (syntaxTreeInfo.ContainsForEachStatement) + if (isRelevantDocument(syntaxTreeInfo)) { + var syntaxFacts = document.GetLanguageService(); var semanticFacts = document.GetLanguageService(); var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -432,50 +441,57 @@ protected async Task> FindReferencesInForEachS foreach (var node in syntaxRoot.DescendantNodesAndSelf()) { cancellationToken.ThrowIfCancellationRequested(); - var info = semanticFacts.GetForEachSymbols(semanticModel, node); - - if (Matches(info.GetEnumeratorMethod, originalUnreducedSymbolDefinition) || - Matches(info.MoveNextMethod, originalUnreducedSymbolDefinition) || - Matches(info.CurrentProperty, originalUnreducedSymbolDefinition) || - Matches(info.DisposeMethod, originalUnreducedSymbolDefinition)) - { - var location = node.GetFirstToken().GetLocation(); - locations.Add(new ReferenceLocation( - document, alias: null, location: location, isImplicit: true, isWrittenTo: false, candidateReason: CandidateReason.None)); - } + collectMatchingReferences(originalUnreducedSymbolDefinition, node, syntaxFacts, semanticFacts, locations); } return locations.ToImmutableAndFree(); } - else - { - return ImmutableArray.Empty; - } + + return ImmutableArray.Empty; } - protected async Task> FindReferencesInDeconstructionAsync( + protected Task> FindReferencesInForEachStatementsAsync( ISymbol symbol, Document document, SemanticModel semanticModel, CancellationToken cancellationToken) { - var syntaxTreeInfo = await SyntaxTreeIndex.GetIndexAsync(document, cancellationToken).ConfigureAwait(false); - if (!syntaxTreeInfo.ContainsDeconstruction) + return FindReferencesInDocumentAsync(symbol, document, semanticModel, isRelevantDocument, collectMatchingReferences, cancellationToken); + + bool isRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) + => syntaxTreeInfo.ContainsForEachStatement; + + void collectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, + ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { - return ImmutableArray.Empty; - } + var info = semanticFacts.GetForEachSymbols(semanticModel, node); - var syntaxFacts = document.GetLanguageService(); - var semanticFacts = document.GetLanguageService(); - var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + if (Matches(info.GetEnumeratorMethod, originalUnreducedSymbolDefinition) || + Matches(info.MoveNextMethod, originalUnreducedSymbolDefinition) || + Matches(info.CurrentProperty, originalUnreducedSymbolDefinition) || + Matches(info.DisposeMethod, originalUnreducedSymbolDefinition)) + { + var location = node.GetFirstToken().GetLocation(); + locations.Add(new ReferenceLocation( + document, alias: null, location: location, isImplicit: true, isWrittenTo: false, candidateReason: CandidateReason.None)); + } + } + } - var locations = ArrayBuilder.GetInstance(); + protected Task> FindReferencesInDeconstructionAsync( + ISymbol symbol, + Document document, + SemanticModel semanticModel, + CancellationToken cancellationToken) + { + return FindReferencesInDocumentAsync(symbol, document, semanticModel, isRelevantDocument, collectMatchingReferences, cancellationToken); - var originalUnreducedSymbolDefinition = symbol.GetOriginalUnreducedDefinition(); + bool isRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) + => syntaxTreeInfo.ContainsDeconstruction; - foreach (var node in syntaxRoot.DescendantNodesAndSelf()) + void collectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, + ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { - cancellationToken.ThrowIfCancellationRequested(); var deconstructMethods = semanticFacts.GetDeconstructionAssignmentMethods(semanticModel, node); if (deconstructMethods.IsEmpty) { @@ -490,8 +506,31 @@ protected async Task> FindReferencesInDeconstr document, alias: null, location, isImplicit: true, isWrittenTo: false, CandidateReason.None)); } } + } - return locations.ToImmutableAndFree(); + protected Task> FindReferencesInAwaitExpressionAsync( + ISymbol symbol, + Document document, + SemanticModel semanticModel, + CancellationToken cancellationToken) + { + return FindReferencesInDocumentAsync(symbol, document, semanticModel, isRelevantDocument, collectMatchingReferences, cancellationToken); + + bool isRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) + => syntaxTreeInfo.ContainsAwait; + + void collectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, + ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) + { + var awaitExpressionMethod = semanticFacts.GetGetAwaiterMethod(semanticModel, node); + + if (Matches(awaitExpressionMethod, originalUnreducedSymbolDefinition)) + { + var location = node.GetFirstToken().GetLocation(); + locations.Add(new ReferenceLocation( + document, alias: null, location, isImplicit: true, isWrittenTo: false, CandidateReason.None)); + } + } } private static bool Matches(ISymbol symbol1, ISymbol notNulloriginalUnreducedSymbol2) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs index 9fb6518fd7778..d27646301a710 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs @@ -97,7 +97,11 @@ protected override async Task> DetermineDocumentsToSear ? await FindDocumentsWithDeconstructionAsync(project, documents, cancellationToken).ConfigureAwait(false) : ImmutableArray.Empty; - return ordinaryDocuments.Concat(forEachDocuments).Concat(deconstructDocuments); + var awaitExpressionDocuments = IsGetAwaiterMethod(methodSymbol) + ? await FindDocumentsWithAwaitExpressionAsync(project, documents, cancellationToken).ConfigureAwait(false) + : ImmutableArray.Empty; + + return ordinaryDocuments.Concat(forEachDocuments).Concat(deconstructDocuments).Concat(awaitExpressionDocuments); } private bool IsForEachMethod(IMethodSymbol methodSymbol) @@ -110,6 +114,9 @@ private bool IsForEachMethod(IMethodSymbol methodSymbol) private bool IsDeconstructMethod(IMethodSymbol methodSymbol) => methodSymbol.Name == WellKnownMemberNames.DeconstructMethodName; + private bool IsGetAwaiterMethod(IMethodSymbol methodSymbol) + => methodSymbol.Name == WellKnownMemberNames.GetAwaiter; + protected override async Task> FindReferencesInDocumentAsync( IMethodSymbol symbol, Document document, @@ -135,6 +142,12 @@ protected override async Task> FindReferencesI nameMatches = nameMatches.Concat(deconstructMatches); } + if (IsGetAwaiterMethod(symbol)) + { + var getAwaiterMatches = await FindReferencesInAwaitExpressionAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); + nameMatches = nameMatches.Concat(getAwaiterMatches); + } + return nameMatches; } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index b4656f3eeb086..25f7f8eb0a99e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -25,7 +25,8 @@ public ContextInfo( bool containsBaseConstructorInitializer, bool containsElementAccessExpression, bool containsIndexerMemberCref, - bool containsDeconstruction) : + bool containsDeconstruction, + bool containsAwait) : this(predefinedTypes, predefinedOperators, ConvertToContainingNodeFlag( containsForEachStatement, @@ -36,7 +37,8 @@ public ContextInfo( containsBaseConstructorInitializer, containsElementAccessExpression, containsIndexerMemberCref, - containsDeconstruction)) + containsDeconstruction, + containsAwait)) { } @@ -56,7 +58,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsBaseConstructorInitializer, bool containsElementAccessExpression, bool containsIndexerMemberCref, - bool containsDeconstruction) + bool containsDeconstruction, + bool containsAwait) { var containingNodes = ContainingNodes.None; @@ -69,6 +72,7 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes = containsElementAccessExpression ? (containingNodes | ContainingNodes.ContainsElementAccessExpression) : containingNodes; containingNodes = containsIndexerMemberCref ? (containingNodes | ContainingNodes.ContainsIndexerMemberCref) : containingNodes; containingNodes = containsDeconstruction ? (containingNodes | ContainingNodes.ContainsDeconstruction) : containingNodes; + containingNodes = containsAwait ? (containingNodes | ContainingNodes.ContainsAwait) : containingNodes; return containingNodes; } @@ -85,6 +89,9 @@ public bool ContainsForEachStatement public bool ContainsDeconstruction => (_containingNodes & ContainingNodes.ContainsDeconstruction) == ContainingNodes.ContainsDeconstruction; + public bool ContainsAwait + => (_containingNodes & ContainingNodes.ContainsAwait) == ContainingNodes.ContainsAwait; + public bool ContainsLockStatement => (_containingNodes & ContainingNodes.ContainsLockStatement) == ContainingNodes.ContainsLockStatement; @@ -143,6 +150,7 @@ private enum ContainingNodes ContainsElementAccessExpression = 1 << 6, ContainsIndexerMemberCref = 1 << 7, ContainsDeconstruction = 1 << 8, + ContainsAwait = 1 << 9, } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 5bea44691ef30..e6ba99858917f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -70,6 +70,7 @@ private static async Task CreateIndexAsync( var containsElementAccess = false; var containsIndexerMemberCref = false; var containsDeconstruction = false; + var containsAwait = false; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -96,6 +97,8 @@ private static async Task CreateIndexAsync( containsDeconstruction = containsDeconstruction || syntaxFacts.IsDeconstructionAssignment(node) || syntaxFacts.IsDeconstructionForEachStatement(node); + containsAwait = containsAwait || syntaxFacts.IsAwaitExpression(node); + // We've received a number of error reports where DeclaredSymbolInfo.GetSymbolAsync() will // crash because the document's syntax root doesn't contain the span of the node returned // by TryGetDeclaredSymbolInfo(). There are two possibilities for this crash: @@ -203,7 +206,8 @@ private static async Task CreateIndexAsync( containsBaseConstructorInitializer, containsElementAccess, containsIndexerMemberCref, - containsDeconstruction), + containsDeconstruction, + containsAwait), new DeclarationInfo( declaredSymbolInfos.ToImmutableAndFree())); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index ecc18a7c4e149..b671d6f4a7d86 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -21,6 +21,7 @@ internal sealed partial class SyntaxTreeIndex public bool ContainsForEachStatement => _contextInfo.ContainsForEachStatement; public bool ContainsDeconstruction => _contextInfo.ContainsDeconstruction; + public bool ContainsAwait => _contextInfo.ContainsAwait; public bool ContainsLockStatement => _contextInfo.ContainsLockStatement; public bool ContainsUsingStatement => _contextInfo.ContainsUsingStatement; public bool ContainsQueryExpression => _contextInfo.ContainsQueryExpression; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 3771b4b7ac814..5d44d02ad831d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal sealed partial class SyntaxTreeIndex : IObjectWritable { private const string PersistenceName = ""; - private const string SerializationFormat = "11"; + private const string SerializationFormat = "12"; public readonly Checksum Checksum; diff --git a/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs b/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs index 028231cbe8fa0..dfce8669c75b2 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs @@ -91,6 +91,8 @@ internal interface ISemanticFactsService : ILanguageService ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement); + IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node); + ImmutableArray GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node); ImmutableArray GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node); diff --git a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs index 1b24140ed7921..d3fc001aa3af2 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs @@ -93,6 +93,7 @@ internal interface ISyntaxFactsService : ILanguageService SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node); + bool IsAwaitExpression(SyntaxNode node); bool IsExpressionOfAwaitExpression(SyntaxNode node); SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node); diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSemanticFactsService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSemanticFactsService.vb index 9429e9f1730a8..89e4990c2640f 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSemanticFactsService.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSemanticFactsService.vb @@ -7,6 +7,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageServices +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -256,6 +257,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return Nothing End Function + Public Function GetGetAwaiterMethod(model As SemanticModel, node As SyntaxNode) As IMethodSymbol Implements ISemanticFactsService.GetGetAwaiterMethod + If node.IsKind(SyntaxKind.AwaitExpression) Then + Dim awaitExpression = DirectCast(node, AwaitExpressionSyntax) + Dim info = model.GetAwaitExpressionInfo(awaitExpression) + Return info.GetAwaiterMethod + End If + + Return Nothing + End Function + Public Function GetDeconstructionAssignmentMethods(model As SemanticModel, deconstruction As SyntaxNode) As ImmutableArray(Of IMethodSymbol) Implements ISemanticFactsService.GetDeconstructionAssignmentMethods Return ImmutableArray(Of IMethodSymbol).Empty End Function diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb index c638db916cc76..9b9d517f52382 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb @@ -1521,6 +1521,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return node IsNot Nothing AndAlso TryCast(node.Parent, InvocationExpressionSyntax)?.Expression Is node End Function + Public Function IsAwaitExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsAwaitExpression + Return node.IsKind(SyntaxKind.AwaitExpression) + 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