<commit_before>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Analyzer.CSharp.Utilities.Lightup;
using Analyzer.Utilities;
using Analyzer.Utilities.Lightup;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.NetCore.Analyzers.InteropServices;

namespace Microsoft.NetCore.CSharp.Analyzers.InteropServices
{
    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    public sealed class CSharpDynamicInterfaceCastableImplementationFixer : DynamicInterfaceCastableImplementationFixer
    {
        protected override async Task<Document> ImplementInterfacesOnDynamicCastableImplementationAsync(
            SyntaxNode root,
            SyntaxNode declaration,
            Document document,
            SyntaxGenerator generator,
            CancellationToken cancellationToken)
        {
            var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
            var type = (INamedTypeSymbol)model.GetDeclaredSymbol(declaration, cancellationToken);

            var defaultMethodBodyStatements = generator.DefaultMethodBody(model.Compilation).ToArray();
            var generatedMembers = new List<SyntaxNode>();
            foreach (var iface in type.AllInterfaces)
            {
                foreach (var member in iface.GetMembers())
                {
                    if (member.IsAbstract && type.FindImplementationForInterfaceMember(member) is null)
                    {
                        var implementation = member.Kind switch
                        {
                            SymbolKind.Method => GenerateMethodImplementation((IMethodSymbol)member),
                            SymbolKind.Property => GeneratePropertyImplementation((IPropertySymbol)member),
                            SymbolKind.Event => GenerateEventImplementation((IEventSymbol)member, generator, defaultMethodBodyStatements),
                            _ => null
                        };
                        if (implementation is not null)
                        {
                            generatedMembers.Add(generator.AsPrivateInterfaceImplementation(
                                implementation,
                                generator.NameExpression(member.ContainingType)));
                        }
                    }
                }
            }

            // Explicitly use the C# syntax APIs to work around https://github.com/dotnet/roslyn/issues/53605
            var typeDeclaration = (TypeDeclarationSyntax)declaration;
            typeDeclaration = typeDeclaration.AddMembers(generatedMembers.Cast<MemberDeclarationSyntax>().ToArray());

            return document.WithSyntaxRoot(root.ReplaceNode(declaration, typeDeclaration));

            SyntaxNode? GenerateMethodImplementation(IMethodSymbol method)
            {
                // Method with associated symbols are not standalone methods that a user would write in C#.
                // They're methods like property or event accessors, which are covered in the following local functions.
                if (method.AssociatedSymbol is not null)
                {
                    return null;
                }
                var methodDecl = generator.MethodDeclaration(method);
                methodDecl = generator.WithModifiers(methodDecl, generator.GetModifiers(methodDecl).WithIsAbstract(false));
                return generator.WithStatements(methodDecl, defaultMethodBodyStatements);
            }

            SyntaxNode GeneratePropertyImplementation(IPropertySymbol property)
            {
                var propertyDecl = property.IsIndexer
                    ? generator.IndexerDeclaration(property)
                    : generator.PropertyDeclaration(property);
                propertyDecl = generator.WithModifiers(propertyDecl, generator.GetModifiers(propertyDecl).WithIsAbstract(false));

                // Remove default property accessors.
                propertyDecl = generator.WithAccessorDeclarations(propertyDecl);

                if (property.GetMethod is not null
                    && model.Compilation.IsSymbolAccessibleWithin(property.GetMethod, type))
                {
                    propertyDecl = generator.WithGetAccessorStatements(propertyDecl, defaultMethodBodyStatements);
                }
                if (property.SetMethod is not null
                    && model.Compilation.IsSymbolAccessibleWithin(property.SetMethod, type))
                {
                    propertyDecl = AddSetAccessor(property, propertyDecl, generator, defaultMethodBodyStatements, includeAccessibility: false);
                }

                return propertyDecl;
            }
        }

        private static SyntaxNode AddSetAccessor(
            IPropertySymbol property,
            SyntaxNode declaration,
            SyntaxGenerator generator,
            SyntaxNode[] defaultMethodBodyStatements,
            bool includeAccessibility)
        {
            if (!property.SetMethod.IsInitOnly())
            {
                return generator.WithSetAccessorStatements(declaration, defaultMethodBodyStatements);
            }

            var setAccessorAccessibility = includeAccessibility && property.DeclaredAccessibility != property.SetMethod.DeclaredAccessibility
                ? property.SetMethod.DeclaredAccessibility
                : Accessibility.NotApplicable;

            var setAccessor = (AccessorDeclarationSyntax)generator.SetAccessorDeclaration(setAccessorAccessibility, defaultMethodBodyStatements);

            var propDecl = (PropertyDeclarationSyntax)declaration;

            SyntaxNode? oldInitAccessor = null;

            foreach (var accessor in propDecl.AccessorList.Accessors)
            {
                if (accessor.IsKind(SyntaxKindEx.InitAccessorDeclaration))
                {
                    oldInitAccessor = accessor;
                    break;
                }
            }

            if (oldInitAccessor is not null)
            {
                propDecl = propDecl.WithAccessorList(propDecl.AccessorList.RemoveNode(oldInitAccessor, SyntaxRemoveOptions.KeepNoTrivia));
            }

            return propDecl.WithAccessorList(propDecl.AccessorList.AddAccessors(
                SyntaxFactory.AccessorDeclaration(
                        SyntaxKindEx.InitAccessorDeclaration,
                        setAccessor.AttributeLists,
                        setAccessor.Modifiers,
                        SyntaxFactory.Token(SyntaxKindEx.InitKeyword),
                        setAccessor.Body,
                        setAccessor.ExpressionBody,
                        setAccessor.SemicolonToken)));
        }

        private static SyntaxNode GenerateEventImplementation(
            IEventSymbol evt,
            SyntaxGenerator generator,
            SyntaxNode[] defaultMethodBodyStatements)
        {
            var eventDecl = generator.CustomEventDeclaration(evt);
            eventDecl = generator.WithModifiers(eventDecl, generator.GetModifiers(eventDecl).WithIsAbstract(false));

            // Explicitly use the C# syntax APIs to work around https://github.com/dotnet/roslyn/issues/53649
            var eventDeclaration = (EventDeclarationSyntax)eventDecl;

            return eventDeclaration.WithAccessorList(
                SyntaxFactory.AccessorList(
                    SyntaxFactory.List(
                new[]
                {
                        generator.WithStatements(generator.GetAccessor(eventDecl, DeclarationKind.AddAccessor), defaultMethodBodyStatements),
                        generator.WithStatements(generator.GetAccessor(eventDecl, DeclarationKind.RemoveAccessor), defaultMethodBodyStatements),
                })));
        }

        protected override async Task<Document> MakeMemberDeclaredOnImplementationTypeStaticAsync(SyntaxNode declaration, Document document, CancellationToken cancellationToken)
        {
            var root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
            var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
            var generator = editor.Generator;<commit_msg>Address feedback<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Analyzer.CSharp.Utilities.Lightup;
using Analyzer.Utilities;
using Analyzer.Utilities.Lightup;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.NetCore.Analyzers.InteropServices;

namespace Microsoft.NetCore.CSharp.Analyzers.InteropServices
{
    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    public sealed class CSharpDynamicInterfaceCastableImplementationFixer : DynamicInterfaceCastableImplementationFixer
    {
        protected override async Task<Document> ImplementInterfacesOnDynamicCastableImplementationAsync(
            SyntaxNode root,
            SyntaxNode declaration,
            Document document,
            SyntaxGenerator generator,
            CancellationToken cancellationToken)
        {
            var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
            var type = (INamedTypeSymbol)model.GetDeclaredSymbol(declaration, cancellationToken);

            var defaultMethodBodyStatements = generator.DefaultMethodBody(model.Compilation).ToArray();
            var generatedMembers = new List<SyntaxNode>();
            foreach (var iface in type.AllInterfaces)
            {
                foreach (var member in iface.GetMembers())
                {
                    if (member.IsAbstract && type.FindImplementationForInterfaceMember(member) is null)
                    {
                        var implementation = member.Kind switch
                        {
                            SymbolKind.Method => GenerateMethodImplementation((IMethodSymbol)member),
                            SymbolKind.Property => GeneratePropertyImplementation((IPropertySymbol)member),
                            SymbolKind.Event => GenerateEventImplementation((IEventSymbol)member, generator, defaultMethodBodyStatements),
                            _ => null
                        };
                        if (implementation is not null)
                        {
                            generatedMembers.Add(generator.AsPrivateInterfaceImplementation(
                                implementation,
                                generator.NameExpression(member.ContainingType)));
                        }
                    }
                }
            }

            // Explicitly use the C# syntax APIs to work around https://github.com/dotnet/roslyn/issues/53605
            var typeDeclaration = (TypeDeclarationSyntax)declaration;
            typeDeclaration = typeDeclaration.AddMembers(generatedMembers.Cast<MemberDeclarationSyntax>().ToArray());

            return document.WithSyntaxRoot(root.ReplaceNode(declaration, typeDeclaration));

            SyntaxNode? GenerateMethodImplementation(IMethodSymbol method)
            {
                // Method with associated symbols are not standalone methods that a user would write in C#.
                // They're methods like property or event accessors, which are covered in the following local functions.
                if (method.AssociatedSymbol is not null)
                {
                    return null;
                }
                var methodDeclaration = generator.MethodDeclaration(method);
                methodDeclaration = generator.WithModifiers(methodDeclaration, generator.GetModifiers(methodDeclaration).WithIsAbstract(false));
                return generator.WithStatements(methodDeclaration, defaultMethodBodyStatements);
            }

            SyntaxNode GeneratePropertyImplementation(IPropertySymbol property)
            {
                var propertyDeclaration = property.IsIndexer
                    ? generator.IndexerDeclaration(property)
                    : generator.PropertyDeclaration(property);
                propertyDeclaration = generator.WithModifiers(propertyDeclaration, generator.GetModifiers(propertyDeclaration).WithIsAbstract(false));

                // Remove default property accessors.
                propertyDeclaration = generator.WithAccessorDeclarations(propertyDeclaration);

                if (property.GetMethod is not null
                    && model.Compilation.IsSymbolAccessibleWithin(property.GetMethod, type))
                {
                    propertyDeclaration = generator.WithGetAccessorStatements(propertyDeclaration, defaultMethodBodyStatements);
                }
                if (property.SetMethod is not null
                    && model.Compilation.IsSymbolAccessibleWithin(property.SetMethod, type))
                {
                    propertyDeclaration = AddSetAccessor(property, propertyDeclaration, generator, defaultMethodBodyStatements, includeAccessibility: false);
                }

                return propertyDeclaration;
            }
        }

        private static SyntaxNode AddSetAccessor(
            IPropertySymbol property,
            SyntaxNode declaration,
            SyntaxGenerator generator,
            SyntaxNode[] defaultMethodBodyStatements,
            bool includeAccessibility)
        {
            if (!property.SetMethod.IsInitOnly())
            {
                return generator.WithSetAccessorStatements(declaration, defaultMethodBodyStatements);
            }

            var setAccessorAccessibility = includeAccessibility && property.DeclaredAccessibility != property.SetMethod.DeclaredAccessibility
                ? property.SetMethod.DeclaredAccessibility
                : Accessibility.NotApplicable;

            var setAccessor = (AccessorDeclarationSyntax)generator.SetAccessorDeclaration(setAccessorAccessibility, defaultMethodBodyStatements);

            var propertyDeclaration = (PropertyDeclarationSyntax)declaration;

            SyntaxNode? oldInitAccessor = null;

            foreach (var accessor in propDecl.AccessorList.Accessors)
            {
                if (accessor.IsKind(SyntaxKindEx.InitAccessorDeclaration))
                {
                    oldInitAccessor = accessor;
                    break;
                }
            }

            if (oldInitAccessor is not null)
            {
                propertyDeclaration = propertyDeclaration.WithAccessorList(propertyDeclaration.AccessorList.RemoveNode(oldInitAccessor, SyntaxRemoveOptions.KeepNoTrivia));
            }

            return propertyDeclaration.WithAccessorList(propDecl.AccessorList.AddAccessors(
                SyntaxFactory.AccessorDeclaration(
                        SyntaxKindEx.InitAccessorDeclaration,
                        setAccessor.AttributeLists,
                        setAccessor.Modifiers,
                        SyntaxFactory.Token(SyntaxKindEx.InitKeyword),
                        setAccessor.Body,
                        setAccessor.ExpressionBody,
                        setAccessor.SemicolonToken)));
        }

        private static SyntaxNode GenerateEventImplementation(
            IEventSymbol evt,
            SyntaxGenerator generator,
            SyntaxNode[] defaultMethodBodyStatements)
        {
            var eventDeclaration = generator.CustomEventDeclaration(evt);
            eventDeclaration = generator.WithModifiers(eventDeclaration, generator.GetModifiers(eventDeclaration).WithIsAbstract(false));

            // Explicitly use the C# syntax APIs to work around https://github.com/dotnet/roslyn/issues/53649
            return ((EventDeclarationSyntax)eventDeclaration).WithAccessorList(
                SyntaxFactory.AccessorList(
                    SyntaxFactory.List(
                new[]
                {
                        generator.WithStatements(generator.GetAccessor(eventDeclaration, DeclarationKind.AddAccessor), defaultMethodBodyStatements),
                        generator.WithStatements(generator.GetAccessor(eventDeclaration, DeclarationKind.RemoveAccessor), defaultMethodBodyStatements),
                })));
        }

        protected override async Task<Document> MakeMemberDeclaredOnImplementationTypeStaticAsync(SyntaxNode declaration, Document document, CancellationToken cancellationToken)
        {
            var root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
            var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
            var generator = editor.Generator;
{'fraction_non_alphanumeric': 0.06351763419826247, 'fraction_numerical': 0.0011506817789540304, 'mean_word_length': 2.200515558828945, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 19, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3234905', 'n_tokens_mistral': 3810, 'n_tokens_neox': 3595, 'n_words': 721}