<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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.InitializeParameter
{
    internal abstract partial class AbstractAddParameterCheckCodeRefactoringProvider<
        TParameterSyntax,
        TStatementSyntax,
        TExpressionSyntax,
        TBinaryExpressionSyntax> : AbstractInitializeParameterCodeRefactoringProvider<
            TParameterSyntax,
            TStatementSyntax,
            TExpressionSyntax>
        where TParameterSyntax : SyntaxNode
        where TStatementSyntax : SyntaxNode
        where TExpressionSyntax : SyntaxNode
        where TBinaryExpressionSyntax : TExpressionSyntax
    {
        protected override async Task<ImmutableArray<CodeAction>> GetRefactoringsForAllParametersAsync(
            Document document, SyntaxNode functionDeclaration, IMethodSymbol methodSymbol,
            IBlockOperation blockStatementOpt, ImmutableArray<SyntaxNode> listOfParameterNodes, int position, CancellationToken cancellationToken)
        {

            // List to keep track of the valid parameters
            var listOfParametersOrdinals = new List<int>();
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            foreach (var parameterNode in listOfParameterNodes)
            {
                var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(parameterNode, cancellationToken);
                if (ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
                {
                    listOfParametersOrdinals.Add(parameter.Ordinal);
                }
            }

            if (listOfParametersOrdinals.Any())
            {
                return ImmutableArray<CodeAction>.Empty;
            }

            // Great.  The list has parameters that need null checks. Offer to add null checks for all.
            return ImmutableArray.Create<CodeAction>(new MyCodeAction(
                 FeaturesResources.Add_null_checks_for_all_parameters,
                     c => UpdateDocumentForRefactoring(document, functionDeclaration, blockStatementOpt, listOfParametersOrdinals, position, c)));
        }

        protected override async Task<ImmutableArray<CodeAction>> GetRefactoringsForSingleParameterAsync(
            Document document, IParameterSymbol parameter, SyntaxNode functionDeclaration, IMethodSymbol methodSymbol,
            IBlockOperation blockStatementOpt, CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // Only should provide null-checks for reference types and nullable types.
            if (!ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
            {
                return ImmutableArray<CodeAction>.Empty;
            }

            // Great.  There was no null check.  Offer to add one.
            var result = ArrayBuilder<CodeAction>.GetInstance();
            result.Add(new MyCodeAction(
                FeaturesResources.Add_null_check,
                c => AddNullCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, c)));

            // Also, if this was a string, offer to add the special checks to 
            // string.IsNullOrEmpty and string.IsNullOrWhitespace.
            if (parameter.Type.SpecialType == SpecialType.System_String)
            {
                result.Add(new MyCodeAction(
                    FeaturesResources.Add_string_IsNullOrEmpty_check,
                    c => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), c)));

                result.Add(new MyCodeAction(
                    FeaturesResources.Add_string_IsNullOrWhiteSpace_check,
                    c => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrWhiteSpace), c)));
            }

            return result.ToImmutableAndFree();
        }

        protected abstract bool CanOffer(SyntaxNode body);

        private async Task<Document> UpdateDocumentForRefactoring(
            Document document,
            SyntaxNode functionDeclaration,
            IBlockOperation blockStatementOpt,
            List<int> listOfParametersOrdinals,
            int position,
            CancellationToken cancellationToken)
        {

            foreach (var index in listOfParametersOrdinals)
            {
                // Updates functionDeclaration and uses it to get the first valid ParameterNode using the ordinals (index).
                var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                var token = root.FindToken(position);
                var firstparameterNode = GetParameterNode(token, position);
                functionDeclaration = firstparameterNode.FirstAncestorOrSelf<SyntaxNode>(IsFunctionDeclaration);

                var generator = SyntaxGenerator.GetGenerator(document);
                var parameterNodes = generator.GetParameters(functionDeclaration);
                var currentNode = parameterNodes[index];
                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
                var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(currentNode, cancellationToken);
                var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();

                if (!CanOfferRefactoring(functionDeclaration, semanticModel, syntaxFacts, cancellationToken, out blockStatementOpt))
                {
                    continue;
                }

                // If parameter is a string, default check would be IsNullOrEmpty - updates document
                if (parameter.Type.SpecialType == SpecialType.System_String)
                {
                    document = await AddStringCheckAsync(document, parameter, functionDeclaration, (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), cancellationToken).ConfigureAwait(false);
                    continue;
                }

                // For all other parameters, add null check - updates document
                document = await AddNullCheckAsync(document, parameter, functionDeclaration,
                    (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, cancellationToken).ConfigureAwait(false);
            }

            return document;
        }

        private bool ContainsNullCoalesceCheck(
            ISyntaxFactsService syntaxFacts, SemanticModel semanticModel,
            IOperation statement, IParameterSymbol parameter,
            CancellationToken cancellationToken)
        {
            // Look for anything in this statement of the form "p ?? throw ...".
            // If so, we'll consider this parameter checked for null and we can stop immediately.

            var syntax = statement.Syntax;
            foreach (var coalesceNode in syntax.DescendantNodes().OfType<TBinaryExpressionSyntax>())
            {
                var operation = semanticModel.GetOperation(coalesceNode, cancellationToken);
                if (operation is ICoalesceOperation coalesceExpression)
                {
                    if (IsParameterReference(coalesceExpression.Value, parameter) &&<commit_msg>"Addressed more comments"<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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.InitializeParameter
{
    internal abstract partial class AbstractAddParameterCheckCodeRefactoringProvider<
        TParameterSyntax,
        TStatementSyntax,
        TExpressionSyntax,
        TBinaryExpressionSyntax> : AbstractInitializeParameterCodeRefactoringProvider<
            TParameterSyntax,
            TStatementSyntax,
            TExpressionSyntax>
        where TParameterSyntax : SyntaxNode
        where TStatementSyntax : SyntaxNode
        where TExpressionSyntax : SyntaxNode
        where TBinaryExpressionSyntax : TExpressionSyntax
    {
        protected override async Task<ImmutableArray<CodeAction>> GetRefactoringsForAllParametersAsync(
            Document document, SyntaxNode functionDeclaration, IMethodSymbol methodSymbol,
            IBlockOperation blockStatementOpt, ImmutableArray<SyntaxNode> listOfParameterNodes, int position, CancellationToken cancellationToken)
        {

            // List to keep track of the valid parameters
            var listOfParametersOrdinals = new List<int>();
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            foreach (var parameterNode in listOfParameterNodes)
            {
                var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(parameterNode, cancellationToken);
                if (ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
                {
                    listOfParametersOrdinals.Add(parameter.Ordinal);
                }
            }

            if (!listOfParametersOrdinals.Any())
            {
                return ImmutableArray<CodeAction>.Empty;
            }

            // Great.  The list has parameters that need null checks. Offer to add null checks for all.
            return ImmutableArray.Create<CodeAction>(new MyCodeAction(
                 FeaturesResources.Add_null_checks_for_all_parameters,
                     c => UpdateDocumentForRefactoringAsync(document, functionDeclaration, blockStatementOpt, listOfParametersOrdinals, position, c)));
        }
        protected override async Task<ImmutableArray<CodeAction>> GetRefactoringsForSingleParameterAsync(
            Document document, IParameterSymbol parameter, SyntaxNode functionDeclaration, IMethodSymbol methodSymbol,
            IBlockOperation blockStatementOpt, CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // Only should provide null-checks for reference types and nullable types.
            if (!ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
            {
                return ImmutableArray<CodeAction>.Empty;
            }

            // Great.  There was no null check.  Offer to add one.
            var result = ArrayBuilder<CodeAction>.GetInstance();
            result.Add(new MyCodeAction(
                FeaturesResources.Add_null_check,
                c => AddNullCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, c)));

            // Also, if this was a string, offer to add the special checks to 
            // string.IsNullOrEmpty and string.IsNullOrWhitespace.
            if (parameter.Type.SpecialType == SpecialType.System_String)
            {
                result.Add(new MyCodeAction(
                    FeaturesResources.Add_string_IsNullOrEmpty_check,
                    c => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), c)));

                result.Add(new MyCodeAction(
                    FeaturesResources.Add_string_IsNullOrWhiteSpace_check,
                    c => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrWhiteSpace), c)));
            }

            return result.ToImmutableAndFree();
        }

        protected abstract bool CanOffer(SyntaxNode body);

        private async Task<Document> UpdateDocumentForRefactoringAsync(
            Document document,
            SyntaxNode functionDeclaration,
            IBlockOperation blockStatementOpt,
            List<int> listOfParametersOrdinals,
            int position,
            CancellationToken cancellationToken)
        {

            foreach (var index in listOfParametersOrdinals)
            {
                // Updates functionDeclaration and uses it to get the first valid ParameterNode using the ordinals (index).
                var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                var token = root.FindToken(position);
                var firstparameterNode = GetParameterNode(token, position);
                functionDeclaration = firstparameterNode.FirstAncestorOrSelf<SyntaxNode>(IsFunctionDeclaration);
                var generator = SyntaxGenerator.GetGenerator(document);
                var parameterNodes = generator.GetParameters(functionDeclaration);
                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
                var parameter = GetParameterAtOrdinal(index, parameterNodes, semanticModel, cancellationToken);
                var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();

                if (!CanOfferRefactoring(functionDeclaration, semanticModel, syntaxFacts, cancellationToken, out blockStatementOpt))
                {
                    continue;
                }

                // If parameter is a string, default check would be IsNullOrEmpty - updates document
                if (parameter.Type.SpecialType == SpecialType.System_String)
                {
                    document = await AddStringCheckAsync(document, parameter, functionDeclaration, (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), cancellationToken).ConfigureAwait(false);
                    continue;
                }

                // For all other parameters, add null check - updates document
                document = await AddNullCheckAsync(document, parameter, functionDeclaration,
                    (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, cancellationToken).ConfigureAwait(false);
            }

            return document;
        }

        private IParameterSymbol GetParameterAtOrdinal(int index, IReadOnlyList<SyntaxNode> parameterNodes, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            foreach (var parameterNode in parameterNodes)
            {
                var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(parameterNode, cancellationToken);
                if (index == parameter.Ordinal)
                {
                    return parameter;
                }
            }
            return null;
        }
        private bool ContainsNullCoalesceCheck(
            ISyntaxFactsService syntaxFacts, SemanticModel semanticModel,
            IOperation statement, IParameterSymbol parameter,
            CancellationToken cancellationToken)
        {
            // Look for anything in this statement of the form "p ?? throw ...".
            // If so, we'll consider this parameter checked for null and we can stop immediately.

            var syntax = statement.Syntax;
            foreach (var coalesceNode in syntax.DescendantNodes().OfType<TBinaryExpressionSyntax>())
            {
                var operation = semanticModel.GetOperation(coalesceNode, cancellationToken);
                if (operation is ICoalesceOperation coalesceExpression)
                {
                    if (IsParameterReference(coalesceExpression.Value, parameter) &&
{'fraction_non_alphanumeric': 0.06196275071633238, 'fraction_numerical': 0.0002387774594078319, 'mean_word_length': 2.645126196692776, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 46, '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': '6966392', 'n_tokens_mistral': 3748, 'n_tokens_neox': 3650, 'n_words': 929}