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

namespace Microsoft.CodeAnalysis.FindSymbols.Finders
{
    internal class ConstructorSymbolReferenceFinder : AbstractReferenceFinder<IMethodSymbol>
    {
        public static readonly ConstructorSymbolReferenceFinder Instance = new();

        private ConstructorSymbolReferenceFinder()
        {
        }

        protected override bool CanFind(IMethodSymbol symbol)
            => symbol.MethodKind switch
            {
                MethodKind.Constructor => true,
                MethodKind.StaticConstructor => true,
                _ => false,
            };

        protected override Task<ImmutableArray<string>> DetermineGlobalAliasesAsync(IMethodSymbol symbol, Project project, CancellationToken cancellationToken)
        {
            var containingType = symbol.ContainingType;
            return GetAllMatchingGlobalAliasNamesAsync(project, containingType.Name, containingType.Arity, cancellationToken);
        }

        protected override async Task<ImmutableArray<Document>> DetermineDocumentsToSearchAsync(
            IMethodSymbol symbol,
            HashSet<string>? globalAliases,
            Project project,
            IImmutableSet<Document>? documents,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            var containingType = symbol.ContainingType;
            var typeName = symbol.ContainingType.Name;

            using var _ = ArrayBuilder<Document>.GetInstance(out var result);

            await AddDocumentsAsync(
                project, documents, typeName, result, cancellationToken).ConfigureAwait(false);

            if (globalAliases != null)
            {
                foreach (var globalAlias in globalAliases)
                {
                    await AddDocumentsAsync(
                        project, documents, globalAlias, result, cancellationToken).ConfigureAwait(false);
                }
            }

            result.AddRange(await FindDocumentsAsync(
                project, documents, containingType.SpecialType.ToPredefinedType(), cancellationToken).ConfigureAwait(false));

            result.AddRange(await FindDocumentsWithGlobalSuppressMessageAttributeAsync(
                project, documents, cancellationToken).ConfigureAwait(false));

            result.AddRange(symbol.MethodKind == MethodKind.Constructor
                ? await FindDocumentsWithImplicitObjectCreationExpressionAsync(project, documents, cancellationToken).ConfigureAwait(false)
                : ImmutableArray<Document>.Empty);

            return result.ToImmutable();
        }

        private static async Task AddDocumentsAsync(
            Project project,
            IImmutableSet<Document>? documents,
            string typeName,
            ArrayBuilder<Document> result,
            CancellationToken cancellationToken)
        {
            var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, typeName).ConfigureAwait(false);

            var documentsWithAttribute = TryGetNameWithoutAttributeSuffix(typeName, project.LanguageServices.GetRequiredService<ISyntaxFactsService>(), out var simpleName)
                ? await FindDocumentsAsync(project, documents, cancellationToken, simpleName).ConfigureAwait(false)
                : ImmutableArray<Document>.Empty;

            result.AddRange(documentsWithName);
            result.AddRange(documentsWithAttribute);
        }

        private static bool IsPotentialReference(
            PredefinedType predefinedType,
            ISyntaxFactsService syntaxFacts,
            SyntaxToken token)
        {
            return
                syntaxFacts.TryGetPredefinedType(token, out var actualType) &&
                predefinedType == actualType;
        }

