Filter list of candidate invocations for parsing in binding generator (#89226)
authorLayomi Akinrinade <laakinri@microsoft.com>
Thu, 20 Jul 2023 17:13:51 +0000 (10:13 -0700)
committerGitHub <noreply@github.com>
Thu, 20 Jul 2023 17:13:51 +0000 (10:13 -0700)
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/BinderInvocation.cs

index f49deca..7b40f19 100644 (file)
@@ -111,7 +111,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
         // Method group. BindConfiguration_T is its own method group.
         Bind = Bind_T | Bind_T_BinderOptions,
 
-        Any = Bind | BindConfiguration_T_path_BinderOptions,
+        BindConfiguration = BindConfiguration_T_path_BinderOptions,
+
+        Any = Bind | BindConfiguration,
     }
 
     /// <summary>
index eac828a..3029a8d 100644 (file)
@@ -1,10 +1,10 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Threading;
 using Microsoft.CodeAnalysis.CSharp.Syntax;
 using Microsoft.CodeAnalysis.Operations;
 using Microsoft.CodeAnalysis;
-using System.Threading;
 
 namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 {
@@ -15,8 +15,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
         public static BinderInvocation? Create(GeneratorSyntaxContext context, CancellationToken cancellationToken)
         {
-            if (context.Node is not InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax } invocationSyntax ||
-                context.SemanticModel.GetOperation(invocationSyntax, cancellationToken) is not IInvocationOperation operation)
+            if (!IsCandidateInvocationExpressionSyntax(context.Node, out InvocationExpressionSyntax? invocationSyntax) ||
+                context.SemanticModel.GetOperation(invocationSyntax, cancellationToken) is not IInvocationOperation operation ||
+                !IsCandidateInvocation(operation))
             {
                 return null;
             }
@@ -27,5 +28,72 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 Location = invocationSyntax.GetLocation()
             };
         }
+
+        private static bool IsCandidateInvocationExpressionSyntax(SyntaxNode node, out InvocationExpressionSyntax? invocationSyntax)
+        {
+            if (node is InvocationExpressionSyntax
+                {
+                    Expression: MemberAccessExpressionSyntax
+                    {
+                        Name.Identifier.ValueText: string memberName
+                    }
+                } syntax && IsCandidateBindingMethodName(memberName))
+            {
+                invocationSyntax = syntax;
+                return true;
+            }
+
+            invocationSyntax = null;
+            return false;
+
+            static bool IsCandidateBindingMethodName(string name) =>
+                IsCandidateMethodName_ConfigurationBinder(name) ||
+                IsCandidateMethodName_OptionsBuilderConfigurationExtensions(name) ||
+                IsValidMethodName_OptionsConfigurationServiceCollectionExtensions(name);
+        }
+
+        private static bool IsCandidateInvocation(IInvocationOperation operation)
+        {
+            if (operation.TargetMethod is not IMethodSymbol
+                {
+                    IsExtensionMethod: true,
+                    Name: string methodName,
+                    ContainingType: ITypeSymbol
+                    {
+                        Name: string containingTypeName,
+                        ContainingNamespace: INamespaceSymbol { } containingNamespace,
+                    } containingType
+                } method ||
+                containingNamespace.ToDisplayString() is not string containingNamespaceName)
+            {
+                return false;
+            }
+
+            return (containingTypeName) switch
+            {
+                "ConfigurationBinder" =>
+                    containingNamespaceName is "Microsoft.Extensions.Configuration" &&
+                    IsCandidateMethodName_ConfigurationBinder(methodName),
+                "OptionsBuilderConfigurationExtensions" =>
+                    containingNamespaceName is "Microsoft.Extensions.DependencyInjection" &&
+                    IsCandidateMethodName_OptionsBuilderConfigurationExtensions(methodName),
+                "OptionsConfigurationServiceCollectionExtensions" =>
+                    containingNamespaceName is "Microsoft.Extensions.DependencyInjection" &&
+                    IsValidMethodName_OptionsConfigurationServiceCollectionExtensions(methodName),
+                _ => false,
+            };
+        }
+
+        private static bool IsCandidateMethodName_ConfigurationBinder(string name) => name is
+            nameof(MethodsToGen_ConfigurationBinder.Bind) or
+            nameof(MethodsToGen_ConfigurationBinder.Get) or
+            nameof(MethodsToGen_ConfigurationBinder.GetValue);
+
+        private static bool IsCandidateMethodName_OptionsBuilderConfigurationExtensions(string name) => name is
+            nameof(MethodsToGen_Extensions_OptionsBuilder.Bind) or
+            nameof(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration);
+
+        private static bool IsValidMethodName_OptionsConfigurationServiceCollectionExtensions(string name) => name is
+            nameof(MethodsToGen_Extensions_ServiceCollection.Configure);
     }
 }