File: Recommendations\AbstractRecommendationService.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.
 
#nullable disable
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Recommendations;
 
internal abstract partial class AbstractRecommendationService<
    TSyntaxContext,
    TAnonymousFunctionSyntax> : IRecommendationService
    where TSyntaxContext : SyntaxContext
    where TAnonymousFunctionSyntax : SyntaxNode
{
    protected abstract AbstractRecommendationServiceRunner CreateRunner(
        TSyntaxContext context, bool filterOutOfScopeLocals, CancellationToken cancellationToken);
 
    public RecommendedSymbols GetRecommendedSymbolsInContext(SyntaxContext syntaxContext, RecommendationServiceOptions options, CancellationToken cancellationToken)
    {
        var semanticModel = syntaxContext.SemanticModel;
        var result = CreateRunner((TSyntaxContext)syntaxContext, options.FilterOutOfScopeLocals, cancellationToken).GetRecommendedSymbols();
 
        var namedSymbols = result.NamedSymbols;
        var unnamedSymbols = result.UnnamedSymbols;
 
        namedSymbols = namedSymbols.FilterToVisibleAndBrowsableSymbols(options.HideAdvancedMembers, semanticModel.Compilation);
        unnamedSymbols = unnamedSymbols.FilterToVisibleAndBrowsableSymbols(options.HideAdvancedMembers, semanticModel.Compilation);
 
        var shouldIncludeSymbolContext = new ShouldIncludeSymbolContext(syntaxContext, cancellationToken);
        namedSymbols = namedSymbols.WhereAsArray(shouldIncludeSymbolContext.ShouldIncludeSymbol);
        unnamedSymbols = unnamedSymbols.WhereAsArray(shouldIncludeSymbolContext.ShouldIncludeSymbol);
 
        return new RecommendedSymbols(namedSymbols, unnamedSymbols);
    }
 
    protected static ISet<INamedTypeSymbol> ComputeOuterTypes(SyntaxContext context, CancellationToken cancellationToken)
    {
        var enclosingSymbol = context.SemanticModel.GetEnclosingSymbol(context.LeftToken.SpanStart, cancellationToken);
        if (enclosingSymbol != null)
        {
            var containingType = enclosingSymbol.GetContainingTypeOrThis();
            if (containingType != null)
            {
                return containingType.GetContainingTypes().ToSet();
            }
        }
 
        return SpecializedCollections.EmptySet<INamedTypeSymbol>();
    }
 
    private sealed class ShouldIncludeSymbolContext
    {
        private readonly SyntaxContext _context;
        private readonly CancellationToken _cancellationToken;
        private ImmutableArray<INamedTypeSymbol> _lazyOuterTypesAndBases;
        private ImmutableArray<INamedTypeSymbol> _lazyEnclosingTypeBases;
 
        internal ShouldIncludeSymbolContext(SyntaxContext context, CancellationToken cancellationToken)
        {
            _context = context;
            _cancellationToken = cancellationToken;
        }
 
        internal bool ShouldIncludeSymbol(ISymbol symbol)
        {
            var isMember = false;
            switch (symbol.Kind)
            {
                case SymbolKind.NamedType:
                    var namedType = (INamedTypeSymbol)symbol;
                    if (namedType.SpecialType == SpecialType.System_Void)
                    {
                        return false;
                    }
 
                    break;
 
                case SymbolKind.Method:
                    switch (((IMethodSymbol)symbol).MethodKind)
                    {
                        case MethodKind.EventAdd:
                        case MethodKind.EventRemove:
                        case MethodKind.EventRaise:
                        case MethodKind.PropertyGet:
                        case MethodKind.PropertySet:
                            return false;
                    }
 
                    isMember = true;
                    break;
 
                case SymbolKind.Event:
                case SymbolKind.Field:
                case SymbolKind.Property:
                    isMember = true;
                    break;
 
                case SymbolKind.TypeParameter:
                    return ((ITypeParameterSymbol)symbol).TypeParameterKind != TypeParameterKind.Cref;
            }
 
            if (_context.IsAttributeNameContext)
            {
                return symbol.IsOrContainsAccessibleAttribute(
                    _context.SemanticModel.GetEnclosingNamedType(_context.LeftToken.SpanStart, _cancellationToken),
                    _context.SemanticModel.Compilation.Assembly,
                    _cancellationToken);
            }
 
            if (_context.IsEnumTypeMemberAccessContext)
            {
                return symbol.Kind == SymbolKind.Field;
            }
 
            // In an expression or statement context, we don't want to display instance members declared in outer containing types.
            if ((_context.IsStatementContext || _context.IsAnyExpressionContext) &&
                !symbol.IsStatic &&
                isMember)
            {
                var containingTypeOriginalDefinition = symbol.ContainingType.OriginalDefinition;
                if (this.GetOuterTypesAndBases().Contains(containingTypeOriginalDefinition))
                {
                    return this.GetEnclosingTypeBases().Contains(containingTypeOriginalDefinition);
                }
            }
 
            if (symbol is INamespaceSymbol namespaceSymbol)
            {
                return namespaceSymbol.ContainsAccessibleTypesOrNamespaces(_context.SemanticModel.Compilation.Assembly);
            }
 
            return true;
        }
 
        private ImmutableArray<INamedTypeSymbol> GetOuterTypesAndBases()
        {
            if (_lazyOuterTypesAndBases.IsDefault)
            {
                _lazyOuterTypesAndBases = ComputeOuterTypes(_context, _cancellationToken)
                    .SelectMany(o => o.GetBaseTypesAndThis())
                    .SelectAsArray(t => t.OriginalDefinition);
            }
 
            return _lazyOuterTypesAndBases;
        }
 
        private ImmutableArray<INamedTypeSymbol> GetEnclosingTypeBases()
        {
            if (_lazyEnclosingTypeBases.IsDefault)
            {
                var enclosingType = _context.SemanticModel.GetEnclosingNamedType(_context.LeftToken.SpanStart, _cancellationToken);
                _lazyEnclosingTypeBases = enclosingType == null
                    ? []
                    : enclosingType.GetBaseTypes().SelectAsArray(b => b.OriginalDefinition);
            }
 
            return _lazyEnclosingTypeBases;
        }
    }
}