        protected override async ValueTask<ImmutableArray<FinderLocation>> FindReferencesInDocumentAsync(
            IMethodSymbol methodSymbol,
            FindReferencesDocumentState state,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            using var _1 = ArrayBuilder<FinderLocation>.GetInstance(out var result);

            var findParentNode = GetNamedTypeOrConstructorFindParentNodeFunction(state, methodSymbol);

            // First just look for this normal constructor references using the name of it's containing type.
            var name = methodSymbol.ContainingType.Name;
            await AddReferencesInDocumentWorkerAsync(
                methodSymbol, name, state, findParentNode, result, cancellationToken).ConfigureAwait(false);

            // Next, look for constructor references through a global alias to our containing type.
            if (state.GlobalAliases != null)
            {
                foreach (var globalAlias in state.GlobalAliases)
                {
                    // ignore the cases where the global alias might match the type name (i.e.
                    // global alias Console = System.Console).  We'll already find those references
                    // above.
                    if (state.SyntaxFacts.StringComparer.Equals(name, globalAlias))
                        continue;

                    await AddReferencesInDocumentWorkerAsync(
                        methodSymbol, globalAlias, state, findParentNode, result, cancellationToken).ConfigureAwait(false);
                }
            }

            // Nest, our containing type might itself have local aliases to it in this particular file.
            // If so, see what the local aliases are and then search for constructor references to that.
            using var _2 = ArrayBuilder<FinderLocation>.GetInstance(out var typeReferences);
            await NamedTypeSymbolReferenceFinder.AddReferencesToTypeOrGlobalAliasToItAsync(
                methodSymbol.ContainingType, state, typeReferences, cancellationToken).ConfigureAwait(false);

            var aliasReferences = await FindLocalAliasReferencesAsync(
                typeReferences, methodSymbol, state, findParentNode, cancellationToken).ConfigureAwait(false);

            // Finally, look for constructor references to predefined types (like `new int()`),
            // implicit object references, and inside global suppression attributes.
            result.AddRange(await FindPredefinedTypeReferencesAsync(
                methodSymbol, state, cancellationToken).ConfigureAwait(false));

            result.AddRange(await FindReferencesInImplicitObjectCreationExpressionAsync(
                methodSymbol, state, cancellationToken).ConfigureAwait(false));

            result.AddRange(await FindReferencesInDocumentInsideGlobalSuppressionsAsync(
                methodSymbol, state, cancellationToken).ConfigureAwait(false));

            return result.ToImmutable();
        }

        /// <summary>
        /// Finds references to <paramref name="symbol"/> in this <paramref name="state"/>, but only if it referenced
        /// though <paramref name="name"/> (which might be the actual name of the type, or a global alias to it).
        /// </summary>
        private static async Task AddReferencesInDocumentWorkerAsync(
            IMethodSymbol symbol,
            string name,
            FindReferencesDocumentState state,
            Func<SyntaxToken, SyntaxNode>? findParentNode,
            ArrayBuilder<FinderLocation> result,
            CancellationToken cancellationToken)
        {
            result.AddRange(await FindOrdinaryReferencesAsync(
                symbol, name, state, findParentNode, cancellationToken).ConfigureAwait(false));
            result.AddRange(await FindAttributeReferencesAsync(
                symbol, name, state, cancellationToken).ConfigureAwait(false));
        }

        private static ValueTask<ImmutableArray<FinderLocation>> FindOrdinaryReferencesAsync(
            IMethodSymbol symbol,
            string name,
            FindReferencesDocumentState state,
            Func<SyntaxToken, SyntaxNode>? findParentNode,
            CancellationToken cancellationToken)
        {
            return FindReferencesInDocumentUsingIdentifierAsync(
                symbol, name, state, findParentNode, cancellationToken);
        }

