Skip to content

Commit 5d19d55

Browse files
Simplify check for await expressions
1 parent a2a1f97 commit 5d19d55

File tree

1 file changed

+23
-17
lines changed

1 file changed

+23
-17
lines changed

src/Features/Core/Portable/ExtractMethod/SelectionResult.cs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Threading;
1010
using System.Threading.Tasks;
1111
using Microsoft.CodeAnalysis.LanguageService;
12+
using Microsoft.CodeAnalysis.PooledObjects;
1213
using Microsoft.CodeAnalysis.Shared.Extensions;
1314
using Microsoft.CodeAnalysis.Text;
1415
using Roslyn.Utilities;
@@ -45,7 +46,7 @@ internal abstract class SelectionResult(
4546
/// </summary>
4647
private ControlFlowAnalysis? _statementControlFlowAnalysis;
4748

48-
protected abstract bool UnderAnonymousOrLocalMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken);
49+
// protected abstract bool UnderAnonymousOrLocalMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken);
4950

5051
public abstract TExecutableStatementSyntax GetFirstStatementUnderContainer();
5152
public abstract TExecutableStatementSyntax GetLastStatementUnderContainer();
@@ -119,31 +120,36 @@ public TExecutableStatementSyntax GetLastStatement()
119120
return token.GetRequiredAncestor<TExecutableStatementSyntax>();
120121
}
121122

122-
public bool CreateAsyncMethod()
123+
public bool ContainsAwaitExpression()
123124
{
124125
_createAsyncMethod ??= CreateAsyncMethodWorker();
125126
return _createAsyncMethod.Value;
126127

127128
bool CreateAsyncMethodWorker()
128129
{
129-
var firstToken = GetFirstTokenInSelection();
130-
var lastToken = GetLastTokenInSelection();
131-
var syntaxFacts = SemanticDocument.GetRequiredLanguageService<ISyntaxFactsService>();
130+
var firstToken = this.GetFirstTokenInSelection();
131+
var lastToken = this.GetLastTokenInSelection();
132+
var span = TextSpan.FromBounds(firstToken.SpanStart, lastToken.Span.End);
132133

133-
for (var currentToken = firstToken;
134-
currentToken.Span.End < lastToken.SpanStart;
135-
currentToken = currentToken.GetNextToken())
134+
using var _ = ArrayBuilder<SyntaxNode>.GetInstance(out var stack);
135+
stack.Push(this.GetContainingScope());
136+
137+
var syntaxFacts = this.SemanticDocument.GetRequiredLanguageService<ISyntaxFactsService>();
138+
139+
while (stack.TryPop(out var current))
136140
{
137-
// [|
138-
// async () => await ....
139-
// |]
140-
//
141-
// for the case above, even if the selection contains "await", it doesn't belong to the enclosing block
142-
// which extract method is applied to
143-
if (syntaxFacts.IsAwaitKeyword(currentToken)
144-
&& !UnderAnonymousOrLocalMethod(currentToken, firstToken, lastToken))
145-
{
141+
// Don't dive into lambdas and local functions. They reset the async/await context.
142+
if (syntaxFacts.IsAnonymousOrLocalFunction(current))
143+
continue;
144+
145+
if (syntaxFacts.IsAwaitExpression(current))
146146
return true;
147+
148+
// Only dive into child nodes within the span being extracted.
149+
foreach (var childNode in current.ChildNodes())
150+
{
151+
if (childNode.Span.OverlapsWith(span))
152+
stack.Push(childNode);
147153
}
148154
}
149155

0 commit comments

Comments
 (0)