|
// 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.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.GenerateMember;
internal abstract partial class AbstractGenerateMemberService<TSimpleNameSyntax, TExpressionSyntax>
where TSimpleNameSyntax : TExpressionSyntax
where TExpressionSyntax : SyntaxNode
{
protected AbstractGenerateMemberService()
{
}
protected static readonly ISet<TypeKind> EnumType = new HashSet<TypeKind> { TypeKind.Enum };
protected static readonly ISet<TypeKind> ClassInterfaceModuleStructTypes = new HashSet<TypeKind>
{
TypeKind.Class,
TypeKind.Module,
TypeKind.Struct,
TypeKind.Interface
};
protected static bool ValidateTypeToGenerateIn(
[NotNullWhen(true)] INamedTypeSymbol? typeToGenerateIn,
bool isStatic,
ISet<TypeKind> typeKinds)
{
if (typeToGenerateIn == null)
return false;
if (typeToGenerateIn.IsAnonymousType)
return false;
if (!typeKinds.Contains(typeToGenerateIn.TypeKind))
return false;
if (typeToGenerateIn.TypeKind == TypeKind.Interface && isStatic)
return false;
// TODO(cyrusn): Make sure that there is a totally visible part somewhere (i.e.
// venus) that we can generate into.
var locations = typeToGenerateIn.Locations;
return locations.Any(static loc => loc.IsInSource);
}
protected static bool TryDetermineTypeToGenerateIn(
SemanticDocument document,
INamedTypeSymbol containingType,
TExpressionSyntax simpleNameOrMemberAccessExpression,
CancellationToken cancellationToken,
[NotNullWhen(true)] out INamedTypeSymbol? typeToGenerateIn,
out bool isStatic,
out bool isColorColorCase)
{
TryDetermineTypeToGenerateInWorker(
document, containingType, simpleNameOrMemberAccessExpression, cancellationToken, out typeToGenerateIn, out isStatic, out isColorColorCase);
typeToGenerateIn = typeToGenerateIn?.OriginalDefinition;
return typeToGenerateIn != null;
}
private static void TryDetermineTypeToGenerateInWorker(
SemanticDocument semanticDocument,
INamedTypeSymbol containingType,
TExpressionSyntax expression,
CancellationToken cancellationToken,
out INamedTypeSymbol? typeToGenerateIn,
out bool isStatic,
out bool isColorColorCase)
{
typeToGenerateIn = null;
isStatic = false;
isColorColorCase = false;
var syntaxFacts = semanticDocument.Document.GetRequiredLanguageService<ISyntaxFactsService>();
var semanticModel = semanticDocument.SemanticModel;
if (syntaxFacts.IsSimpleMemberAccessExpression(expression))
{
// Figure out what's before the dot. For VB, that also means finding out
// what ".X" might mean, even when there's nothing before the dot itself.
var beforeDotExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(
expression, allowImplicitTarget: true);
if (beforeDotExpression != null)
{
DetermineTypeToGenerateInWorker(
semanticModel, beforeDotExpression, out typeToGenerateIn, out isStatic, out isColorColorCase, cancellationToken);
}
return;
}
if (syntaxFacts.IsConditionalAccessExpression(expression))
{
var beforeDotExpression = syntaxFacts.GetExpressionOfConditionalAccessExpression(expression);
if (beforeDotExpression != null)
{
DetermineTypeToGenerateInWorker(
semanticModel, beforeDotExpression, out typeToGenerateIn, out isStatic, out isColorColorCase, cancellationToken);
if (typeToGenerateIn.IsNullable(out var underlyingType) &&
underlyingType is INamedTypeSymbol underlyingNamedType)
{
typeToGenerateIn = underlyingNamedType;
}
}
return;
}
if (syntaxFacts.IsPointerMemberAccessExpression(expression))
{
var beforeArrowExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(expression);
if (beforeArrowExpression != null)
{
var typeInfo = semanticModel.GetTypeInfo(beforeArrowExpression, cancellationToken);
if (typeInfo.Type is IPointerTypeSymbol pointerType)
{
typeToGenerateIn = pointerType.PointedAtType as INamedTypeSymbol;
isStatic = false;
}
}
return;
}
if (syntaxFacts.IsAttributeNamedArgumentIdentifier(expression))
{
var attributeNode = expression.GetAncestors().FirstOrDefault(syntaxFacts.IsAttribute);
Contract.ThrowIfNull(attributeNode);
var attributeName = syntaxFacts.GetNameOfAttribute(attributeNode);
var attributeType = semanticModel.GetTypeInfo(attributeName, cancellationToken);
typeToGenerateIn = attributeType.Type as INamedTypeSymbol;
isStatic = false;
return;
}
if (syntaxFacts.IsMemberInitializerNamedAssignmentIdentifier(
expression, out var initializedObject))
{
typeToGenerateIn = semanticModel.GetTypeInfo(initializedObject, cancellationToken).Type as INamedTypeSymbol;
isStatic = false;
return;
}
else if (syntaxFacts.IsNameOfSubpattern(expression))
{
var propertyPatternClause = expression.Ancestors().FirstOrDefault(syntaxFacts.IsPropertyPatternClause);
if (propertyPatternClause != null)
{
// something like: { [|X|]: int i } or like: Blah { [|X|]: int i }
var inferenceService = semanticDocument.Document.GetRequiredLanguageService<ITypeInferenceService>();
typeToGenerateIn = inferenceService.InferType(semanticModel, propertyPatternClause, objectAsDefault: true, cancellationToken) as INamedTypeSymbol;
isStatic = false;
return;
}
}
// Generating into the containing type.
typeToGenerateIn = containingType;
isStatic = syntaxFacts.IsInStaticContext(expression);
}
private static void DetermineTypeToGenerateInWorker(
SemanticModel semanticModel,
SyntaxNode expression,
out INamedTypeSymbol? typeToGenerateIn,
out bool isStatic,
out bool isColorColorCase,
CancellationToken cancellationToken)
{
var typeInfo = semanticModel.GetTypeInfo(expression, cancellationToken);
var semanticInfo = semanticModel.GetSymbolInfo(expression, cancellationToken);
typeToGenerateIn = typeInfo.Type is ITypeParameterSymbol typeParameter
? typeParameter.GetNamedTypeSymbolConstraint()
: typeInfo.Type as INamedTypeSymbol;
isStatic = semanticInfo.Symbol is INamedTypeSymbol;
isColorColorCase = typeInfo.Type != null && semanticInfo.Symbol != null && semanticInfo.Symbol.Name == typeInfo.Type.Name;
}
}
|