|
// 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;
}
}
}
|