<commit_before>﻿// 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;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator
{
    using static CodeFixHelpers;
    using static CSharpUseRangeOperatorDiagnosticAnalyzer;
    using static Helpers;
    using static SyntaxFactory;

    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    internal class CSharpUseRangeOperatorCodeFixProvider : SyntaxEditorBasedCodeFixProvider
    {
        [ImportingConstructor]
        [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
        public CSharpUseRangeOperatorCodeFixProvider()
        {
        }

        public override ImmutableArray<string> FixableDiagnosticIds { get; } =
            ImmutableArray.Create(IDEDiagnosticIds.UseRangeOperatorDiagnosticId);

        internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeStyle;

        public override Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            context.RegisterCodeFix(new MyCodeAction(
                c => FixAsync(context.Document, context.Diagnostics[0], c)),
                context.Diagnostics);

            return Task.CompletedTask;
        }

        protected override async Task FixAllAsync(
            Document document, ImmutableArray<Diagnostic> diagnostics,
            SyntaxEditor editor, CancellationToken cancellationToken)
        {
            var invocationNodes = diagnostics.Select(d => GetInvocationExpression(d, cancellationToken))
                                             .OrderByDescending(i => i.SpanStart)
                                             .ToImmutableArray();
            var syntaxGenerator = SyntaxGenerator.GetGenerator(document);

            await editor.ApplyExpressionLevelSemanticEditsAsync(
                document, invocationNodes,
                canReplace: (_1, _2) => true,
                (semanticModel, currentRoot, currentInvocation) =>
                    UpdateInvocation(semanticModel, currentRoot, currentInvocation, syntaxGenerator, cancellationToken),
                cancellationToken).ConfigureAwait(false);
        }

        private static SyntaxNode UpdateInvocation(
            SemanticModel semanticModel, SyntaxNode currentRoot,
            InvocationExpressionSyntax currentInvocation,
            SyntaxGenerator generator,
            CancellationToken cancellationToken)
        {
            if (semanticModel.GetOperation(currentInvocation, cancellationToken) is IInvocationOperation invocation)
            {
                var infoCache = new InfoCache(semanticModel.Compilation);
                var resultOpt = AnalyzeInvocation(
                    invocation, infoCache, analyzerOptionsOpt: null, cancellationToken);

                if (resultOpt != null)
                {
                    var result = resultOpt.Value;
                    var updatedNode = FixOne(result, generator);
                    if (updatedNode != null)
                    {
                        return currentRoot.ReplaceNode(result.Invocation, updatedNode);
                    }
                }
            }

            return currentRoot;
        }

        private static InvocationExpressionSyntax GetInvocationExpression(Diagnostic d, CancellationToken cancellationToken)
            => (InvocationExpressionSyntax)d.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken);

        private static ExpressionSyntax FixOne(Result result, SyntaxGenerator generator)
        {
            var invocation = result.Invocation;
            var expression = invocation switch
            {
                { Expression: MemberAccessExpressionSyntax memberAccess } _ => memberAccess.Expression,
                { Parent: ConditionalAccessExpressionSyntax _ } => null,
                _ => invocation.Expression
            };

            var rangeExpression = CreateRangeExpression(result, generator);
            var argument = Argument(rangeExpression).WithAdditionalAnnotations(Formatter.Annotation);
            var arguments = SingletonSeparatedList(argument);

            if (result.MemberInfo.OverloadedMethodOpt == null)
            {
                var argList = invocation.ArgumentList;
                var argumentList = BracketedArgumentList(
                                        Token(SyntaxKind.OpenBracketToken).WithTriviaFrom(argList.OpenParenToken),
                                        arguments,
                                        Token(SyntaxKind.CloseBracketToken).WithTriviaFrom(argList.CloseParenToken));
                return expression == null
                    ? (ExpressionSyntax)ElementBindingExpression(argumentList)
                    : ElementAccessExpression(expression, argumentList);
            }
            else
            {
                return invocation.ReplaceNode(
                    invocation.ArgumentList,
                    invocation.ArgumentList.WithArguments(arguments));
            }
        }

        private static RangeExpressionSyntax CreateRangeExpression(Result result, SyntaxGenerator generator)
            => result.Kind switch
            {
                ResultKind.Computed => CreateComputedRange(result),<commit_msg>Move expression calculation in if block<commit_after>﻿// 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;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator
{
    using static CodeFixHelpers;
    using static CSharpUseRangeOperatorDiagnosticAnalyzer;
    using static Helpers;
    using static SyntaxFactory;

    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    internal class CSharpUseRangeOperatorCodeFixProvider : SyntaxEditorBasedCodeFixProvider
    {
        [ImportingConstructor]
        [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
        public CSharpUseRangeOperatorCodeFixProvider()
        {
        }

        public override ImmutableArray<string> FixableDiagnosticIds { get; } =
            ImmutableArray.Create(IDEDiagnosticIds.UseRangeOperatorDiagnosticId);

        internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeStyle;

        public override Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            context.RegisterCodeFix(new MyCodeAction(
                c => FixAsync(context.Document, context.Diagnostics[0], c)),
                context.Diagnostics);

            return Task.CompletedTask;
        }

        protected override async Task FixAllAsync(
            Document document, ImmutableArray<Diagnostic> diagnostics,
            SyntaxEditor editor, CancellationToken cancellationToken)
        {
            var invocationNodes = diagnostics.Select(d => GetInvocationExpression(d, cancellationToken))
                                             .OrderByDescending(i => i.SpanStart)
                                             .ToImmutableArray();
            var syntaxGenerator = SyntaxGenerator.GetGenerator(document);

            await editor.ApplyExpressionLevelSemanticEditsAsync(
                document, invocationNodes,
                canReplace: (_1, _2) => true,
                (semanticModel, currentRoot, currentInvocation) =>
                    UpdateInvocation(semanticModel, currentRoot, currentInvocation, syntaxGenerator, cancellationToken),
                cancellationToken).ConfigureAwait(false);
        }

        private static SyntaxNode UpdateInvocation(
            SemanticModel semanticModel, SyntaxNode currentRoot,
            InvocationExpressionSyntax currentInvocation,
            SyntaxGenerator generator,
            CancellationToken cancellationToken)
        {
            if (semanticModel.GetOperation(currentInvocation, cancellationToken) is IInvocationOperation invocation)
            {
                var infoCache = new InfoCache(semanticModel.Compilation);
                var resultOpt = AnalyzeInvocation(
                    invocation, infoCache, analyzerOptionsOpt: null, cancellationToken);

                if (resultOpt != null)
                {
                    var result = resultOpt.Value;
                    var updatedNode = FixOne(result, generator);
                    if (updatedNode != null)
                    {
                        return currentRoot.ReplaceNode(result.Invocation, updatedNode);
                    }
                }
            }

            return currentRoot;
        }

        private static InvocationExpressionSyntax GetInvocationExpression(Diagnostic d, CancellationToken cancellationToken)
            => (InvocationExpressionSyntax)d.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken);

        private static ExpressionSyntax FixOne(Result result, SyntaxGenerator generator)
        {
            var invocation = result.Invocation;

            var rangeExpression = CreateRangeExpression(result, generator);
            var argument = Argument(rangeExpression).WithAdditionalAnnotations(Formatter.Annotation);
            var arguments = SingletonSeparatedList(argument);

            if (result.MemberInfo.OverloadedMethodOpt == null)
            {
                var expression = invocation switch
                {
                    { Expression: MemberAccessExpressionSyntax memberAccess } _ => memberAccess.Expression,
                    { Parent: ConditionalAccessExpressionSyntax _ } => null,
                    _ => invocation.Expression
                };

                var argList = invocation.ArgumentList;
                var argumentList = BracketedArgumentList(
                        Token(SyntaxKind.OpenBracketToken).WithTriviaFrom(argList.OpenParenToken),
                        arguments,
                        Token(SyntaxKind.CloseBracketToken).WithTriviaFrom(argList.CloseParenToken));
                return expression == null
                    ? (ExpressionSyntax)ElementBindingExpression(argumentList)
                    : ElementAccessExpression(expression, argumentList);
            }
            else
            {
                return invocation.ReplaceNode(
                    invocation.ArgumentList,
                    invocation.ArgumentList.WithArguments(arguments));
            }
        }

        private static RangeExpressionSyntax CreateRangeExpression(Result result, SyntaxGenerator generator)
            => result.Kind switch
            {
                ResultKind.Computed => CreateComputedRange(result),
{'fraction_non_alphanumeric': 0.06473232529628116, 'fraction_numerical': 0.002125051082958725, 'mean_word_length': 2.3866592859119846, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 31, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6277496', 'n_tokens_mistral': 2762, 'n_tokens_neox': 2638, 'n_words': 596}