        private static ValueTask<ImmutableArray<FinderLocation>> FindPredefinedTypeReferencesAsync(
            IMethodSymbol symbol,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {
            var predefinedType = symbol.ContainingType.SpecialType.ToPredefinedType();
            if (predefinedType == PredefinedType.None)
            {
                return new ValueTask<ImmutableArray<FinderLocation>>(ImmutableArray<FinderLocation>.Empty);
            }

            var syntaxFacts = state.SyntaxFacts;
            return FindReferencesInDocumentAsync(
                symbol, state,
                static (state, token, predefinedType, _) => IsPotentialReference(predefinedType, state.SyntaxFacts, token),
                predefinedType,
                cancellationToken);
        }

        private static ValueTask<ImmutableArray<FinderLocation>> FindAttributeReferencesAsync(
            IMethodSymbol symbol,
            string name,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {
            var syntaxFacts = state.SyntaxFacts;
            return TryGetNameWithoutAttributeSuffix(name, syntaxFacts, out var simpleName)
                ? FindReferencesInDocumentUsingIdentifierAsync(symbol, simpleName, state, cancellationToken)
                : new ValueTask<ImmutableArray<FinderLocation>>(ImmutableArray<FinderLocation>.Empty);
        }

        private Task<ImmutableArray<FinderLocation>> FindReferencesInImplicitObjectCreationExpressionAsync(
            IMethodSymbol symbol,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {<commit_msg>Simplify<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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FindSymbols.Finders
{
    internal class ConstructorSymbolReferenceFinder : AbstractReferenceFinder<IMethodSymbol>
    {
        public static readonly ConstructorSymbolReferenceFinder Instance = new();

        private ConstructorSymbolReferenceFinder()
        {
        }

        protected override bool CanFind(IMethodSymbol symbol)
            => symbol.MethodKind switch
            {
                MethodKind.Constructor => true,
                MethodKind.StaticConstructor => true,
                _ => false,
            };

        protected override Task<ImmutableArray<string>> DetermineGlobalAliasesAsync(IMethodSymbol symbol, Project project, CancellationToken cancellationToken)
        {
            var containingType = symbol.ContainingType;
            return GetAllMatchingGlobalAliasNamesAsync(project, containingType.Name, containingType.Arity, cancellationToken);
        }

        protected override async Task<ImmutableArray<Document>> DetermineDocumentsToSearchAsync(
            IMethodSymbol symbol,
            HashSet<string>? globalAliases,
            Project project,
            IImmutableSet<Document>? documents,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            var containingType = symbol.ContainingType;
            var typeName = symbol.ContainingType.Name;

            using var _ = ArrayBuilder<Document>.GetInstance(out var result);

            await AddDocumentsAsync(
                project, documents, typeName, result, cancellationToken).ConfigureAwait(false);

            if (globalAliases != null)
            {
                foreach (var globalAlias in globalAliases)
                {
                    await AddDocumentsAsync(
                        project, documents, globalAlias, result, cancellationToken).ConfigureAwait(false);
                }
            }

            result.AddRange(await FindDocumentsAsync(
                project, documents, containingType.SpecialType.ToPredefinedType(), cancellationToken).ConfigureAwait(false));

            result.AddRange(await FindDocumentsWithGlobalSuppressMessageAttributeAsync(
                project, documents, cancellationToken).ConfigureAwait(false));

            result.AddRange(symbol.MethodKind == MethodKind.Constructor
                ? await FindDocumentsWithImplicitObjectCreationExpressionAsync(project, documents, cancellationToken).ConfigureAwait(false)
                : ImmutableArray<Document>.Empty);

            return result.ToImmutable();
        }

        private static async Task AddDocumentsAsync(
            Project project,
            IImmutableSet<Document>? documents,
            string typeName,
            ArrayBuilder<Document> result,
            CancellationToken cancellationToken)
        {
            var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, typeName).ConfigureAwait(false);

            var documentsWithAttribute = TryGetNameWithoutAttributeSuffix(typeName, project.LanguageServices.GetRequiredService<ISyntaxFactsService>(), out var simpleName)
                ? await FindDocumentsAsync(project, documents, cancellationToken, simpleName).ConfigureAwait(false)
                : ImmutableArray<Document>.Empty;

            result.AddRange(documentsWithName);
            result.AddRange(documentsWithAttribute);
        }

        private static bool IsPotentialReference(
            PredefinedType predefinedType,
            ISyntaxFactsService syntaxFacts,
            SyntaxToken token)
        {
            return
                syntaxFacts.TryGetPredefinedType(token, out var actualType) &&
                predefinedType == actualType;
        }

        protected override async ValueTask<ImmutableArray<FinderLocation>> FindReferencesInDocumentAsync(
            IMethodSymbol methodSymbol,
            FindReferencesDocumentState state,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            using var _1 = ArrayBuilder<FinderLocation>.GetInstance(out var result);

            var findParentNode = GetNamedTypeOrConstructorFindParentNodeFunction(state, methodSymbol);

            // First just look for this normal constructor references using the name of it's containing type.
            var name = methodSymbol.ContainingType.Name;
            await AddReferencesInDocumentWorkerAsync(
                methodSymbol, name, state, findParentNode, result, cancellationToken).ConfigureAwait(false);

            // Next, look for constructor references through a global alias to our containing type.
            if (state.GlobalAliases != null)
            {
                foreach (var globalAlias in state.GlobalAliases)
                {
                    // ignore the cases where the global alias might match the type name (i.e.
                    // global alias Console = System.Console).  We'll already find those references
                    // above.
                    if (state.SyntaxFacts.StringComparer.Equals(name, globalAlias))
                        continue;

                    await AddReferencesInDocumentWorkerAsync(
                        methodSymbol, globalAlias, state, findParentNode, result, cancellationToken).ConfigureAwait(false);
                }
            }

            // Nest, our containing type might itself have local aliases to it in this particular file.
            // If so, see what the local aliases are and then search for constructor references to that.
            using var _2 = ArrayBuilder<FinderLocation>.GetInstance(out var typeReferences);
            await NamedTypeSymbolReferenceFinder.AddReferencesToTypeOrGlobalAliasToItAsync(
                methodSymbol.ContainingType, state, typeReferences, cancellationToken).ConfigureAwait(false);

            var aliasReferences = await FindLocalAliasReferencesAsync(
                typeReferences, methodSymbol, state, findParentNode, cancellationToken).ConfigureAwait(false);

            // Finally, look for constructor references to predefined types (like `new int()`),
            // implicit object references, and inside global suppression attributes.
            result.AddRange(await FindPredefinedTypeReferencesAsync(
                methodSymbol, state, cancellationToken).ConfigureAwait(false));

            result.AddRange(await FindReferencesInImplicitObjectCreationExpressionAsync(
                methodSymbol, state, cancellationToken).ConfigureAwait(false));

            result.AddRange(await FindReferencesInDocumentInsideGlobalSuppressionsAsync(
                methodSymbol, state, cancellationToken).ConfigureAwait(false));

            return result.ToImmutable();
        }

        /// <summary>
        /// Finds references to <paramref name="symbol"/> in this <paramref name="state"/>, but only if it referenced
        /// though <paramref name="name"/> (which might be the actual name of the type, or a global alias to it).
        /// </summary>
        private static async Task AddReferencesInDocumentWorkerAsync(
            IMethodSymbol symbol,
            string name,
            FindReferencesDocumentState state,
            Func<SyntaxToken, SyntaxNode>? findParentNode,
            ArrayBuilder<FinderLocation> result,
            CancellationToken cancellationToken)
        {
            result.AddRange(await FindOrdinaryReferencesAsync(
                symbol, name, state, findParentNode, cancellationToken).ConfigureAwait(false));
            result.AddRange(await FindAttributeReferencesAsync(
                symbol, name, state, cancellationToken).ConfigureAwait(false));
        }

        private static ValueTask<ImmutableArray<FinderLocation>> FindOrdinaryReferencesAsync(
            IMethodSymbol symbol,
            string name,
            FindReferencesDocumentState state,
            Func<SyntaxToken, SyntaxNode>? findParentNode,
            CancellationToken cancellationToken)
        {
            return FindReferencesInDocumentUsingIdentifierAsync(
                symbol, name, state, findParentNode, cancellationToken);
        }

        private static ValueTask<ImmutableArray<FinderLocation>> FindPredefinedTypeReferencesAsync(
            IMethodSymbol symbol,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {
            var predefinedType = symbol.ContainingType.SpecialType.ToPredefinedType();
            if (predefinedType == PredefinedType.None)
            {
                return new ValueTask<ImmutableArray<FinderLocation>>(ImmutableArray<FinderLocation>.Empty);
            }

            return FindReferencesInDocumentAsync(
                symbol, state,
                static (state, token, predefinedType, _) => IsPotentialReference(predefinedType, state.SyntaxFacts, token),
                predefinedType,
                cancellationToken);
        }

        private static ValueTask<ImmutableArray<FinderLocation>> FindAttributeReferencesAsync(
            IMethodSymbol symbol,
            string name,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {
            var syntaxFacts = state.SyntaxFacts;
            return TryGetNameWithoutAttributeSuffix(name, syntaxFacts, out var simpleName)
                ? FindReferencesInDocumentUsingIdentifierAsync(symbol, simpleName, state, cancellationToken)
                : new ValueTask<ImmutableArray<FinderLocation>>(ImmutableArray<FinderLocation>.Empty);
        }

        private Task<ImmutableArray<FinderLocation>> FindReferencesInImplicitObjectCreationExpressionAsync(
            IMethodSymbol symbol,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {
{'fraction_non_alphanumeric': 0.06348523932358842, 'fraction_numerical': 0.00019107671730199676, 'mean_word_length': 2.527974384900573, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 91, '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': '836021', 'n_tokens_mistral': 4676, 'n_tokens_neox': 4525, 'n_words': 1141}