<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 System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeQuality.Analyzers.Maintainability
{
    using UnusedParameterDictionary = IDictionary<IMethodSymbol, ISet<IParameterSymbol>>;

    /// <summary>
    /// CA1801: Review unused parameters
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class ReviewUnusedParametersAnalyzer : DiagnosticAnalyzer
    {
        internal const string RuleId = "CA1801";

        private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftCodeQualityAnalyzersResources.ReviewUnusedParametersTitle), MicrosoftCodeQualityAnalyzersResources.ResourceManager, typeof(MicrosoftCodeQualityAnalyzersResources));

        private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftCodeQualityAnalyzersResources.ReviewUnusedParametersMessage), MicrosoftCodeQualityAnalyzersResources.ResourceManager, typeof(MicrosoftCodeQualityAnalyzersResources));
        private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftCodeQualityAnalyzersResources.ReviewUnusedParametersDescription), MicrosoftCodeQualityAnalyzersResources.ResourceManager, typeof(MicrosoftCodeQualityAnalyzersResources));

        internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(RuleId,
                                                                             s_localizableTitle,
                                                                             s_localizableMessage,
                                                                             DiagnosticCategory.Usage,
                                                                             DiagnosticHelpers.DefaultDiagnosticSeverity,
                                                                             isEnabledByDefault: DiagnosticHelpers.EnabledByDefaultIfNotBuildingVSIX,
                                                                             description: s_localizableDescription,
                                                                             helpLinkUri: "https://docs.microsoft.com/visualstudio/code-quality/ca1801-review-unused-parameters",
                                                                             customTags: FxCopWellKnownDiagnosticTags.PortedFxCopRule);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

#pragma warning disable RS1026 // Enable concurrent execution
        public override void Initialize(AnalysisContext context)
