| File: SignatureHelp\InvocationExpressionSignatureHelpProviderBase_MethodGroup.cs | Web Access |
| Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.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.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; internal abstract partial class InvocationExpressionSignatureHelpProviderBase { internal virtual Task<(ImmutableArray<SignatureHelpItem> items, int? selectedItemIndex)> GetMethodGroupItemsAndSelectionAsync( ImmutableArray<IMethodSymbol> accessibleMethods, Document document, InvocationExpressionSyntax invocationExpression, SemanticModel semanticModel, SymbolInfo symbolInfo, IMethodSymbol? currentSymbol, CancellationToken cancellationToken) { var items = accessibleMethods.SelectAsArray(method => ConvertMethodGroupMethod( document, method, invocationExpression.SpanStart, semanticModel)); var selectedItemIndex = TryGetSelectedIndex(accessibleMethods, currentSymbol); return Task.FromResult((items, selectedItemIndex)); } private static ImmutableArray<IMethodSymbol> GetAccessibleMethods( InvocationExpressionSyntax invocationExpression, SemanticModel semanticModel, ISymbol within, IEnumerable<IMethodSymbol> methodGroup, CancellationToken cancellationToken) { ITypeSymbol? throughType = null; if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccess) { var throughExpression = memberAccess.Expression; var throughSymbol = semanticModel.GetSymbolInfo(throughExpression, cancellationToken).GetAnySymbol(); // if it is via a base expression "base.", we know the "throughType" is the base class but // we need to be able to tell between "base.M()" and "new Base().M()". // currently, Access check methods do not differentiate between them. // so handle "base." primary-expression here by nulling out "throughType" if (throughExpression is not BaseExpressionSyntax) { throughType = semanticModel.GetTypeInfo(throughExpression, cancellationToken).Type; } // SyntaxKind.IdentifierName is for basic case, e.g. "MyClass.MyStaticMethod(...)" // SyntaxKind.SimpleMemberAccessExpression is for not imported types, e.g. "MyNamespace.MyClass.MyStaticMethod(...)" // SyntaxKind.PredefinedType is for built-in types, e.g. "string.Equals(...)" var includeInstance = throughExpression.Kind() is not (SyntaxKind.IdentifierName or SyntaxKind.SimpleMemberAccessExpression or SyntaxKind.PredefinedType) || throughSymbol is not INamespaceOrTypeSymbol || semanticModel.LookupSymbols(throughExpression.SpanStart, name: throughSymbol?.Name).Any(static s => s is not INamedTypeSymbol); var includeStatic = throughSymbol is INamedTypeSymbol || (throughExpression.IsKind(SyntaxKind.IdentifierName) && semanticModel.LookupNamespacesAndTypes(throughExpression.SpanStart, name: throughSymbol?.Name).Any(static (t, throughType) => Equals(t.GetSymbolType(), throughType), throughType)); Contract.ThrowIfFalse(includeInstance || includeStatic); methodGroup = methodGroup.Where(m => (m.IsStatic && includeStatic) || (!m.IsStatic && includeInstance)); } else if (invocationExpression.Expression is SimpleNameSyntax && invocationExpression.IsInStaticContext()) { // We always need to include local functions regardless of whether they are static. methodGroup = methodGroup.Where(m => m.IsStatic || m is IMethodSymbol { MethodKind: MethodKind.LocalFunction }); } var accessibleMethods = methodGroup.Where(m => m.IsAccessibleWithin(within, throughType: throughType)).ToImmutableArrayOrEmpty(); if (accessibleMethods.Length == 0) { return accessibleMethods; } var methodSet = accessibleMethods.ToSet(); return accessibleMethods.Where(m => !IsHiddenByOtherMethod(m, methodSet)).ToImmutableArrayOrEmpty(); } private static bool IsHiddenByOtherMethod(IMethodSymbol method, ISet<IMethodSymbol> methodSet) { foreach (var m in methodSet) { if (!Equals(m, method)) { if (IsHiddenBy(method, m)) { return true; } } } return false; } private static bool IsHiddenBy(IMethodSymbol method1, IMethodSymbol method2) { // If they have the same parameter types and the same parameter names, then the // constructed method is hidden by the unconstructed one. return method2.IsMoreSpecificThan(method1) == true; } }