|
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Microsoft.CodeAnalysis.LanguageService;
internal abstract partial class AbstractSemanticFactsService : ISemanticFacts
{
public abstract ISyntaxFacts SyntaxFacts { get; }
public abstract IBlockFacts BlockFacts { get; }
protected abstract ISemanticFacts SemanticFacts { get; }
protected abstract SyntaxToken ToIdentifierToken(string identifier);
// local name can be same as field or property. but that will hide
// those and can cause semantic change later in some context.
// so to be safe, we consider field and property in scope when
// creating unique name for local
private static readonly Func<ISymbol, bool> s_LocalNameFilter = s =>
s.Kind is SymbolKind.Local or
SymbolKind.Parameter or
SymbolKind.RangeVariable or
SymbolKind.Field or
SymbolKind.Property ||
s is { Kind: SymbolKind.NamedType, IsStatic: true };
public SyntaxToken GenerateUniqueName(
SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt,
string baseName, CancellationToken cancellationToken)
{
return GenerateUniqueName(
semanticModel, location, containerOpt, baseName, filter: null, usedNames: null, cancellationToken);
}
public SyntaxToken GenerateUniqueName(
SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt,
string baseName, IEnumerable<string> usedNames, CancellationToken cancellationToken)
{
return GenerateUniqueName(
semanticModel, location, containerOpt, baseName, filter: null, usedNames, cancellationToken);
}
public SyntaxToken GenerateUniqueLocalName(
SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt,
string baseName, CancellationToken cancellationToken)
{
return GenerateUniqueName(
semanticModel, location, containerOpt, baseName, s_LocalNameFilter, usedNames: [], cancellationToken);
}
public SyntaxToken GenerateUniqueLocalName(
SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt,
string baseName, IEnumerable<string> usedNames, CancellationToken cancellationToken)
{
return GenerateUniqueName(
semanticModel, location, containerOpt, baseName, s_LocalNameFilter, usedNames: usedNames, cancellationToken);
}
public SyntaxToken GenerateUniqueName(
SemanticModel semanticModel,
SyntaxNode location, SyntaxNode containerOpt,
string baseName, Func<ISymbol, bool> filter,
IEnumerable<string> usedNames, CancellationToken cancellationToken)
{
var container = containerOpt ?? location.AncestorsAndSelf().FirstOrDefault(
a => BlockFacts.IsExecutableBlock(a) || SyntaxFacts.IsParameterList(a) || SyntaxFacts.IsMethodBody(a));
var candidates = GetCollidableSymbols(semanticModel, location, container, cancellationToken);
var filteredCandidates = filter != null ? candidates.Where(filter) : candidates;
return GenerateUniqueName(baseName, filteredCandidates.Select(s => s.Name).Concat(usedNames));
}
/// <summary>
/// Retrieves all symbols that could collide with a symbol at the specified location.
/// A symbol can possibly collide with the location if it is available to that location and/or
/// could cause a compiler error if its name is re-used at that location.
/// </summary>
protected virtual IEnumerable<ISymbol> GetCollidableSymbols(SemanticModel semanticModel, SyntaxNode location, SyntaxNode container, CancellationToken cancellationToken)
=> semanticModel.LookupSymbols(location.SpanStart).Concat(semanticModel.GetExistingSymbols(container, cancellationToken));
public SyntaxToken GenerateUniqueName(string baseName, IEnumerable<string> usedNames)
{
return this.ToIdentifierToken(
NameGenerator.EnsureUniqueness(
baseName, usedNames, this.SyntaxFacts.IsCaseSensitive));
}
#nullable enable
protected static IMethodSymbol? FindDisposeMethod(Compilation compilation, ITypeSymbol? type, bool isAsync)
{
if (type is null)
return null;
var (iDisposableInterfaceType, disposeMethodToLookFor) = isAsync
? GetIDisposableInterfaceAndDisposeMethod(typeof(IAsyncDisposable).FullName!, nameof(IAsyncDisposable.DisposeAsync))
: GetIDisposableInterfaceAndDisposeMethod(typeof(IDisposable).FullName!, nameof(IDisposable.Dispose));
if (disposeMethodToLookFor is null)
return null;
var impl = type.FindImplementationForInterfaceMember(disposeMethodToLookFor);
if (impl is IMethodSymbol implMethod)
{
return implMethod;
}
// If we didn't find implementation for the method
// look for the method with the right signature.
// This will help with error recovery and produce correct result
// for the case with a pattern-based using statement on `ref struct`s
if (isAsync)
{
var valueTaskType = compilation.ValueTaskType();
var currentType = type;
while (currentType is not null)
{
if (currentType
.GetMembers(nameof(IAsyncDisposable.DisposeAsync))
.FirstOrDefault(m => m is IMethodSymbol { DeclaredAccessibility: Accessibility.Public, ReturnType: var returnType, Parameters.Length: 0 } &&
SymbolEqualityComparer.Default.Equals(returnType, valueTaskType)) is IMethodSymbol disposeMethodFromPattern)
{
return disposeMethodFromPattern;
}
currentType = currentType.BaseType;
}
}
else
{
var currentType = type;
while (currentType is not null)
{
if (currentType
.GetMembers(nameof(IDisposable.Dispose))
.FirstOrDefault(m => m is IMethodSymbol { DeclaredAccessibility: Accessibility.Public, ReturnsVoid: true, Parameters.Length: 0 }) is IMethodSymbol disposeMethodFromPattern)
{
return disposeMethodFromPattern;
}
currentType = currentType.BaseType;
}
}
// If type doesn't implement disposable interface and doesn't have dispose method with right shape
// return null rather than completely unrelated dispose method for that type
return type.Implements(iDisposableInterfaceType!) ? disposeMethodToLookFor : null;
(INamedTypeSymbol?, IMethodSymbol?) GetIDisposableInterfaceAndDisposeMethod(string typeName, string methodName)
{
var disposableType = compilation.GetBestTypeByMetadataName(typeName);
return (disposableType, disposableType?.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(m => m.Parameters.Length == 0 && m.Name == methodName));
}
}
#nullable disable
#region ISemanticFacts implementation
public bool SupportsImplicitInterfaceImplementation => SemanticFacts.SupportsImplicitInterfaceImplementation;
public bool SupportsParameterizedProperties => SemanticFacts.SupportsParameterizedProperties;
public bool ExposesAnonymousFunctionParameterNames => SemanticFacts.ExposesAnonymousFunctionParameterNames;
public bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsWrittenTo(semanticModel, node, cancellationToken);
public bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsOnlyWrittenTo(semanticModel, node, cancellationToken);
public bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInOutContext(semanticModel, node, cancellationToken);
public bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInRefContext(semanticModel, node, cancellationToken);
public bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInInContext(semanticModel, node, cancellationToken);
public bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
=> SemanticFacts.CanReplaceWithRValue(semanticModel, expression, cancellationToken);
public ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken)
=> SemanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken);
public bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol)
=> SemanticFacts.LastEnumValueHasInitializer(namedTypeSymbol);
public bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel)
=> SemanticFacts.TryGetSpeculativeSemanticModel(oldSemanticModel, oldNode, newNode, out speculativeModel);
public ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken)
=> SemanticFacts.GetAliasNameSet(model, cancellationToken);
public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement)
=> SemanticFacts.GetForEachSymbols(semanticModel, forEachStatement);
public SymbolInfo GetCollectionInitializerSymbolInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.GetCollectionInitializerSymbolInfo(semanticModel, node, cancellationToken);
public IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node)
=> SemanticFacts.GetGetAwaiterMethod(semanticModel, node);
public ImmutableArray<IMethodSymbol> GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node)
=> SemanticFacts.GetDeconstructionAssignmentMethods(semanticModel, node);
public ImmutableArray<IMethodSymbol> GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node)
=> SemanticFacts.GetDeconstructionForEachMethods(semanticModel, node);
public bool IsPartial(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken)
=> SemanticFacts.IsPartial(typeSymbol, cancellationToken);
public IEnumerable<ISymbol> GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken)
=> SemanticFacts.GetDeclaredSymbols(semanticModel, memberDeclaration, cancellationToken);
public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken)
=> SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates, allowParams, cancellationToken);
public IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken)
=> SemanticFacts.FindParameterForAttributeArgument(semanticModel, argumentNode, allowUncertainCandidates, allowParams, cancellationToken);
public ISymbol FindFieldOrPropertyForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken)
=> SemanticFacts.FindFieldOrPropertyForArgument(semanticModel, argumentNode, cancellationToken);
public ISymbol FindFieldOrPropertyForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken)
=> SemanticFacts.FindFieldOrPropertyForAttributeArgument(semanticModel, argumentNode, cancellationToken);
public ImmutableArray<ISymbol> GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken)
=> SemanticFacts.GetBestOrAllSymbols(semanticModel, node, token, cancellationToken);
public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInsideNameOfExpression(semanticModel, node, cancellationToken);
public ImmutableArray<IMethodSymbol> GetLocalFunctionSymbols(Compilation compilation, ISymbol symbol, CancellationToken cancellationToken)
=> SemanticFacts.GetLocalFunctionSymbols(compilation, symbol, cancellationToken);
public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken)
=> SemanticFacts.IsInExpressionTree(semanticModel, node, expressionTypeOpt, cancellationToken);
public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken)
=> SemanticFacts.GenerateNameForExpression(semanticModel, expression, capitalize, cancellationToken);
#if !CODE_STYLE
public Task<ISymbol> GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken)
=> SemanticFacts.GetInterceptorSymbolAsync(document, position, cancellationToken);
#endif
#endregion
}
|