#pragma warning restore RS1026 // Enable concurrent execution
        {
            // TODO: Consider making this analyzer thread-safe.
            //context.EnableConcurrentExecution();

            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(compilationStartContext =>
            {
                INamedTypeSymbol? eventsArgSymbol = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemEventArgs);

                // Ignore conditional methods (FxCop compat - One conditional will often call another conditional method as its only use of a parameter)
                INamedTypeSymbol? conditionalAttributeSymbol = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticsConditionalAttribute);

                // Ignore methods with special serialization attributes (FxCop compat - All serialization methods need to take 'StreamingContext')
                INamedTypeSymbol? onDeserializingAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnDeserializingAttribute);
                INamedTypeSymbol? onDeserializedAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnDeserializedAttribute);
                INamedTypeSymbol? onSerializingAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnSerializingAttribute);
                INamedTypeSymbol? onSerializedAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnSerializedAttribute);
                INamedTypeSymbol? obsoleteAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute);

                ImmutableHashSet<INamedTypeSymbol?> attributeSetForMethodsToIgnore = ImmutableHashSet.Create(
                    conditionalAttributeSymbol,
                    onDeserializedAttribute,
                    onDeserializingAttribute,
                    onSerializedAttribute,
                    onSerializingAttribute,
                    obsoleteAttribute);

                UnusedParameterDictionary unusedMethodParameters = new ConcurrentDictionary<IMethodSymbol, ISet<IParameterSymbol>>();
                ISet<IMethodSymbol> methodsUsedAsDelegates = new HashSet<IMethodSymbol>();

                // Create a list of functions to exclude from analysis. We assume that any function that is used in an IMethodBindingExpression
                // cannot have its signature changed, and add it to the list of methods to be excluded from analysis.
                compilationStartContext.RegisterOperationAction(operationContext =>
                {
                    var methodBinding = (IMethodReferenceOperation)operationContext.Operation;
                    methodsUsedAsDelegates.Add(methodBinding.Method.OriginalDefinition);
                }, OperationKind.MethodReference);

                compilationStartContext.RegisterOperationBlockStartAction(startOperationBlockContext =>
                {
                    // We only care about methods.
                    if (!(startOperationBlockContext.OwningSymbol is IMethodSymbol method))
                    {
                        return;
                    }

                    AnalyzeMethod(method, startOperationBlockContext, unusedMethodParameters,
                        eventsArgSymbol, methodsUsedAsDelegates, attributeSetForMethodsToIgnore);

                    foreach (var localFunctionOperation in startOperationBlockContext.OperationBlocks.SelectMany(o => o.Descendants()).OfType<ILocalFunctionOperation>())
                    {
                        AnalyzeMethod(localFunctionOperation.Symbol, startOperationBlockContext, unusedMethodParameters,
                           eventsArgSymbol, methodsUsedAsDelegates, attributeSetForMethodsToIgnore);
                    }
                });

                // Register a compilation end action to filter all methods used as delegates and report any diagnostics
                compilationStartContext.RegisterCompilationEndAction(compilationAnalysisContext =>
                {
                    // Report diagnostics for unused parameters.
                    var unusedParameters = unusedMethodParameters.Where(kvp => !methodsUsedAsDelegates.Contains(kvp.Key)).SelectMany(kvp => kvp.Value);
                    foreach (var parameter in unusedParameters)
                    {
                        var diagnostic = Diagnostic.Create(Rule, parameter.Locations[0], parameter.Name, parameter.ContainingSymbol.Name);
                        compilationAnalysisContext.ReportDiagnostic(diagnostic);
                    }

                });
            });
        }

        private static void AnalyzeMethod(
            IMethodSymbol method,
            OperationBlockStartAnalysisContext startOperationBlockContext,
            UnusedParameterDictionary unusedMethodParameters,
            INamedTypeSymbol? eventsArgSymbol,
            ISet<IMethodSymbol> methodsUsedAsDelegates,
            ImmutableHashSet<INamedTypeSymbol?> attributeSetForMethodsToIgnore)
        {
            // We only care about methods with parameters.
            if (method.Parameters.IsEmpty)
            {
                return;
            }

            // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat).
            if (method.IsImplicitlyDeclared ||
                method.IsExtern ||
                method.IsAbstract ||
                method.IsVirtual ||
                method.IsOverride ||
                method.IsImplementationOfAnyInterfaceMember() ||
                method.IsFinalizer())
            {
                return;
            }

            // Ignore property accessors.
            if (method.IsPropertyAccessor())
            {
                return;
            }

            // Ignore event handler methods "Handler(object, MyEventArgs)"
            if (eventsArgSymbol != null &&
                method.Parameters.Length == 2 &&
                method.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
                // UWP has specific EventArgs not inheriting from System.EventArgs. It was decided to go for a suffix match rather than a whitelist.
                (method.Parameters[1].Type.Inherits(eventsArgSymbol) || method.Parameters[1].Type.Name.EndsWith("EventArgs", StringComparison.Ordinal)))
            {
                return;
            }

            // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'.
            if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass)))
            {
                return;
            }

            // Ignore methods that were used as delegates
            if (methodsUsedAsDelegates.Contains(method))
            {
                return;
            }

            // Bail out if user has configured to skip analysis for the method.<commit_msg>Remove unused EventArgs named type null check<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 System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeQuality.Analyzers.Maintainability
{
    using UnusedParameterDictionary = IDictionary<IMethodSymbol, ISet<IParameterSymbol>>;

    /// <summary>
    /// CA1801: Review unused parameters
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class ReviewUnusedParametersAnalyzer : DiagnosticAnalyzer
    {
        internal const string RuleId = "CA1801";

        private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftCodeQualityAnalyzersResources.ReviewUnusedParametersTitle), MicrosoftCodeQualityAnalyzersResources.ResourceManager, typeof(MicrosoftCodeQualityAnalyzersResources));

        private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftCodeQualityAnalyzersResources.ReviewUnusedParametersMessage), MicrosoftCodeQualityAnalyzersResources.ResourceManager, typeof(MicrosoftCodeQualityAnalyzersResources));
        private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftCodeQualityAnalyzersResources.ReviewUnusedParametersDescription), MicrosoftCodeQualityAnalyzersResources.ResourceManager, typeof(MicrosoftCodeQualityAnalyzersResources));

        internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(RuleId,
                                                                             s_localizableTitle,
                                                                             s_localizableMessage,
                                                                             DiagnosticCategory.Usage,
                                                                             DiagnosticHelpers.DefaultDiagnosticSeverity,
                                                                             isEnabledByDefault: DiagnosticHelpers.EnabledByDefaultIfNotBuildingVSIX,
                                                                             description: s_localizableDescription,
                                                                             helpLinkUri: "https://docs.microsoft.com/visualstudio/code-quality/ca1801-review-unused-parameters",
                                                                             customTags: FxCopWellKnownDiagnosticTags.PortedFxCopRule);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

#pragma warning disable RS1026 // Enable concurrent execution
        public override void Initialize(AnalysisContext context)
