File: Microsoft.NetCore.Analyzers\Performance\CSharpRecommendCaseInsensitiveStringComparisonFixer.cs
Web Access
Project: ..\..\..\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.CSharp.NetAnalyzers\Microsoft.CodeAnalysis.CSharp.NetAnalyzers.csproj (Microsoft.CodeAnalysis.CSharp.NetAnalyzers)
// 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.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.NetCore.Analyzers.Performance;
 
namespace Microsoft.NetCore.CSharp.Analyzers.Performance
{
    using RCISCAnalyzer = RecommendCaseInsensitiveStringComparisonAnalyzer;
 
    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    public sealed class CSharpRecommendCaseInsensitiveStringComparisonFixer : RecommendCaseInsensitiveStringComparisonFixer
    {
        protected override IEnumerable<SyntaxNode> GetNewArgumentsForInvocation(SyntaxGenerator generator,
            string caseChangingApproachValue, IInvocationOperation mainInvocationOperation, INamedTypeSymbol stringComparisonType,
            string? leftOffendingMethod, string? rightOffendingMethod, out SyntaxNode? mainInvocationInstance)
        {
            InvocationExpressionSyntax invocationExpression = (InvocationExpressionSyntax)mainInvocationOperation.Syntax;
 
            mainInvocationInstance = null;
 
            if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccessExpression)
            {
                ExpressionSyntax internalExpression = memberAccessExpression.Expression;
                while (internalExpression is ParenthesizedExpressionSyntax parenthesizedExpression)
                {
                    internalExpression = parenthesizedExpression.Expression;
                }
 
                if (leftOffendingMethod != null &&
                    internalExpression is InvocationExpressionSyntax internalInvocationExpression &&
                    internalInvocationExpression.Expression is MemberAccessExpressionSyntax internalMemberAccessExpression &&
                    internalMemberAccessExpression.Name.Identifier.Text == leftOffendingMethod)
                {
                    // We know we have an instance invocation that is an offending method, retrieve just the instance
                    mainInvocationInstance = internalMemberAccessExpression.Expression;
                }
                else
                {
                    mainInvocationInstance = memberAccessExpression.Expression;
                }
            }
 
            List<SyntaxNode> arguments = new();
            bool isAnyArgumentNamed = false;
 
            foreach (IArgumentOperation arg in mainInvocationOperation.Arguments)
            {
                SyntaxNode newArgumentNode;
 
                // When accessing the main invocation operation arguments, the bottom operation is retrieved
                // so we need to go up until we find the actual argument syntax ancestor, and skip through any
                // parenthesized syntax nodes
                SyntaxNode actualArgumentNode = arg.Syntax;
                while (actualArgumentNode is not ArgumentSyntax)
                {
                    actualArgumentNode = actualArgumentNode.Parent!;
                }
 
                string? argumentName = ((ArgumentSyntax)actualArgumentNode).NameColon?.Name.Identifier.ValueText;
                isAnyArgumentNamed |= argumentName != null;
 
                // The arguments could be named and out of order, so we need to detect the string parameter
                // and remove the offending invocation if there is one
                if (rightOffendingMethod != null && arg.Parameter?.Type?.Name == StringTypeName)
                {
                    ExpressionSyntax? desiredExpression = null;
                    if (arg.Syntax is ArgumentSyntax argumentExpression)
                    {
                        desiredExpression = argumentExpression.Expression;
                        while (desiredExpression is ParenthesizedExpressionSyntax parenthesizedExpression)
                        {
                            desiredExpression = parenthesizedExpression.Expression;
                        }
                    }
                    else if (arg.Syntax is InvocationExpressionSyntax argumentInvocationExpression)
                    {
                        desiredExpression = argumentInvocationExpression;
                    }
 
                    if (desiredExpression is InvocationExpressionSyntax invocation &&
                        invocation.Expression is MemberAccessExpressionSyntax argumentMemberAccessExpression)
                    {
                        newArgumentNode = argumentName == RCISCAnalyzer.StringParameterName ?
                                generator.Argument(RCISCAnalyzer.StringParameterName, RefKind.None, argumentMemberAccessExpression.Expression) :
                                generator.Argument(argumentMemberAccessExpression.Expression);
                    }
                    else
                    {
                        newArgumentNode = arg.Syntax;
                    }
                }
                else
                {
                    newArgumentNode = arg.Syntax;
                }
 
                arguments.Add(newArgumentNode.WithTriviaFrom(arg.Syntax));
            }
 
            Debug.Assert(mainInvocationInstance != null);
 
            SyntaxNode stringComparisonArgument = GetNewStringComparisonArgument(generator, stringComparisonType, caseChangingApproachValue, isAnyArgumentNamed);
 
            arguments.Add(stringComparisonArgument);
 
            return arguments;
        }
 
        protected override IEnumerable<SyntaxNode> GetNewArgumentsForBinary(SyntaxGenerator generator, SyntaxNode rightNode, SyntaxNode typeMemberAccess) =>
            new List<SyntaxNode>()
            {
                generator.Argument(rightNode),
                generator.Argument(typeMemberAccess)
            };
    }
}