@@ -64,7 +64,8 @@ private sealed class NestedAnalyzer(
6464 INamedTypeSymbol cancellationTokenType ,
6565 INamedTypeSymbol [ ] fixedTaskTypes ,
6666 INamedTypeSymbol [ ] genericTaskTypes ,
67- INamedTypeSymbol asyncMethodBuilderAttributeType )
67+ INamedTypeSymbol asyncMethodBuilderAttributeType ,
68+ INamedTypeSymbol ? jsInvokableAttributeType )
6869 {
6970 #region Private Data Members
7071
@@ -73,6 +74,7 @@ private sealed class NestedAnalyzer(
7374 private readonly INamedTypeSymbol [ ] fixedTaskTypes = fixedTaskTypes ;
7475 private readonly INamedTypeSymbol [ ] genericTaskTypes = genericTaskTypes ;
7576 private readonly INamedTypeSymbol asyncMethodBuilderAttributeType = asyncMethodBuilderAttributeType ;
77+ private readonly INamedTypeSymbol ? jsInvokableAttributeType = jsInvokableAttributeType ;
7678 private readonly ConcurrentDictionary < ( ITypeSymbol Source , ITypeSymbol Target ) , bool > canAccessCancellationTokenProperty
7779 = new ( TypeAccessComparer . Instance ) ;
7880
@@ -89,9 +91,13 @@ private sealed class NestedAnalyzer(
8991 INamedTypeSymbol ? valueTask1Type = compilation . GetTypeByMetadataName ( typeof ( ValueTask < > ) . FullName ) ;
9092 INamedTypeSymbol ? asyncMethodBuilderAttributeType = compilation . GetTypeByMetadataName ( typeof ( AsyncMethodBuilderAttribute ) . FullName ) ;
9193
94+ // This may be null if the project doesn't reference Microsoft.JSInterop.
95+ // That's fine; the HasJSInvokableAttribute check will simply be skipped.
96+ INamedTypeSymbol ? jsInvokableAttributeType = compilation . GetTypeByMetadataName ( "Microsoft.JSInterop.JSInvokableAttribute" ) ;
97+
9298 NestedAnalyzer ? result = cancellationTokenType != null && taskType != null && task1Type != null
9399 && valueTaskType != null && valueTask1Type != null && asyncMethodBuilderAttributeType != null
94- ? new ( caller , cancellationTokenType , [ taskType , valueTaskType ] , [ task1Type , valueTask1Type ] , asyncMethodBuilderAttributeType )
100+ ? new ( caller , cancellationTokenType , [ taskType , valueTaskType ] , [ task1Type , valueTask1Type ] , asyncMethodBuilderAttributeType , jsInvokableAttributeType )
95101 : null ;
96102 return result ;
97103 }
@@ -134,7 +140,8 @@ public void HandleNamedTypeSymbol(SymbolAnalysisContext context)
134140 && method . ExplicitInterfaceImplementations . IsDefaultOrEmpty
135141 && ! IsImplicitInterfaceImplementation ( method )
136142 && ! settings . IsUnitTestMethod ( method )
137- && ! IsAssemblyEntryPoint ( method , ref context ) )
143+ && ! IsAssemblyEntryPoint ( method , ref context )
144+ && ! HasJSInvokableAttribute ( method ) )
138145 {
139146 eligibleMethods . Add ( method ) ;
140147
@@ -263,6 +270,15 @@ private bool IsImplicitInterfaceImplementation(IMethodSymbol method)
263270 return result ;
264271 }
265272
273+ private bool HasJSInvokableAttribute ( IMethodSymbol method )
274+ {
275+ // JSInvokable methods in Blazor must be public and are called from JavaScript.
276+ // JavaScript can't pass a CancellationToken.
277+ bool result = this . jsInvokableAttributeType != null
278+ && method . GetAttributes ( ) . Any ( attr => SymbolComparer . Equals ( attr . AttributeClass , this . jsInvokableAttributeType ) ) ;
279+ return result ;
280+ }
281+
266282 private static bool IsAssemblyEntryPoint ( IMethodSymbol method , ref SymbolAnalysisContext context )
267283 {
268284 bool result = method . IsStatic
0 commit comments