File: Completion\Providers\AbstractObjectCreationCompletionProvider.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Completion.Providers;
 
internal abstract class AbstractObjectCreationCompletionProvider<TSyntaxContext> : AbstractSymbolCompletionProvider<TSyntaxContext>
    where TSyntaxContext : SyntaxContext
{
    /// <summary>
    /// Return null if not in object creation type context.
    /// </summary>
    protected abstract SyntaxNode? GetObjectCreationNewExpression(SyntaxTree tree, int position, CancellationToken cancellationToken);
    protected abstract CompletionItemRules GetCompletionItemRules(ImmutableArray<SymbolAndSelectionInfo> symbols);
 
    protected override CompletionItem CreateItem(
        CompletionContext completionContext,
        string displayText,
        string displayTextSuffix,
        string insertionText,
        ImmutableArray<SymbolAndSelectionInfo> symbols,
        TSyntaxContext context,
        SupportedPlatformData? supportedPlatformData)
    {
        return SymbolCompletionItem.CreateWithSymbolId(
            displayText: displayText,
            displayTextSuffix: displayTextSuffix,
            symbols: symbols.SelectAsArray(t => t.Symbol),
            // Always preselect
            rules: GetCompletionItemRules(symbols).WithMatchPriority(MatchPriority.Preselect),
            contextPosition: context.Position,
            insertionText: insertionText,
            filterText: GetFilterTextDefault(symbols[0].Symbol, displayText, context),
            supportedPlatforms: supportedPlatformData);
    }
 
    protected override Task<ImmutableArray<SymbolAndSelectionInfo>> GetSymbolsAsync(
        CompletionContext? completionContext, TSyntaxContext context, int position, CompletionOptions options, CancellationToken cancellationToken)
    {
        var newExpression = GetObjectCreationNewExpression(context.SyntaxTree, position, cancellationToken);
        if (newExpression == null)
            return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
 
        var typeInferenceService = context.GetRequiredLanguageService<ITypeInferenceService>();
        var type = typeInferenceService.InferType(
            context.SemanticModel, position, objectAsDefault: false, cancellationToken: cancellationToken);
 
        // Unwrap an array type fully.  We only want to offer the underlying element type in the
        // list of completion items.
        var isArray = type is IArrayTypeSymbol;
        while (type is IArrayTypeSymbol arrayType)
            type = arrayType.ElementType;
 
        if (type == null)
            return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
 
        // Unwrap nullable
        if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
            type = type.GetTypeArguments().Single();
 
        if (type.SpecialType == SpecialType.System_Void)
            return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
 
        if (type.ContainsAnonymousType())
            return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
 
        if (!type.CanBeReferencedByName)
            return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
 
        // Normally the user can't say things like "new IList".  Except for "IList[] x = new |".
        // In this case we do want to allow them to preselect certain types in the completion
        // list even if they can't new them directly.
        if (!isArray)
        {
            if (type.TypeKind is TypeKind.Interface or TypeKind.Pointer or TypeKind.Dynamic ||
                type.IsAbstract)
            {
                return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
            }
 
            if (type is ITypeParameterSymbol typeParameter && !typeParameter.HasConstructorConstraint)
                return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
        }
 
        if (!type.IsEditorBrowsable(options.MemberDisplayOptions.HideAdvancedMembers, context.SemanticModel.Compilation))
            return SpecializedTasks.EmptyImmutableArray<SymbolAndSelectionInfo>();
 
        // In the case of array creation, we don't offer a preselected/hard-selected item because
        // the user may want an implicitly-typed array creation
        return Task.FromResult(ImmutableArray.Create(new SymbolAndSelectionInfo(Symbol: type, Preselect: !isArray)));
    }
}