提交 499f27ad 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #18307 from sharwell/async-local-function

Fix support for local functions in "Make Method Asynchronous"
......@@ -599,5 +599,180 @@ async ValueTask<int> TestAsync()
}";
await TestInRegularAndScriptAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(14133, "https://github.com/dotnet/roslyn/issues/14133")]
public async Task AddAsyncInLocalFunction()
{
await TestInRegularAndScriptAsync(
@"using System.Threading.Tasks;
class C
{
public void M1()
{
void M2()
{
[|await M3Async();|]
}
}
async Task<int> M3Async()
{
return 1;
}
}",
@"using System.Threading.Tasks;
class C
{
public void M1()
{
async Task M2Async()
{
await M3Async();
}
}
async Task<int> M3Async()
{
return 1;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(14133, "https://github.com/dotnet/roslyn/issues/14133")]
public async Task AddAsyncInLocalFunctionKeepVoidReturn()
{
await TestInRegularAndScriptAsync(
@"using System.Threading.Tasks;
class C
{
public void M1()
{
void M2()
{
[|await M3Async();|]
}
}
async Task<int> M3Async()
{
return 1;
}
}",
@"using System.Threading.Tasks;
class C
{
public void M1()
{
async void M2Async()
{
await M3Async();
}
}
async Task<int> M3Async()
{
return 1;
}
}",
index: 1);
}
[Theory]
[InlineData(0, "Task")]
[InlineData(1, "void", Skip = "https://github.com/dotnet/roslyn/issues/18396")]
[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")]
public async Task AddAsyncInLocalFunctionKeepsTrivia(int codeFixIndex, string expectedReturn)
{
await TestInRegularAndScriptAsync(
@"using System.Threading.Tasks;
class C
{
public void M1()
{
// Leading trivia
/*1*/ void /*2*/ M2/*3*/() /*4*/
{
[|await M3Async();|]
}
}
async Task<int> M3Async()
{
return 1;
}
}",
$@"using System.Threading.Tasks;
class C
{{
public void M1()
{{
// Leading trivia
/*1*/ async {expectedReturn} /*2*/ M2Async/*3*/() /*4*/
{{
await M3Async();
}}
}}
async Task<int> M3Async()
{{
return 1;
}}
}}",
index: codeFixIndex,
ignoreTrivia: false);
}
[Theory]
[InlineData("", 0, "Task")]
[InlineData("", 1, "void", Skip = "https://github.com/dotnet/roslyn/issues/18396")]
[InlineData("public", 0, "Task")]
[InlineData("public", 1, "void")]
[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")]
public async Task AddAsyncKeepsTrivia(string modifiers, int codeFixIndex, string expectedReturn)
{
await TestInRegularAndScriptAsync(
$@"using System.Threading.Tasks;
class C
{{
// Leading trivia
{modifiers}/*1*/ void /*2*/ M2/*3*/() /*4*/
{{
[|await M3Async();|]
}}
async Task<int> M3Async()
{{
return 1;
}}
}}",
$@"using System.Threading.Tasks;
class C
{{
// Leading trivia
{modifiers}/*1*/ async {expectedReturn} /*2*/ M2Async/*3*/() /*4*/
{{
await M3Async();
}}
async Task<int> M3Async()
{{
return 1;
}}
}}",
index: codeFixIndex,
ignoreTrivia: false);
}
}
}
\ No newline at end of file
......@@ -548,5 +548,112 @@ async void BarAsync()
}
}", ignoreTrivia: false);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(14133, "https://github.com/dotnet/roslyn/issues/14133")]
public async Task RemoveAsyncInLocalFunction()
{
await TestInRegularAndScriptAsync(
@"using System.Threading.Tasks;
class C
{
public void M1()
{
async Task [|M2Async|]()
{
}
}
}",
@"using System.Threading.Tasks;
class C
{
public void M1()
{
void M2()
{
}
}
}");
}
[Theory]
[InlineData("Task<C>", "C")]
[InlineData("Task<int>", "int")]
[InlineData("Task", "void")]
[InlineData("void", "void")]
[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")]
public async Task RemoveAsyncInLocalFunctionKeepsTrivia(string asyncReturn, string expectedReturn)
{
await TestInRegularAndScriptAsync(
$@"using System;
using System.Threading.Tasks;
class C
{{
public void M1()
{{
// Leading trivia
/*1*/ async {asyncReturn} /*2*/ [|M2Async|]/*3*/() /*4*/
{{
throw new NotImplementedException();
}}
}}
}}",
$@"using System;
using System.Threading.Tasks;
class C
{{
public void M1()
{{
// Leading trivia
/*1*/ {expectedReturn} /*2*/ M2/*3*/() /*4*/
{{
throw new NotImplementedException();
}}
}}
}}");
}
[Theory]
[InlineData("", "Task<C>", "C")]
[InlineData("", "Task<int>", "int")]
[InlineData("", "Task", "void")]
[InlineData("", "void", "void")]
[InlineData("public", "Task<C>", "C")]
[InlineData("public", "Task<int>", "int")]
[InlineData("public", "Task", "void")]
[InlineData("public", "void", "void")]
[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
[WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")]
public async Task RemoveAsyncKeepsTrivia(string modifiers, string asyncReturn, string expectedReturn)
{
await TestInRegularAndScriptAsync(
$@"using System;
using System.Threading.Tasks;
class C
{{
// Leading trivia
{modifiers}/*1*/ async {asyncReturn} /*2*/ [|M2Async|]/*3*/() /*4*/
{{
throw new NotImplementedException();
}}
}}",
$@"using System;
using System.Threading.Tasks;
class C
{{
// Leading trivia
{modifiers}/*1*/ {expectedReturn} /*2*/ M2/*3*/() /*4*/
{{
throw new NotImplementedException();
}}
}}");
}
}
}
\ No newline at end of file
......@@ -33,10 +33,8 @@ protected override string GetMakeAsyncVoidFunctionResource()
return CSharpFeaturesResources.Make_method_async_remain_void;
}
protected override bool IsMethodOrAnonymousFunction(SyntaxNode node)
{
return node.IsKind(SyntaxKind.MethodDeclaration) || node.IsAnyLambdaOrAnonymousMethod();
}
protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node)
=> node.IsAsyncSupportingFunctionSyntax();
protected override SyntaxNode AddAsyncTokenAndFixReturnType(
bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node,
......@@ -45,6 +43,7 @@ protected override bool IsMethodOrAnonymousFunction(SyntaxNode node)
switch (node)
{
case MethodDeclarationSyntax method: return FixMethod(keepVoid, methodSymbolOpt, method, taskType, taskOfTType, valueTaskOfTType);
case LocalFunctionStatementSyntax localFunction: return FixLocalFunction(keepVoid, methodSymbolOpt, localFunction, taskType, taskOfTType, valueTaskOfTType);
case AnonymousMethodExpressionSyntax method: return FixAnonymousMethod(method);
case ParenthesizedLambdaExpressionSyntax lambda: return FixParenthesizedLambda(lambda);
case SimpleLambdaExpressionSyntax lambda: return FixSimpleLambda(lambda);
......@@ -56,7 +55,25 @@ protected override bool IsMethodOrAnonymousFunction(SyntaxNode node)
bool keepVoid, IMethodSymbol methodSymbol, MethodDeclarationSyntax method,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType)
{
var newReturnType = method.ReturnType;
var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, method.ReturnType, taskType, taskOfTType, valueTaskOfTType);
var newModifiers = AddAsyncModifierWithCorrectedTrivia(method.Modifiers, ref newReturnType);
return method.WithReturnType(newReturnType).WithModifiers(newModifiers);
}
private SyntaxNode FixLocalFunction(
bool keepVoid, IMethodSymbol methodSymbol, LocalFunctionStatementSyntax localFunction,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType)
{
var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, localFunction.ReturnType, taskType, taskOfTType, valueTaskOfTType);
var newModifiers = AddAsyncModifierWithCorrectedTrivia(localFunction.Modifiers, ref newReturnType);
return localFunction.WithReturnType(newReturnType).WithModifiers(newModifiers);
}
private static TypeSyntax FixMethodReturnType(
bool keepVoid, IMethodSymbol methodSymbol, TypeSyntax returnType,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType)
{
var newReturnType = returnType;
if (methodSymbol.ReturnsVoid)
{
......@@ -75,8 +92,18 @@ protected override bool IsMethodOrAnonymousFunction(SyntaxNode node)
}
}
var newModifiers = method.Modifiers.Add(s_asyncToken);
return method.WithReturnType(newReturnType).WithModifiers(newModifiers);
return newReturnType.WithTriviaFrom(returnType);
}
private static SyntaxTokenList AddAsyncModifierWithCorrectedTrivia(SyntaxTokenList modifiers, ref TypeSyntax newReturnType)
{
if (modifiers.Any())
return modifiers.Add(s_asyncToken);
// Move the leading trivia from the return type to the new modifiers list.
SyntaxTokenList result = SyntaxFactory.TokenList(s_asyncToken.WithLeadingTrivia(newReturnType.GetLeadingTrivia()));
newReturnType = newReturnType.WithoutLeadingTrivia();
return result;
}
private SyntaxNode FixParenthesizedLambda(ParenthesizedLambdaExpressionSyntax lambda)
......
......@@ -17,16 +17,15 @@ internal class CSharpMakeMethodSynchronousCodeFixProvider : AbstractMakeMethodSy
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(CS1998);
protected override bool IsMethodOrAnonymousFunction(SyntaxNode node)
{
return node.IsKind(SyntaxKind.MethodDeclaration) || node.IsAnyLambdaOrAnonymousMethod();
}
protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node)
=> node.IsAsyncSupportingFunctionSyntax();
protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbolOpt, SyntaxNode node, ITypeSymbol taskType, ITypeSymbol taskOfTType)
{
switch (node)
{
case MethodDeclarationSyntax method: return FixMethod(methodSymbolOpt, method, taskType, taskOfTType);
case LocalFunctionStatementSyntax localFunction: return FixLocalFunction(methodSymbolOpt, localFunction, taskType, taskOfTType);
case AnonymousMethodExpressionSyntax method: return FixAnonymousMethod(method);
case ParenthesizedLambdaExpressionSyntax lambda: return FixParenthesizedLambda(lambda);
case SimpleLambdaExpressionSyntax lambda: return FixSimpleLambda(lambda);
......@@ -36,47 +35,66 @@ protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol met
private SyntaxNode FixMethod(IMethodSymbol methodSymbol, MethodDeclarationSyntax method, ITypeSymbol taskType, ITypeSymbol taskOfTType)
{
var newReturnType = method.ReturnType;
var newReturnType = FixMethodReturnType(methodSymbol, method.ReturnType, taskType, taskOfTType);
var newModifiers = FixMethodModifiers(method.Modifiers, ref newReturnType);
return method.WithReturnType(newReturnType).WithModifiers(newModifiers);
}
private SyntaxNode FixLocalFunction(IMethodSymbol methodSymbol, LocalFunctionStatementSyntax localFunction, ITypeSymbol taskType, ITypeSymbol taskOfTType)
{
var newReturnType = FixMethodReturnType(methodSymbol, localFunction.ReturnType, taskType, taskOfTType);
var newModifiers = FixMethodModifiers(localFunction.Modifiers, ref newReturnType);
return localFunction.WithReturnType(newReturnType).WithModifiers(newModifiers);
}
private static TypeSyntax FixMethodReturnType(IMethodSymbol methodSymbol, TypeSyntax returnType, ITypeSymbol taskType, ITypeSymbol taskOfTType)
{
var newReturnType = returnType;
// If the return type is Task<T>, then make the new return type "T".
// If it is Task, then make the new return type "void".
if (methodSymbol.ReturnType.OriginalDefinition.Equals(taskType))
{
newReturnType = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)).WithTriviaFrom(method.ReturnType);
newReturnType = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)).WithTriviaFrom(returnType);
}
else if (methodSymbol.ReturnType.OriginalDefinition.Equals(taskOfTType))
{
newReturnType = methodSymbol.ReturnType.GetTypeArguments()[0].GenerateTypeSyntax().WithTriviaFrom(method.ReturnType);
newReturnType = methodSymbol.ReturnType.GetTypeArguments()[0].GenerateTypeSyntax().WithTriviaFrom(returnType);
}
var asyncTokenIndex = method.Modifiers.IndexOf(SyntaxKind.AsyncKeyword);
return newReturnType;
}
private static SyntaxTokenList FixMethodModifiers(SyntaxTokenList modifiers, ref TypeSyntax newReturnType)
{
var asyncTokenIndex = modifiers.IndexOf(SyntaxKind.AsyncKeyword);
SyntaxTokenList newModifiers;
if (asyncTokenIndex == 0)
{
// Have to move the trivia on teh async token appropriately.
var asyncLeadingTrivia = method.Modifiers[0].LeadingTrivia;
// Have to move the trivia on the async token appropriately.
var asyncLeadingTrivia = modifiers[0].LeadingTrivia;
if (method.Modifiers.Count > 1)
if (modifiers.Count > 1)
{
// Move the trivia to the next modifier;
newModifiers = method.Modifiers.Replace(
method.Modifiers[1],
method.Modifiers[1].WithPrependedLeadingTrivia(asyncLeadingTrivia));
newModifiers = modifiers.Replace(
modifiers[1],
modifiers[1].WithPrependedLeadingTrivia(asyncLeadingTrivia));
newModifiers = newModifiers.RemoveAt(0);
}
else
{
// move it to the return type.
newModifiers = method.Modifiers.RemoveAt(0);
newModifiers = modifiers.RemoveAt(0);
newReturnType = newReturnType.WithPrependedLeadingTrivia(asyncLeadingTrivia);
}
}
else
{
newModifiers = method.Modifiers.RemoveAt(asyncTokenIndex);
newModifiers = modifiers.RemoveAt(asyncTokenIndex);
}
return method.WithReturnType(newReturnType).WithModifiers(newModifiers);
return newModifiers;
}
private SyntaxNode FixParenthesizedLambda(ParenthesizedLambdaExpressionSyntax lambda)
......
......@@ -6,7 +6,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
......@@ -15,7 +14,7 @@ namespace Microsoft.CodeAnalysis.MakeMethodAsynchronous
{
internal abstract class AbstractMakeMethodAsynchronousCodeFixProvider : CodeFixProvider
{
protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node);
protected abstract bool IsAsyncSupportingFunctionSyntax(SyntaxNode node);
protected abstract SyntaxNode AddAsyncTokenAndFixReturnType(
bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType);
......@@ -51,8 +50,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
// If it's a void returning method, offer to keep the void return type, or convert to
// a Task return type.
if (symbol?.MethodKind == MethodKind.Ordinary &&
symbol.ReturnsVoid)
bool isOrdinaryOrLocalFunction = symbol.IsOrdinaryMethodOrLocalFunction();
if (isOrdinaryOrLocalFunction && symbol.ReturnsVoid)
{
context.RegisterCodeFix(
new MyCodeAction(GetMakeAsyncTaskFunctionResource(), c => FixNodeAsync(
......@@ -100,8 +99,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol;
if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary &&
!methodSymbolOpt.Name.EndsWith(AsyncSuffix))
bool isOrdinaryOrLocalFunction = methodSymbolOpt.IsOrdinaryMethodOrLocalFunction();
if (isOrdinaryOrLocalFunction && !methodSymbolOpt.Name.EndsWith(AsyncSuffix))
{
return await RenameThenAddAsyncTokenAsync(
keepVoid, document, node, methodSymbolOpt, cancellationToken).ConfigureAwait(false);
......@@ -116,7 +115,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
private SyntaxNode GetContainingFunction(Diagnostic diagnostic, CancellationToken cancellationToken)
{
var token = diagnostic.Location.FindToken(cancellationToken);
var node = token.GetAncestor(IsMethodOrAnonymousFunction);
var node = token.GetAncestor(IsAsyncSupportingFunctionSyntax);
return node;
}
......@@ -152,8 +151,7 @@ private SyntaxNode GetContainingFunction(Diagnostic diagnostic, CancellationToke
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var (taskType, taskOfTType, valueTaskOfTType) = GetTaskTypes(compilation);
var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType, valueTaskOfTType)
.WithAdditionalAnnotations(Formatter.Annotation);
var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType, valueTaskOfTType);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newRoot = root.ReplaceNode(node, newNode);
......
......@@ -22,7 +22,7 @@ internal abstract class AbstractMakeMethodSynchronousCodeFixProvider : CodeFixPr
{
public static readonly string EquivalenceKey = FeaturesResources.Make_method_synchronous;
protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node);
protected abstract bool IsAsyncSupportingFunctionSyntax(SyntaxNode node);
protected abstract SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbolOpt, SyntaxNode node, ITypeSymbol taskType, ITypeSymbol taskOfTType);
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
......@@ -41,7 +41,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
{
var token = diagnostic.Location.FindToken(cancellationToken);
var node = token.GetAncestor(IsMethodOrAnonymousFunction);
var node = token.GetAncestor(IsAsyncSupportingFunctionSyntax);
// See if we're on an actual method declaration (otherwise we're on a lambda declaration).
// If we're on a method declaration, we'll get an IMethodSymbol back. In that case, check
......@@ -49,7 +49,8 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol;
if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary &&
bool isOrdinaryOrLocalFunction = methodSymbolOpt.IsOrdinaryMethodOrLocalFunction();
if (isOrdinaryOrLocalFunction &&
methodSymbolOpt.Name.Length > AsyncSuffix.Length &&
methodSymbolOpt.Name.EndsWith(AsyncSuffix))
{
......
......@@ -35,13 +35,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous
Return VBFeaturesResources.Make_Async_Sub
End Function
Protected Overrides Function IsMethodOrAnonymousFunction(node As SyntaxNode) As Boolean
Return node.IsKind(SyntaxKind.FunctionBlock) OrElse
node.IsKind(SyntaxKind.SubBlock) OrElse
node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) OrElse
node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineSubLambdaExpression)
Protected Overrides Function IsAsyncSupportingFunctionSyntax(node As SyntaxNode) As Boolean
Return node.IsAsyncSupportedFunctionSyntax()
End Function
Protected Overrides Function AddAsyncTokenAndFixReturnType(
......
......@@ -21,13 +21,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MakeMethodSynchronous
End Get
End Property
Protected Overrides Function IsMethodOrAnonymousFunction(node As SyntaxNode) As Boolean
Return node.IsKind(SyntaxKind.FunctionBlock) OrElse
node.IsKind(SyntaxKind.SubBlock) OrElse
node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) OrElse
node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineSubLambdaExpression)
Protected Overrides Function IsAsyncSupportingFunctionSyntax(node As SyntaxNode) As Boolean
Return node.IsAsyncSupportedFunctionSyntax()
End Function
Protected Overrides Function RemoveAsyncTokenAndFixReturnType(methodSymbolOpt As IMethodSymbol, node As SyntaxNode, taskType As ITypeSymbol, taskOfTType As ITypeSymbol) As SyntaxNode
......
......@@ -295,6 +295,13 @@ public static TNode ConvertToSingleLine<TNode>(this TNode node, bool useElasticT
return (TNode)rewriter.Visit(node);
}
public static bool IsAsyncSupportingFunctionSyntax(this SyntaxNode node)
{
return node.IsKind(SyntaxKind.MethodDeclaration)
|| node.IsAnyLambdaOrAnonymousMethod()
|| node.IsKind(SyntaxKind.LocalFunctionStatement);
}
public static bool IsAnyArgumentList(this SyntaxNode node)
{
return node.IsKind(SyntaxKind.ArgumentList) ||
......
......@@ -253,6 +253,17 @@ public static bool IsOrdinaryMethod(this ISymbol symbol)
return (symbol as IMethodSymbol)?.MethodKind == MethodKind.Ordinary;
}
public static bool IsOrdinaryMethodOrLocalFunction(this ISymbol symbol)
{
if (!(symbol is IMethodSymbol method))
{
return false;
}
return method.MethodKind == MethodKind.Ordinary
|| method.MethodKind == MethodKind.LocalFunction;
}
public static bool IsDelegateType(this ISymbol symbol)
{
return symbol is ITypeSymbol && ((ITypeSymbol)symbol).TypeKind == TypeKind.Delegate;
......
......@@ -188,6 +188,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Return Contract.FailWithReturn(Of SyntaxList(Of StatementSyntax))("unknown statements container!")
End Function
<Extension()>
Friend Function IsAsyncSupportedFunctionSyntax(node As SyntaxNode) As Boolean
Select Case node?.Kind()
Case _
SyntaxKind.FunctionBlock,
SyntaxKind.SubBlock,
SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression
Return True
End Select
Return False
End Function
<Extension()>
Friend Function IsMultiLineLambda(node As SyntaxNode) As Boolean
Return SyntaxFacts.IsMultiLineLambdaExpression(node.Kind())
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册