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

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeQuality.Analyzers.QualityGuidelines;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace Microsoft.CodeQuality.CSharp.Analyzers.QualityGuidelines
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class CSharpAvoidDuplicateElementInitialization : AvoidDuplicateElementInitialization
    {
        private static readonly SyntaxGenerator s_generator = SyntaxGenerator.GetGenerator(new AdhocWorkspace(), LanguageNames.CSharp);

        public override void Initialize(AnalysisContext analysisContext)
        {
            analysisContext.EnableConcurrentExecution();
            analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            analysisContext.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ObjectInitializerExpression);
        }

        private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            var objectInitializer = (InitializerExpressionSyntax)context.Node;
            var initializedElementIndexes = new HashSet<object[]>(ConstantArgumentEqualityComparer.Instance);

            foreach (var intializationExpression in objectInitializer.Expressions.OfType<AssignmentExpressionSyntax>())
            {
                if (intializationExpression.Left is ImplicitElementAccessSyntax elementAccess)
                {
                    var argumentList = elementAccess.ArgumentList;
                    var values = TryGetResolvedArgumentValues(argumentList, context);
                    if (values != null && !initializedElementIndexes.Add(values))
                    {
                        var indexesText = string.Join(", ", values.Select(value => s_generator.LiteralExpression(value)));
                        context.ReportDiagnostic(Diagnostic.Create(Rule, argumentList.GetLocation(), indexesText));
                    }
                }

                if (context.CancellationToken.IsCancellationRequested)
                {
                    break;
                }
            }
        }

        private static int GetParameterIndex(ImmutableArray<IParameterSymbol> parameters, string parameterName)
        {
            for (int i = 0; i < parameters.Length; i++)
            {
                if (parameters[i].Name.Equals(parameterName, StringComparison.Ordinal))
                {
                    return i;
                }
            }

            return -1;
        }

        private static int GetParameterIndex(ImmutableArray<IParameterSymbol> parameters, ArgumentSyntax argument, int argumentIndex)
        {
            if (argument.NameColon != null)
            {
                return GetParameterIndex(parameters, GetParameterName(argument));
            }

            if (argumentIndex < parameters.Length)
            {
                return argumentIndex;
            }

            return -1;
        }

        private static string GetParameterName(ArgumentSyntax argument)
        {
            var name = argument.NameColon.Name.ToString();
            if (name.Length != 0 && name[0] == '@')
            {
                return name.Substring(1);
            }
            return name;
        }

        private static object[] TryGetResolvedArgumentValues(BracketedArgumentListSyntax node, SyntaxNodeAnalysisContext context)
        {
            var propertySymbol = context.SemanticModel.GetSymbolInfo(node.Parent, context.CancellationToken).Symbol as IPropertySymbol;
            if (propertySymbol == null)
            {
                return null;
            }

            var parameters = propertySymbol.Parameters;
            var usedParameterIndexes = new HashSet<int>();
            var result = new object[parameters.Length];
            for (int argumentIndex = 0; argumentIndex < node.Arguments.Count; argumentIndex++)
            {
                var argument = node.Arguments[argumentIndex];
                var constant = context.SemanticModel.GetConstantValue(argument.Expression, context.CancellationToken);
                if (!constant.HasValue)
                {
                    return null;
                }

                int parameterIndex = GetParameterIndex(parameters, argument, argumentIndex);
                if (parameterIndex < 0 || !usedParameterIndexes.Add(parameterIndex))
                {
                    return null;
                }
                
                result[parameterIndex] = constant.Value;
            }

            if (usedParameterIndexes.Count < parameters.Length)
            {
                for (int parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++)
                {
                    if (usedParameterIndexes.Add(parameterIndex))
                    {
                        var parameter = parameters[parameterIndex];
                        if (!parameter.HasExplicitDefaultValue)
                        {
                            return null;
                        }
                        result[parameterIndex] = parameter.ExplicitDefaultValue;
                    }
                }
            }

            return result;
        }

        private sealed class ConstantArgumentEqualityComparer : IEqualityComparer<object[]>
        {
            public static readonly ConstantArgumentEqualityComparer Instance = new ConstantArgumentEqualityComparer();

            private readonly EqualityComparer<object> _objectComparer = EqualityComparer<object>.Default;

            private ConstantArgumentEqualityComparer() { }

            bool IEqualityComparer<object[]>.Equals(object[] x, object[] y)
            {
                if (x == y)
                {
                    return true;
                }

                if (x == null || y == null || x.Length != y.Length)
                {
                    return false;
                }

                for (int i = 0; i < x.Length; i++)
                {
                    if (!_objectComparer.Equals(x[i], y[i]))
                    {<commit_msg>Update the AvoidDuplicateElementInitialization analyzer to analyze IOperations<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeQuality.Analyzers.QualityGuidelines;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeQuality.CSharp.Analyzers.QualityGuidelines
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class CSharpAvoidDuplicateElementInitialization : AvoidDuplicateElementInitialization
    {
        private static readonly SyntaxGenerator s_generator = SyntaxGenerator.GetGenerator(new AdhocWorkspace(), LanguageNames.CSharp);

        public override void Initialize(AnalysisContext analysisContext)
        {
            analysisContext.EnableConcurrentExecution();
            analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            analysisContext.RegisterOperationAction(AnalyzeOperation, OperationKind.ObjectCreation);
        }

        private static void AnalyzeOperation(OperationAnalysisContext context)
        {
            var objectInitializer = (IObjectCreationOperation)context.Operation;
            var semanticModel = context.Compilation.GetSemanticModel(objectInitializer.Syntax.SyntaxTree);
            var initializedElementIndexes = new HashSet<object[]>(ConstantArgumentEqualityComparer.Instance);

            foreach (var initializer in objectInitializer.Initializer.Initializers)
            {
                if (initializer is ISimpleAssignmentOperation assignment &&
                    assignment.Target is IPropertyReferenceOperation propertyReference &&
                    propertyReference.Arguments.Length != 0 &&
                    ((AssignmentExpressionSyntax)assignment.Syntax).Left is ImplicitElementAccessSyntax elementAccess)
                {
                    var values = GetConstantArgumentValues(propertyReference.Arguments, semanticModel, context);
                    if (values != null && !initializedElementIndexes.Add(values))
                    {
                        var indexesText = string.Join(", ", values.Select(value => s_generator.LiteralExpression(value)));
                        context.ReportDiagnostic(
                            Diagnostic.Create(Rule, elementAccess.ArgumentList.GetLocation(), indexesText));
                    }
                }

                if (context.CancellationToken.IsCancellationRequested)
                {
                    break;
                }
            }
        }

        /// <summary>
        /// Gets the argument values in parameter order, filling in defaults if necessary, if all
        /// arguments are constants. Otherwise, returns null.
        /// </summary>
        private static object[] GetConstantArgumentValues(
            ImmutableArray<IArgumentOperation> arguments,
            SemanticModel semanticModel,
            OperationAnalysisContext context)
        {
            var result = new object[arguments.Length];
            foreach(var argument in arguments)
            {
                var parameter = argument.Parameter;
                if (parameter == null ||
                    parameter.Ordinal >= result.Length ||
                    !TryGetConstantValue(argument, out result[parameter.Ordinal]))
                {
                    return null;
                }
            }
            return result;

            bool TryGetConstantValue(IArgumentOperation argument, out object value)
            {
                var parameter = argument.Parameter;
                switch (argument.ArgumentKind)
                {
                    case ArgumentKind.DefaultValue:
                        value = parameter.ExplicitDefaultValue;
                        return parameter.HasExplicitDefaultValue;
                    case ArgumentKind.Explicit:
                        var constantValue = semanticModel.GetConstantValue(
                            ((ArgumentSyntax)argument.Syntax).Expression,
                            context.CancellationToken);
                        value = constantValue.Value;
                        return constantValue.HasValue;
                    default:
                        value = null;
                        return false;
                }
            }
        }

        private sealed class ConstantArgumentEqualityComparer : IEqualityComparer<object[]>
        {
            public static readonly ConstantArgumentEqualityComparer Instance = new ConstantArgumentEqualityComparer();

            private readonly EqualityComparer<object> _objectComparer = EqualityComparer<object>.Default;

            private ConstantArgumentEqualityComparer() { }

            bool IEqualityComparer<object[]>.Equals(object[] x, object[] y)
            {
                if (x == y)
                {
                    return true;
                }

                if (x == null || y == null || x.Length != y.Length)
                {
                    return false;
                }

                for (int i = 0; i < x.Length; i++)
                {
                    if (!_objectComparer.Equals(x[i], y[i]))
                    {
{'fraction_non_alphanumeric': 0.0720772787318362, 'fraction_numerical': 0.001321003963011889, 'mean_word_length': 1.8676609848484849, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 23, 'https://': 0, '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': '7397383', 'n_tokens_mistral': 2732, 'n_tokens_neox': 2578, 'n_words': 572}