File: src\Analyzers\Core\CodeFixes\ImplementInterface\ImplementInterfaceGenerator_Method.cs
Web Access
Project: src\src\CodeStyle\Core\CodeFixes\Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.CodeStyle.Fixes)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
#if CODE_STYLE
using DeclarationModifiers = Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers;
#else
using DeclarationModifiers = Microsoft.CodeAnalysis.Editing.DeclarationModifiers;
#endif
 
namespace Microsoft.CodeAnalysis.ImplementInterface;
 
internal abstract partial class AbstractImplementInterfaceService
{
    private sealed partial class ImplementInterfaceGenerator
    {
        private ISymbol GenerateMethod(
            Compilation compilation,
            IMethodSymbol method,
            IMethodSymbol? conflictingMethod,
            Accessibility accessibility,
            DeclarationModifiers modifiers,
            bool generateAbstractly,
            bool useExplicitInterfaceSymbol,
            string memberName)
        {
            var syntaxFacts = Document.GetRequiredLanguageService<ISyntaxFactsService>();
 
            var updatedMethod = method.EnsureNonConflictingNames(State.ClassOrStructType, syntaxFacts);
 
            updatedMethod = updatedMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(
                State.ClassOrStructType,
                AttributesToRemove(compilation));
 
            return CodeGenerationSymbolFactory.CreateMethodSymbol(
                updatedMethod,
                accessibility: accessibility,
                modifiers: modifiers,
                explicitInterfaceImplementations: useExplicitInterfaceSymbol ? [updatedMethod] : default,
                name: memberName,
                statements: generateAbstractly ? default : [GenerateStatement(compilation, updatedMethod, conflictingMethod)]);
        }
 
        private SyntaxNode GenerateStatement(
            Compilation compilation, IMethodSymbol updatedMethod, IMethodSymbol? conflictingMethod)
        {
            var factory = Document.GetRequiredLanguageService<SyntaxGenerator>();
            if (ThroughMember != null)
                return factory.GenerateDelegateThroughMemberStatement(updatedMethod, ThroughMember);
 
            // Forward from the explicit method we're creating to the existing method it conflicts with if possible.
            if (CanForwardToConflictingMethod(compilation, updatedMethod, conflictingMethod))
            {
                var invocation = factory.InvocationExpression(
                    factory.MemberAccessExpression(
                        factory.ThisExpression(),
                        conflictingMethod.Name),
                    factory.CreateArguments(updatedMethod.Parameters));
 
                return updatedMethod.ReturnsVoid
                    ? factory.ExpressionStatement(invocation)
                    : factory.ReturnStatement(invocation);
            }
 
            return factory.CreateThrowNotImplementedStatement(compilation);
        }
 
        private static bool CanForwardToConflictingMethod(
            Compilation compilation, IMethodSymbol method, [NotNullWhen(true)] IMethodSymbol? conflictingMethod)
        {
            if (conflictingMethod is null)
                return false;
 
            if (method.Parameters.Length != conflictingMethod.Parameters.Length)
                return false;
 
            if (method.ReturnsVoid != conflictingMethod.ReturnsVoid)
                return false;
 
            if (method.Parameters.Zip(conflictingMethod.Parameters, (p1, p2) => (p1, p2)).Any(
                    t => compilation.ClassifyCommonConversion(t.p1.Type, t.p2.Type) is not { IsImplicit: true, Exists: true }))
            {
                return false;
            }
 
            if (method.ReturnsVoid)
                return true;
 
            return compilation.ClassifyCommonConversion(conflictingMethod.ReturnType, method.ReturnType) is { IsImplicit: true, Exists: true };
        }
    }
}