#pragma warning restore RS1026 // Enable concurrent execution
        {
            // TODO: Consider making this analyzer thread-safe.
            //context.EnableConcurrentExecution();

            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(compilationStartContext =>
            {
                INamedTypeSymbol? eventsArgSymbol = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemEventArgs);

                // Ignore conditional methods (FxCop compat - One conditional will often call another conditional method as its only use of a parameter)
                INamedTypeSymbol? conditionalAttributeSymbol = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticsConditionalAttribute);

                // Ignore methods with special serialization attributes (FxCop compat - All serialization methods need to take 'StreamingContext')
                INamedTypeSymbol? onDeserializingAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnDeserializingAttribute);
                INamedTypeSymbol? onDeserializedAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnDeserializedAttribute);
                INamedTypeSymbol? onSerializingAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnSerializingAttribute);
                INamedTypeSymbol? onSerializedAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationOnSerializedAttribute);
                INamedTypeSymbol? obsoleteAttribute = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute);

                ImmutableHashSet<INamedTypeSymbol?> attributeSetForMethodsToIgnore = ImmutableHashSet.Create(
                    conditionalAttributeSymbol,
                    onDeserializedAttribute,
                    onDeserializingAttribute,
                    onSerializedAttribute,
                    onSerializingAttribute,
                    obsoleteAttribute);

                UnusedParameterDictionary unusedMethodParameters = new ConcurrentDictionary<IMethodSymbol, ISet<IParameterSymbol>>();
                ISet<IMethodSymbol> methodsUsedAsDelegates = new HashSet<IMethodSymbol>();

                // Create a list of functions to exclude from analysis. We assume that any function that is used in an IMethodBindingExpression
                // cannot have its signature changed, and add it to the list of methods to be excluded from analysis.
                compilationStartContext.RegisterOperationAction(operationContext =>
                {
                    var methodBinding = (IMethodReferenceOperation)operationContext.Operation;
                    methodsUsedAsDelegates.Add(methodBinding.Method.OriginalDefinition);
                }, OperationKind.MethodReference);

                compilationStartContext.RegisterOperationBlockStartAction(startOperationBlockContext =>
                {
                    // We only care about methods.
                    if (!(startOperationBlockContext.OwningSymbol is IMethodSymbol method))
                    {
                        return;
                    }

                    AnalyzeMethod(method, startOperationBlockContext, unusedMethodParameters,
                        eventsArgSymbol, methodsUsedAsDelegates, attributeSetForMethodsToIgnore);

                    foreach (var localFunctionOperation in startOperationBlockContext.OperationBlocks.SelectMany(o => o.Descendants()).OfType<ILocalFunctionOperation>())
                    {
                        AnalyzeMethod(localFunctionOperation.Symbol, startOperationBlockContext, unusedMethodParameters,
                           eventsArgSymbol, methodsUsedAsDelegates, attributeSetForMethodsToIgnore);
                    }
                });

                // Register a compilation end action to filter all methods used as delegates and report any diagnostics
                compilationStartContext.RegisterCompilationEndAction(compilationAnalysisContext =>
                {
                    // Report diagnostics for unused parameters.
                    var unusedParameters = unusedMethodParameters.Where(kvp => !methodsUsedAsDelegates.Contains(kvp.Key)).SelectMany(kvp => kvp.Value);
                    foreach (var parameter in unusedParameters)
                    {
                        var diagnostic = Diagnostic.Create(Rule, parameter.Locations[0], parameter.Name, parameter.ContainingSymbol.Name);
                        compilationAnalysisContext.ReportDiagnostic(diagnostic);
                    }

                });
            });
        }

        private static void AnalyzeMethod(
            IMethodSymbol method,
            OperationBlockStartAnalysisContext startOperationBlockContext,
            UnusedParameterDictionary unusedMethodParameters,
            INamedTypeSymbol? eventsArgSymbol,
            ISet<IMethodSymbol> methodsUsedAsDelegates,
            ImmutableHashSet<INamedTypeSymbol?> attributeSetForMethodsToIgnore)
        {
            // We only care about methods with parameters.
            if (method.Parameters.IsEmpty)
            {
                return;
            }

            // Ignore implicitly declared methods, extern methods, abstract methods, virtual methods, interface implementations and finalizers (FxCop compat).
            if (method.IsImplicitlyDeclared ||
                method.IsExtern ||
                method.IsAbstract ||
                method.IsVirtual ||
                method.IsOverride ||
                method.IsImplementationOfAnyInterfaceMember() ||
                method.IsFinalizer())
            {
                return;
            }

            // Ignore property accessors.
            if (method.IsPropertyAccessor())
            {
                return;
            }

            // Ignore event handler methods "Handler(object, MyEventArgs)"
            if (method.Parameters.Length == 2 &&
                method.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
                // UWP has specific EventArgs not inheriting from System.EventArgs. It was decided to go for a suffix match rather than a whitelist.
                (method.Parameters[1].Type.Inherits(eventsArgSymbol) || method.Parameters[1].Type.Name.EndsWith("EventArgs", StringComparison.Ordinal)))
            {
                return;
            }

            // Ignore methods with any attributes in 'attributeSetForMethodsToIgnore'.
            if (method.GetAttributes().Any(a => a.AttributeClass != null && attributeSetForMethodsToIgnore.Contains(a.AttributeClass)))
            {
                return;
            }

            // Ignore methods that were used as delegates
            if (methodsUsedAsDelegates.Contains(method))
            {
                return;
            }

            // Bail out if user has configured to skip analysis for the method.
{'fraction_non_alphanumeric': 0.05681763833755799, 'fraction_numerical': 0.002582619924434454, 'mean_word_length': 2.3080208827717135, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 47, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 48, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3319877', 'n_tokens_mistral': 4424, 'n_tokens_neox': 4158, 'n_words': 929}