|
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
// This is a type symbol associated with a type definition in source code.
// That is, for a generic type C<T> this is the instance type C<T>.
internal sealed partial class SourceNamedTypeSymbol : SourceMemberContainerTypeSymbol, IAttributeTargetSymbol
{
private readonly TypeParameterInfo _typeParameterInfo;
private CustomAttributesBag<CSharpAttributeData> _lazyCustomAttributesBag;
private string _lazyDocComment;
private string _lazyExpandedDocComment;
private ThreeState _lazyIsExplicitDefinitionOfNoPiaLocalType = ThreeState.Unknown;
protected override Location GetCorrespondingBaseListLocation(NamedTypeSymbol @base)
{
Location backupLocation = null;
foreach (SyntaxReference part in SyntaxReferences)
{
TypeDeclarationSyntax typeBlock = (TypeDeclarationSyntax)part.GetSyntax();
BaseListSyntax bases = typeBlock.BaseList;
if (bases == null)
{
continue;
}
SeparatedSyntaxList<BaseTypeSyntax> inheritedTypeDecls = bases.Types;
var baseBinder = this.DeclaringCompilation.GetBinder(bases);
baseBinder = baseBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this);
if ((object)backupLocation == null)
{
backupLocation = inheritedTypeDecls[0].Type.GetLocation();
}
foreach (BaseTypeSyntax baseTypeSyntax in inheritedTypeDecls)
{
TypeSyntax t = baseTypeSyntax.Type;
TypeSymbol bt = baseBinder.BindType(t, BindingDiagnosticBag.Discarded).Type;
if (TypeSymbol.Equals(bt, @base, TypeCompareKind.ConsiderEverything2))
{
return t.GetLocation();
}
}
}
return backupLocation;
}
internal SourceNamedTypeSymbol(NamespaceOrTypeSymbol containingSymbol, MergedTypeDeclaration declaration, BindingDiagnosticBag diagnostics, TupleExtraData tupleData = null)
: base(containingSymbol, declaration, diagnostics, tupleData)
{
switch (declaration.Kind)
{
case DeclarationKind.Struct:
case DeclarationKind.Interface:
case DeclarationKind.Enum:
case DeclarationKind.Delegate:
case DeclarationKind.Class:
case DeclarationKind.Record:
case DeclarationKind.RecordStruct:
break;
default:
Debug.Assert(false, "bad declaration kind");
break;
}
if (containingSymbol.Kind == SymbolKind.NamedType)
{
// Nested types are never unified.
_lazyIsExplicitDefinitionOfNoPiaLocalType = ThreeState.False;
}
_typeParameterInfo = declaration.Arity == 0
? TypeParameterInfo.Empty
: new TypeParameterInfo();
}
protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData)
{
return new SourceNamedTypeSymbol(ContainingType, declaration, BindingDiagnosticBag.Discarded, newData);
}
#region Syntax
private static SyntaxToken GetName(CSharpSyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.EnumDeclaration:
return ((EnumDeclarationSyntax)node).Identifier;
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)node).Identifier;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.RecordDeclaration:
case SyntaxKind.RecordStructDeclaration:
return ((BaseTypeDeclarationSyntax)node).Identifier;
default:
return default(SyntaxToken);
}
}
public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
ref var lazyDocComment = ref expandIncludes ? ref _lazyExpandedDocComment : ref _lazyDocComment;
return SourceDocumentationCommentUtils.GetAndCacheDocumentationComment(this, expandIncludes, ref lazyDocComment);
}
#endregion
#region Type Parameters
private ImmutableArray<TypeParameterSymbol> MakeTypeParameters(BindingDiagnosticBag diagnostics)
{
if (declaration.Arity == 0)
{
return ImmutableArray<TypeParameterSymbol>.Empty;
}
var typeParameterMismatchReported = false;
var typeParameterNames = new string[declaration.Arity];
var typeParameterVarianceKeywords = new string[declaration.Arity];
var parameterBuilders1 = new List<List<TypeParameterBuilder>>();
foreach (var syntaxRef in this.SyntaxReferences)
{
var typeDecl = (CSharpSyntaxNode)syntaxRef.GetSyntax();
var syntaxTree = syntaxRef.SyntaxTree;
TypeParameterListSyntax tpl;
SyntaxKind typeKind = typeDecl.Kind();
switch (typeKind)
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.RecordDeclaration:
case SyntaxKind.RecordStructDeclaration:
tpl = ((TypeDeclarationSyntax)typeDecl).TypeParameterList;
break;
case SyntaxKind.DelegateDeclaration:
tpl = ((DelegateDeclarationSyntax)typeDecl).TypeParameterList;
break;
case SyntaxKind.EnumDeclaration:
default:
// there is no such thing as a generic enum, so code should never reach here.
throw ExceptionUtilities.UnexpectedValue(typeDecl.Kind());
}
MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, tpl.LessThanToken);
bool isInterfaceOrDelegate = typeKind == SyntaxKind.InterfaceDeclaration || typeKind == SyntaxKind.DelegateDeclaration;
var parameterBuilder = new List<TypeParameterBuilder>();
parameterBuilders1.Add(parameterBuilder);
int i = 0;
foreach (var tp in tpl.Parameters)
{
if (tp.VarianceKeyword.Kind() != SyntaxKind.None)
{
if (!isInterfaceOrDelegate)
{
diagnostics.Add(ErrorCode.ERR_IllegalVarianceSyntax, tp.VarianceKeyword.GetLocation());
}
else
{
MessageID.IDS_FeatureTypeVariance.CheckFeatureAvailability(diagnostics, tp.VarianceKeyword);
}
}
var name = typeParameterNames[i];
var location = new SourceLocation(tp.Identifier);
var varianceKind = typeParameterVarianceKeywords[i];
ReportReservedTypeName(tp.Identifier.Text, this.DeclaringCompilation, diagnostics.DiagnosticBag, location);
if (name == null)
{
name = typeParameterNames[i] = tp.Identifier.ValueText;
varianceKind = typeParameterVarianceKeywords[i] = tp.VarianceKeyword.ValueText;
for (int j = 0; j < i; j++)
{
if (name == typeParameterNames[j])
{
typeParameterMismatchReported = true;
diagnostics.Add(ErrorCode.ERR_DuplicateTypeParameter, location, name);
goto next;
}
}
if (!ReferenceEquals(ContainingType, null))
{
var tpEnclosing = ContainingType.FindEnclosingTypeParameter(name);
if ((object)tpEnclosing != null)
{
// Type parameter '{0}' has the same name as the type parameter from outer type '{1}'
diagnostics.Add(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, location, name, tpEnclosing.ContainingType);
}
}
next:;
}
else if (!typeParameterMismatchReported)
{
// Note: the "this", below, refers to the name of the current class, which includes its type
// parameter names. But the type parameter names have not been computed yet. Therefore, we
// take advantage of the fact that "this" won't undergo "ToString()" until later, when the
// diagnostic is printed, by which time the type parameters field will have been filled in.
if (varianceKind != tp.VarianceKeyword.ValueText)
{
// Dev10 reports CS1067, even if names also don't match
typeParameterMismatchReported = true;
diagnostics.Add(
ErrorCode.ERR_PartialWrongTypeParamsVariance,
declaration.NameLocations.First(),
this); // see comment above
}
else if (name != tp.Identifier.ValueText)
{
typeParameterMismatchReported = true;
diagnostics.Add(
ErrorCode.ERR_PartialWrongTypeParams,
declaration.NameLocations.First(),
this); // see comment above
}
}
parameterBuilder.Add(new TypeParameterBuilder(syntaxTree.GetReference(tp), this, location));
i++;
}
}
var parameterBuilders2 = parameterBuilders1.Transpose(); // type arguments are positional
var parameters = parameterBuilders2.Select((builders, i) => builders[0].MakeSymbol(i, builders, diagnostics));
return parameters.AsImmutable();
}
/// <summary>
/// Returns the constraint types for the given type parameter.
/// </summary>
internal ImmutableArray<TypeWithAnnotations> GetTypeParameterConstraintTypes(int ordinal)
{
var constraintTypes = GetTypeParameterConstraintTypes();
return (constraintTypes.Length > 0) ? constraintTypes[ordinal] : ImmutableArray<TypeWithAnnotations>.Empty;
}
private ImmutableArray<ImmutableArray<TypeWithAnnotations>> GetTypeParameterConstraintTypes()
{
if (_typeParameterInfo.LazyTypeParameterConstraintTypes.IsDefault)
{
GetTypeParameterConstraintKinds();
var diagnostics = BindingDiagnosticBag.GetInstance();
if (ImmutableInterlocked.InterlockedInitialize(
ref _typeParameterInfo.LazyTypeParameterConstraintTypes,
MakeTypeParameterConstraintTypes(diagnostics)))
{
this.AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
Debug.Assert(!_typeParameterInfo.LazyTypeParameterConstraintTypes.IsDefault);
return _typeParameterInfo.LazyTypeParameterConstraintTypes;
}
/// <summary>
/// Returns the constraint kind for the given type parameter.
/// </summary>
internal TypeParameterConstraintKind GetTypeParameterConstraintKind(int ordinal)
{
var constraintKinds = GetTypeParameterConstraintKinds();
return (constraintKinds.Length > 0) ? constraintKinds[ordinal] : TypeParameterConstraintKind.None;
}
private ImmutableArray<TypeParameterConstraintKind> GetTypeParameterConstraintKinds()
{
if (_typeParameterInfo.LazyTypeParameterConstraintKinds.IsDefault)
{
ImmutableInterlocked.InterlockedInitialize(
ref _typeParameterInfo.LazyTypeParameterConstraintKinds,
MakeTypeParameterConstraintKinds());
}
Debug.Assert(!_typeParameterInfo.LazyTypeParameterConstraintKinds.IsDefault);
return _typeParameterInfo.LazyTypeParameterConstraintKinds;
}
private ImmutableArray<ImmutableArray<TypeWithAnnotations>> MakeTypeParameterConstraintTypes(BindingDiagnosticBag diagnostics)
{
var typeParameters = this.TypeParameters;
var results = ImmutableArray<TypeParameterConstraintClause>.Empty;
int arity = typeParameters.Length;
if (arity > 0)
{
bool skipPartialDeclarationsWithoutConstraintClauses = SkipPartialDeclarationsWithoutConstraintClauses();
ArrayBuilder<ImmutableArray<TypeParameterConstraintClause>> otherPartialClauses = null;
foreach (var decl in declaration.Declarations)
{
var syntaxRef = decl.SyntaxReference;
var constraintClauses = GetConstraintClauses((CSharpSyntaxNode)syntaxRef.GetSyntax(), out TypeParameterListSyntax typeParameterList);
if (skipPartialDeclarationsWithoutConstraintClauses && constraintClauses.Count == 0)
{
continue;
}
var binderFactory = this.DeclaringCompilation.GetBinderFactory(syntaxRef.SyntaxTree);
Binder binder;
ImmutableArray<TypeParameterConstraintClause> constraints;
if (constraintClauses.Count == 0)
{
binder = binderFactory.GetBinder(typeParameterList.Parameters[0]);
constraints = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);
}
else
{
binder = binderFactory.GetBinder(constraintClauses[0]);
// Wrap binder from factory in a generic constraints specific binder
// to avoid checking constraints when binding type names.
Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause));
binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks);
constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, typeParameterList, constraintClauses, diagnostics, performOnlyCycleSafeValidation: false);
}
Debug.Assert(constraints.Length == arity);
if (results.Length == 0)
{
results = constraints;
}
else
{
(otherPartialClauses ??= ArrayBuilder<ImmutableArray<TypeParameterConstraintClause>>.GetInstance()).Add(constraints);
}
}
results = MergeConstraintTypesForPartialDeclarations(results, otherPartialClauses, diagnostics);
if (results.All(clause => clause.ConstraintTypes.IsEmpty))
{
results = ImmutableArray<TypeParameterConstraintClause>.Empty;
}
otherPartialClauses?.Free();
}
return results.SelectAsArray(clause => clause.ConstraintTypes);
}
private bool SkipPartialDeclarationsWithoutConstraintClauses()
{
foreach (var decl in declaration.Declarations)
{
if (GetConstraintClauses((CSharpSyntaxNode)decl.SyntaxReference.GetSyntax(), out _).Count != 0)
{
return true;
}
}
return false;
}
private ImmutableArray<TypeParameterConstraintKind> MakeTypeParameterConstraintKinds()
{
var typeParameters = this.TypeParameters;
var results = ImmutableArray<TypeParameterConstraintClause>.Empty;
int arity = typeParameters.Length;
if (arity > 0)
{
bool skipPartialDeclarationsWithoutConstraintClauses = SkipPartialDeclarationsWithoutConstraintClauses();
ArrayBuilder<ImmutableArray<TypeParameterConstraintClause>> otherPartialClauses = null;
foreach (var decl in declaration.Declarations)
{
var syntaxRef = decl.SyntaxReference;
var constraintClauses = GetConstraintClauses((CSharpSyntaxNode)syntaxRef.GetSyntax(), out TypeParameterListSyntax typeParameterList);
if (skipPartialDeclarationsWithoutConstraintClauses && constraintClauses.Count == 0)
{
continue;
}
var binderFactory = this.DeclaringCompilation.GetBinderFactory(syntaxRef.SyntaxTree);
Binder binder;
ImmutableArray<TypeParameterConstraintClause> constraints;
if (constraintClauses.Count == 0)
{
binder = binderFactory.GetBinder(typeParameterList.Parameters[0]);
constraints = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);
}
else
{
binder = binderFactory.GetBinder(constraintClauses[0]);
// Wrap binder from factory in a generic constraints specific binder
// to avoid checking constraints when binding type names.
// Also, suppress type argument binding in constraint types, this helps to avoid cycles while we figure out constraint kinds.
Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause));
binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressTypeArgumentBinding);
// We will recompute this diagnostics more accurately later, when binding without BinderFlags.SuppressTypeArgumentBinding
constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, typeParameterList, constraintClauses, BindingDiagnosticBag.Discarded, performOnlyCycleSafeValidation: true);
}
Debug.Assert(constraints.Length == arity);
if (results.Length == 0)
{
results = constraints;
}
else
{
(otherPartialClauses ??= ArrayBuilder<ImmutableArray<TypeParameterConstraintClause>>.GetInstance()).Add(constraints);
}
}
results = MergeConstraintKindsForPartialDeclarations(results, otherPartialClauses);
results = ConstraintsHelper.AdjustConstraintKindsBasedOnConstraintTypes(typeParameters, results);
if (results.All(clause => clause.Constraints == TypeParameterConstraintKind.None))
{
results = ImmutableArray<TypeParameterConstraintClause>.Empty;
}
otherPartialClauses?.Free();
}
return results.SelectAsArray(clause => clause.Constraints);
}
private static SyntaxList<TypeParameterConstraintClauseSyntax> GetConstraintClauses(CSharpSyntaxNode node, out TypeParameterListSyntax typeParameterList)
{
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.RecordDeclaration:
case SyntaxKind.RecordStructDeclaration:
var typeDeclaration = (TypeDeclarationSyntax)node;
typeParameterList = typeDeclaration.TypeParameterList;
return typeDeclaration.ConstraintClauses;
case SyntaxKind.DelegateDeclaration:
var delegateDeclaration = (DelegateDeclarationSyntax)node;
typeParameterList = delegateDeclaration.TypeParameterList;
return delegateDeclaration.ConstraintClauses;
default:
throw ExceptionUtilities.UnexpectedValue(node.Kind());
}
}
/// <summary>
/// Note, only nullability aspects are merged if possible, other mismatches are treated as failures.
/// </summary>
private ImmutableArray<TypeParameterConstraintClause> MergeConstraintTypesForPartialDeclarations(ImmutableArray<TypeParameterConstraintClause> constraintClauses,
ArrayBuilder<ImmutableArray<TypeParameterConstraintClause>> otherPartialClauses,
BindingDiagnosticBag diagnostics)
{
if (otherPartialClauses == null)
{
return constraintClauses;
}
ArrayBuilder<TypeParameterConstraintClause> builder = null;
var typeParameters = TypeParameters;
int arity = typeParameters.Length;
Debug.Assert(constraintClauses.Length == arity);
for (int i = 0; i < arity; i++)
{
var constraint = constraintClauses[i];
ImmutableArray<TypeWithAnnotations> originalConstraintTypes = constraint.ConstraintTypes;
ArrayBuilder<TypeWithAnnotations> mergedConstraintTypes = null;
SmallDictionary<TypeWithAnnotations, int> originalConstraintTypesMap = null;
// Constraints defined on multiple partial declarations.
// Report any mismatched constraints.
bool report = (GetTypeParameterConstraintKind(i) & TypeParameterConstraintKind.PartialMismatch) != 0;
foreach (ImmutableArray<TypeParameterConstraintClause> otherPartialConstraints in otherPartialClauses)
{
if (!mergeConstraints(originalConstraintTypes, ref originalConstraintTypesMap, ref mergedConstraintTypes, otherPartialConstraints[i]))
{
report = true;
}
}
if (report)
{
// "Partial declarations of '{0}' have inconsistent constraints for type parameter '{1}'"
diagnostics.Add(ErrorCode.ERR_PartialWrongConstraints, GetFirstLocation(), this, typeParameters[i]);
}
if (mergedConstraintTypes != null)
{
#if DEBUG
Debug.Assert(originalConstraintTypes.Length == mergedConstraintTypes.Count);
for (int j = 0; j < originalConstraintTypes.Length; j++)
{
Debug.Assert(originalConstraintTypes[j].Equals(mergedConstraintTypes[j], TypeCompareKind.ObliviousNullableModifierMatchesAny));
}
#endif
if (builder == null)
{
builder = ArrayBuilder<TypeParameterConstraintClause>.GetInstance(constraintClauses.Length);
builder.AddRange(constraintClauses);
}
builder[i] = TypeParameterConstraintClause.Create(constraint.Constraints,
mergedConstraintTypes?.ToImmutableAndFree() ?? originalConstraintTypes);
}
}
if (builder != null)
{
constraintClauses = builder.ToImmutableAndFree();
}
return constraintClauses;
static bool mergeConstraints(ImmutableArray<TypeWithAnnotations> originalConstraintTypes,
ref SmallDictionary<TypeWithAnnotations, int> originalConstraintTypesMap, ref ArrayBuilder<TypeWithAnnotations> mergedConstraintTypes,
TypeParameterConstraintClause clause)
{
bool result = true;
if (originalConstraintTypes.Length == 0)
{
if (clause.ConstraintTypes.Length == 0)
{
return result;
}
return false;
}
else if (clause.ConstraintTypes.Length == 0)
{
return false;
}
originalConstraintTypesMap ??= toDictionary(originalConstraintTypes,
TypeWithAnnotations.EqualsComparer.IgnoreNullableModifiersForReferenceTypesComparer);
SmallDictionary<TypeWithAnnotations, int> clauseConstraintTypesMap = toDictionary(clause.ConstraintTypes, originalConstraintTypesMap.Comparer);
foreach (int index1 in originalConstraintTypesMap.Values)
{
TypeWithAnnotations constraintType1 = mergedConstraintTypes?[index1] ?? originalConstraintTypes[index1];
int index2;
if (!clauseConstraintTypesMap.TryGetValue(constraintType1, out index2))
{
// No matching type
result = false;
continue;
}
TypeWithAnnotations constraintType2 = clause.ConstraintTypes[index2];
if (!constraintType1.Equals(constraintType2, TypeCompareKind.ObliviousNullableModifierMatchesAny))
{
// Nullability mismatch that doesn't involve oblivious
result = false;
continue;
}
if (!constraintType1.Equals(constraintType2, TypeCompareKind.ConsiderEverything))
{
// Mismatch with oblivious, merge
if (mergedConstraintTypes == null)
{
mergedConstraintTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance(originalConstraintTypes.Length);
mergedConstraintTypes.AddRange(originalConstraintTypes);
}
mergedConstraintTypes[index1] = constraintType1.MergeEquivalentTypes(constraintType2, VarianceKind.None);
}
}
foreach (var constraintType in clauseConstraintTypesMap.Keys)
{
if (!originalConstraintTypesMap.ContainsKey(constraintType))
{
result = false;
break;
}
}
return result;
}
static SmallDictionary<TypeWithAnnotations, int> toDictionary(ImmutableArray<TypeWithAnnotations> constraintTypes, IEqualityComparer<TypeWithAnnotations> comparer)
{
var result = new SmallDictionary<TypeWithAnnotations, int>(comparer);
for (int i = constraintTypes.Length - 1; i >= 0; i--)
{
result[constraintTypes[i]] = i; // Use the first type among the duplicates as the source of the nullable information
}
return result;
}
}
/// <summary>
/// Note, only nullability aspects are merged if possible, other mismatches are treated as failures.
/// </summary>
private ImmutableArray<TypeParameterConstraintClause> MergeConstraintKindsForPartialDeclarations(ImmutableArray<TypeParameterConstraintClause> constraintClauses,
ArrayBuilder<ImmutableArray<TypeParameterConstraintClause>> otherPartialClauses)
{
if (otherPartialClauses == null)
{
return constraintClauses;
}
ArrayBuilder<TypeParameterConstraintClause> builder = null;
var typeParameters = TypeParameters;
int arity = typeParameters.Length;
Debug.Assert(constraintClauses.Length == arity);
for (int i = 0; i < arity; i++)
{
var constraint = constraintClauses[i];
TypeParameterConstraintKind mergedKind = constraint.Constraints;
ImmutableArray<TypeWithAnnotations> originalConstraintTypes = constraint.ConstraintTypes;
foreach (ImmutableArray<TypeParameterConstraintClause> otherPartialConstraints in otherPartialClauses)
{
mergeConstraints(ref mergedKind, originalConstraintTypes, otherPartialConstraints[i]);
}
if (constraint.Constraints != mergedKind)
{
Debug.Assert((constraint.Constraints & (TypeParameterConstraintKind.AllNonNullableKinds | TypeParameterConstraintKind.NotNull)) ==
(mergedKind & (TypeParameterConstraintKind.AllNonNullableKinds | TypeParameterConstraintKind.NotNull)));
Debug.Assert((mergedKind & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) == 0 ||
(constraint.Constraints & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0);
Debug.Assert((constraint.Constraints & TypeParameterConstraintKind.AllReferenceTypeKinds) == (mergedKind & TypeParameterConstraintKind.AllReferenceTypeKinds) ||
(constraint.Constraints & TypeParameterConstraintKind.AllReferenceTypeKinds) == TypeParameterConstraintKind.ReferenceType);
if (builder == null)
{
builder = ArrayBuilder<TypeParameterConstraintClause>.GetInstance(constraintClauses.Length);
builder.AddRange(constraintClauses);
}
builder[i] = TypeParameterConstraintClause.Create(mergedKind, originalConstraintTypes);
}
}
if (builder != null)
{
constraintClauses = builder.ToImmutableAndFree();
}
return constraintClauses;
static void mergeConstraints(ref TypeParameterConstraintKind mergedKind, ImmutableArray<TypeWithAnnotations> originalConstraintTypes, TypeParameterConstraintClause clause)
{
if ((mergedKind & (TypeParameterConstraintKind.AllNonNullableKinds | TypeParameterConstraintKind.NotNull)) != (clause.Constraints & (TypeParameterConstraintKind.AllNonNullableKinds | TypeParameterConstraintKind.NotNull)))
{
mergedKind |= TypeParameterConstraintKind.PartialMismatch;
}
if ((mergedKind & TypeParameterConstraintKind.ReferenceType) != 0 && (clause.Constraints & TypeParameterConstraintKind.ReferenceType) != 0)
{
// Try merging nullability of a 'class' constraint
TypeParameterConstraintKind clause1Constraints = mergedKind & TypeParameterConstraintKind.AllReferenceTypeKinds;
TypeParameterConstraintKind clause2Constraints = clause.Constraints & TypeParameterConstraintKind.AllReferenceTypeKinds;
if (clause1Constraints != clause2Constraints)
{
if (clause1Constraints == TypeParameterConstraintKind.ReferenceType) // Oblivious
{
// Take nullability from clause2
mergedKind = (mergedKind & (~TypeParameterConstraintKind.AllReferenceTypeKinds)) | clause2Constraints;
}
else if (clause2Constraints != TypeParameterConstraintKind.ReferenceType)
{
// Neither nullability is oblivious and they do not match. Cannot merge.
mergedKind |= TypeParameterConstraintKind.PartialMismatch;
}
}
}
if (originalConstraintTypes.Length == 0 && clause.ConstraintTypes.Length == 0)
{
// Try merging nullability of implied 'object' constraint
if (((mergedKind | clause.Constraints) & ~(TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType | TypeParameterConstraintKind.Constructor)) == 0 &&
(mergedKind & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0 && // 'object~'
(clause.Constraints & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) == 0) // 'object?'
{
// Merged value is 'object?'
mergedKind &= ~TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType;
}
}
}
}
internal sealed override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotationsNoUseSiteDiagnostics
{
get
{
return GetTypeParametersAsTypeArguments();
}
}
public override ImmutableArray<TypeParameterSymbol> TypeParameters
{
get
{
if (_typeParameterInfo.LazyTypeParameters.IsDefault)
{
var diagnostics = BindingDiagnosticBag.GetInstance();
if (ImmutableInterlocked.InterlockedInitialize(
ref _typeParameterInfo.LazyTypeParameters,
MakeTypeParameters(diagnostics)))
{
AddDeclarationDiagnostics(diagnostics);
}
diagnostics.Free();
}
return _typeParameterInfo.LazyTypeParameters;
}
}
#endregion
#region Attributes
/// <summary>
/// Gets all the attribute lists for this named type. If <paramref name="quickAttributes"/> is provided
/// the attribute lists will only be returned if there is reasonable belief that
/// the type has one of the attributes specified by <paramref name="quickAttributes"/> on it.
/// This can avoid going back to syntax if we know the type definitely doesn't have an attribute
/// on it that could be the one specified by <paramref name="quickAttributes"/>. Pass <see langword="null"/>
/// to get all attribute declarations.
/// </summary>
internal ImmutableArray<SyntaxList<AttributeListSyntax>> GetAttributeDeclarations(QuickAttributes? quickAttributes = null)
{
// if the compilation has any global aliases to these quick attributes, then we have to return
// all the attributes on the decl. For example, if there is a `global using X = Y;` and
// then we have to return any attributes on the type as they might say `[X]`.
if (quickAttributes != null)
{
foreach (var decl in this.DeclaringCompilation.MergedRootDeclaration.Declarations)
{
if (decl is RootSingleNamespaceDeclaration rootNamespaceDecl &&
(rootNamespaceDecl.GlobalAliasedQuickAttributes & quickAttributes) != 0)
{
return declaration.GetAttributeDeclarations(quickAttributes: null);
}
}
}
return declaration.GetAttributeDeclarations(quickAttributes);
}
IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner
{
get { return this; }
}
AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation
{
get { return AttributeLocation.Type; }
}
AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
{
get
{
switch (TypeKind)
{
case TypeKind.Delegate:
return AttributeLocation.Type | AttributeLocation.Return;
case TypeKind.Enum:
case TypeKind.Interface:
return AttributeLocation.Type;
case TypeKind.Struct:
case TypeKind.Class:
return AttributeLocation.Type | (HasPrimaryConstructor ? AttributeLocation.Method : 0);
default:
return AttributeLocation.None;
}
}
}
/// <summary>
/// Returns a bag of applied custom attributes and data decoded from well-known attributes. Returns null if there are no attributes applied on the symbol.
/// </summary>
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
private CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
{
var bag = _lazyCustomAttributesBag;
if (bag != null && bag.IsSealed)
{
return bag;
}
if (LoadAndValidateAttributes(OneOrMany.Create(this.GetAttributeDeclarations()), ref _lazyCustomAttributesBag))
{
var completed = state.NotePartComplete(CompletionPart.Attributes);
Debug.Assert(completed);
}
Debug.Assert(_lazyCustomAttributesBag.IsSealed);
return _lazyCustomAttributesBag;
}
/// <summary>
/// Gets the attributes applied on this symbol.
/// Returns an empty array if there are no attributes.
/// </summary>
public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
{
return this.GetAttributesBag().Attributes;
}
/// <summary>
/// Returns data decoded from well-known attributes applied to the symbol or null if there are no applied attributes.
/// </summary>
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
private TypeWellKnownAttributeData GetDecodedWellKnownAttributeData()
{
var attributesBag = _lazyCustomAttributesBag;
if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed)
{
attributesBag = this.GetAttributesBag();
}
return (TypeWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
}
#nullable enable
/// <summary>
/// Returns data decoded from special early bound well-known attributes applied to the symbol or null if there are no applied attributes.
/// </summary>
/// <remarks>
/// Forces binding and decoding of attributes.
/// </remarks>
internal TypeEarlyWellKnownAttributeData? GetEarlyDecodedWellKnownAttributeData()
{
var attributesBag = _lazyCustomAttributesBag;
if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
{
attributesBag = this.GetAttributesBag();
}
return (TypeEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData;
}
internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments<EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation> arguments)
{
bool hasAnyDiagnostics;
CSharpAttributeData? attributeData;
BoundAttribute? boundAttribute;
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.ComImportAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().HasComImportAttribute = true;
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
return (null, null);
}
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CodeAnalysisEmbeddedAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().HasCodeAnalysisEmbeddedAttribute = true;
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
return (null, null);
}
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.ConditionalAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
string? name = attributeData.GetConstructorArgument<string>(0, SpecialType.System_String);
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().AddConditionalSymbol(name);
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
return (null, null);
}
ObsoleteAttributeData? obsoleteData;
if (EarlyDecodeDeprecatedOrExperimentalOrObsoleteAttribute(ref arguments, out attributeData, out boundAttribute, out obsoleteData))
{
if (obsoleteData != null)
{
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().ObsoleteAttributeData = obsoleteData;
}
return (attributeData, boundAttribute);
}
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.AttributeUsageAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
AttributeUsageInfo info = this.DecodeAttributeUsageAttribute(attributeData, arguments.AttributeSyntax, diagnose: false);
if (!info.IsNull)
{
var typeData = arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>();
if (typeData.AttributeUsageInfo.IsNull)
{
typeData.AttributeUsageInfo = info;
}
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
}
return (null, null);
}
// We want to decode this early because it can influence overload resolution, which could affect attribute binding itself. Consider an attribute with these
// constructors:
//
// MyAttribute(string s)
// MyAttribute(CustomBuilder c) // CustomBuilder has InterpolatedStringHandlerAttribute on the type
//
// If it's applied with [MyAttribute($"{1}")], overload resolution rules say that we should prefer the CustomBuilder overload over the string overload. This
// is an error scenario regardless (non-constant interpolated string), but it's good to get right as it will affect public API results.
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.InterpolatedStringHandlerAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().HasInterpolatedStringHandlerAttribute = true;
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
return (null, null);
}
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.InlineArrayAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
int length = attributeData.GetConstructorArgument<int>(0, SpecialType.System_Int32);
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().InlineArrayLength = length > 0 ? length : -1;
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
return (null, null);
}
if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CollectionBuilderAttribute))
{
(attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
if (!attributeData.HasErrors)
{
Debug.Assert(attributeData.CommonConstructorArguments[0].Kind == TypedConstantKind.Type);
TypeSymbol? builderType = attributeData.CommonConstructorArguments[0].ValueInternal as TypeSymbol;
string? methodName = attributeData.GetConstructorArgument<string>(1, SpecialType.System_String);
var data = new CollectionBuilderAttributeData(builderType, methodName);
arguments.GetOrCreateData<TypeEarlyWellKnownAttributeData>().CollectionBuilder = data;
if (!hasAnyDiagnostics)
{
return (attributeData, boundAttribute);
}
}
return (null, null);
}
return base.EarlyDecodeWellKnownAttribute(ref arguments);
}
#nullable disable
internal override AttributeUsageInfo GetAttributeUsageInfo()
{
Debug.Assert(this.SpecialType == SpecialType.System_Object || this.DeclaringCompilation.IsAttributeType(this));
TypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData();
if (data != null && !data.AttributeUsageInfo.IsNull)
{
return data.AttributeUsageInfo;
}
return ((object)this.BaseTypeNoUseSiteDiagnostics != null) ? this.BaseTypeNoUseSiteDiagnostics.GetAttributeUsageInfo() : AttributeUsageInfo.Default;
}
/// <summary>
/// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
/// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
/// </summary>
internal override ObsoleteAttributeData ObsoleteAttributeData
{
get
{
var lazyCustomAttributesBag = _lazyCustomAttributesBag;
if (lazyCustomAttributesBag != null && lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
{
var data = (TypeEarlyWellKnownAttributeData)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData;
return data != null ? data.ObsoleteAttributeData : null;
}
foreach (var decl in this.declaration.Declarations)
{
if (decl.HasAnyAttributes)
{
return ObsoleteAttributeData.Uninitialized;
}
}
return null;
}
}
#nullable enable
protected sealed override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
{
Debug.Assert(arguments.AttributeSyntaxOpt is { });
var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
var attribute = arguments.Attribute;
Debug.Assert(!attribute.HasErrors);
Debug.Assert(arguments.SymbolPart == AttributeLocation.None);
if (attribute.IsTargetAttribute(AttributeDescription.AttributeUsageAttribute))
{
DecodeAttributeUsageAttribute(attribute, arguments.AttributeSyntaxOpt, diagnose: true, diagnosticsOpt: diagnostics);
}
else if (attribute.IsTargetAttribute(AttributeDescription.DefaultMemberAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasDefaultMemberAttribute = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.CoClassAttribute))
{
DecodeCoClassAttribute(ref arguments);
}
else if (attribute.IsTargetAttribute(AttributeDescription.ConditionalAttribute))
{
ValidateConditionalAttribute(attribute, arguments.AttributeSyntaxOpt, diagnostics);
}
else if (attribute.IsTargetAttribute(AttributeDescription.GuidAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().GuidString = attribute.DecodeGuidAttribute(arguments.AttributeSyntaxOpt, diagnostics);
}
else if (attribute.IsTargetAttribute(AttributeDescription.SpecialNameAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasSpecialNameAttribute = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.SerializableAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasSerializableAttribute = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.ExcludeFromCodeCoverageAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasExcludeFromCodeCoverageAttribute = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.StructLayoutAttribute))
{
AttributeData.DecodeStructLayoutAttribute<TypeWellKnownAttributeData, AttributeSyntax, CSharpAttributeData, AttributeLocation>(
ref arguments, this.DefaultMarshallingCharSet, defaultAutoLayoutSize: 0, messageProvider: MessageProvider.Instance);
}
else if (attribute.IsTargetAttribute(AttributeDescription.SuppressUnmanagedCodeSecurityAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasSuppressUnmanagedCodeSecurityAttribute = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.ClassInterfaceAttribute))
{
attribute.DecodeClassInterfaceAttribute(arguments.AttributeSyntaxOpt, diagnostics);
}
else if (attribute.IsTargetAttribute(AttributeDescription.InterfaceTypeAttribute))
{
attribute.DecodeInterfaceTypeAttribute(arguments.AttributeSyntaxOpt, diagnostics);
}
else if (attribute.IsTargetAttribute(AttributeDescription.WindowsRuntimeImportAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasWindowsRuntimeImportAttribute = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.RequiredAttributeAttribute))
{
// CS1608: The Required attribute is not permitted on C# types
diagnostics.Add(ErrorCode.ERR_CantUseRequiredAttribute, arguments.AttributeSyntaxOpt.Name.Location);
}
else if (ReportExplicitUseOfReservedAttributes(in arguments,
ReservedAttributes.DynamicAttribute
| ReservedAttributes.IsReadOnlyAttribute
| ReservedAttributes.RequiresLocationAttribute
| ReservedAttributes.IsUnmanagedAttribute
| ReservedAttributes.IsByRefLikeAttribute
| ReservedAttributes.TupleElementNamesAttribute
| ReservedAttributes.NullableAttribute
| ReservedAttributes.NullableContextAttribute
| ReservedAttributes.NativeIntegerAttribute
| ReservedAttributes.CaseSensitiveExtensionAttribute
| ReservedAttributes.RequiredMemberAttribute))
{
}
else if (attribute.IsTargetAttribute(AttributeDescription.SecurityCriticalAttribute)
|| attribute.IsTargetAttribute(AttributeDescription.SecuritySafeCriticalAttribute))
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().HasSecurityCriticalAttributes = true;
}
else if (attribute.IsTargetAttribute(AttributeDescription.SkipLocalsInitAttribute))
{
CSharpAttributeData.DecodeSkipLocalsInitAttribute<TypeWellKnownAttributeData>(DeclaringCompilation, ref arguments);
}
else if (attribute.IsTargetAttribute(AttributeDescription.CollectionBuilderAttribute))
{
var builderType = attribute.CommonConstructorArguments[0].ValueInternal as TypeSymbol;
if (!IsValidCollectionBuilderType(builderType))
{
diagnostics.Add(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, arguments.AttributeSyntaxOpt.Name.Location);
}
// Ensure dependencies for the builder type are added here since
// use-site info is ignored in early attribute decoding.
diagnostics.AddDependencies(builderType);
string? methodName = attribute.CommonConstructorArguments[1].DecodeValue<string>(SpecialType.System_String);
if (string.IsNullOrEmpty(methodName))
{
diagnostics.Add(ErrorCode.ERR_CollectionBuilderAttributeInvalidMethodName, arguments.AttributeSyntaxOpt.Name.Location);
}
}
else if (_lazyIsExplicitDefinitionOfNoPiaLocalType == ThreeState.Unknown && attribute.IsTargetAttribute(AttributeDescription.TypeIdentifierAttribute))
{
_lazyIsExplicitDefinitionOfNoPiaLocalType = ThreeState.True;
}
else if (attribute.IsTargetAttribute(AttributeDescription.InlineArrayAttribute))
{
int length = attribute.CommonConstructorArguments[0].DecodeValue<int>(SpecialType.System_Int32);
if (length <= 0)
{
diagnostics.Add(ErrorCode.ERR_InvalidInlineArrayLength, attribute.GetAttributeArgumentLocation(0));
}
if (TypeKind != TypeKind.Struct)
{
diagnostics.Add(ErrorCode.ERR_AttributeOnBadSymbolType, arguments.AttributeSyntaxOpt.Name.Location, arguments.AttributeSyntaxOpt.GetErrorDisplayName(), "struct");
}
else if (IsRecordStruct)
{
diagnostics.Add(ErrorCode.ERR_InlineArrayAttributeOnRecord, arguments.AttributeSyntaxOpt.Name.Location);
}
}
else
{
var compilation = this.DeclaringCompilation;
if (attribute.IsSecurityAttribute(compilation))
{
attribute.DecodeSecurityAttribute<TypeWellKnownAttributeData>(this, compilation, ref arguments);
}
}
}
internal static bool IsValidCollectionBuilderType([NotNullWhen(true)] TypeSymbol? builderType)
{
return builderType is NamedTypeSymbol { TypeKind: TypeKind.Class or TypeKind.Struct, IsGenericType: false };
}
#nullable disable
internal override bool IsExplicitDefinitionOfNoPiaLocalType
{
get
{
if (_lazyIsExplicitDefinitionOfNoPiaLocalType == ThreeState.Unknown)
{
CheckPresenceOfTypeIdentifierAttribute();
if (_lazyIsExplicitDefinitionOfNoPiaLocalType == ThreeState.Unknown)
{
_lazyIsExplicitDefinitionOfNoPiaLocalType = ThreeState.False;
}
}
Debug.Assert(_lazyIsExplicitDefinitionOfNoPiaLocalType != ThreeState.Unknown);
return _lazyIsExplicitDefinitionOfNoPiaLocalType == ThreeState.True;
}
}
private void CheckPresenceOfTypeIdentifierAttribute()
{
// Have we already decoded well-known attributes?
if (_lazyCustomAttributesBag?.IsDecodedWellKnownAttributeDataComputed == true)
{
return;
}
// We want this function to be as cheap as possible, it is called for every top level type
// and we don't want to bind attributes attached to the declaration unless there is a chance
// that one of them is TypeIdentifier attribute.
ImmutableArray<SyntaxList<AttributeListSyntax>> attributeLists = GetAttributeDeclarations(QuickAttributes.TypeIdentifier);
foreach (SyntaxList<AttributeListSyntax> list in attributeLists)
{
var syntaxTree = list.Node.SyntaxTree;
QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(list.Node.SyntaxTree).GetBinder(list.Node).QuickAttributeChecker;
foreach (AttributeListSyntax attrList in list)
{
foreach (AttributeSyntax attr in attrList.Attributes)
{
if (checker.IsPossibleMatch(attr, QuickAttributes.TypeIdentifier))
{
// This attribute syntax might be an application of TypeIdentifierAttribute.
// Let's bind it.
// For simplicity we bind all attributes.
GetAttributes();
return;
}
}
}
}
}
// Process the specified AttributeUsage attribute on the given ownerSymbol
private AttributeUsageInfo DecodeAttributeUsageAttribute(CSharpAttributeData attribute, AttributeSyntax node, bool diagnose, BindingDiagnosticBag diagnosticsOpt = null)
{
Debug.Assert(diagnose == (diagnosticsOpt != null));
Debug.Assert(!attribute.HasErrors);
Debug.Assert(!this.IsErrorType());
// AttributeUsage can only be specified for attribute classes
if (!this.DeclaringCompilation.IsAttributeType(this))
{
if (diagnose)
{
diagnosticsOpt.Add(ErrorCode.ERR_AttributeUsageOnNonAttributeClass, node.Name.Location, node.GetErrorDisplayName());
}
return AttributeUsageInfo.Null;
}
else
{
AttributeUsageInfo info = attribute.DecodeAttributeUsageAttribute();
// Validate first ctor argument for AttributeUsage specification is a valid AttributeTargets enum member
if (!info.HasValidAttributeTargets)
{
if (diagnose)
{
// invalid attribute target
diagnosticsOpt.Add(ErrorCode.ERR_InvalidAttributeArgument, attribute.GetAttributeArgumentLocation(0), node.GetErrorDisplayName());
}
return AttributeUsageInfo.Null;
}
return info;
}
}
private void DecodeCoClassAttribute(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
{
var attribute = arguments.Attribute;
Debug.Assert(!attribute.HasErrors);
if (this.IsInterfaceType() && (!arguments.HasDecodedData || (object)((TypeWellKnownAttributeData)arguments.DecodedData).ComImportCoClass == null))
{
TypedConstant argument = attribute.CommonConstructorArguments[0];
Debug.Assert(argument.Kind == TypedConstantKind.Type);
var coClassType = argument.ValueInternal as NamedTypeSymbol;
if ((object)coClassType != null && coClassType.TypeKind == TypeKind.Class)
{
arguments.GetOrCreateData<TypeWellKnownAttributeData>().ComImportCoClass = coClassType;
}
}
}
internal override bool IsComImport
{
get
{
TypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData();
return data != null && data.HasComImportAttribute;
}
}
internal override NamedTypeSymbol ComImportCoClass
{
get
{
TypeWellKnownAttributeData data = this.GetDecodedWellKnownAttributeData();
return data != null ? data.ComImportCoClass : null;
}
}
#nullable enable
internal sealed override bool HasCollectionBuilderAttribute(out TypeSymbol? builderType, out string? methodName)
{
var attributeData = GetEarlyDecodedWellKnownAttributeData()?.CollectionBuilder;
if (attributeData == null)
{
builderType = null;
methodName = null;
return false;
}
builderType = attributeData.BuilderType;
methodName = attributeData.MethodName;
return true;
}
#nullable disable
private void ValidateConditionalAttribute(CSharpAttributeData attribute, AttributeSyntax node, BindingDiagnosticBag diagnostics)
{
Debug.Assert(this.IsConditional);
Debug.Assert(!attribute.HasErrors);
if (!this.DeclaringCompilation.IsAttributeType(this))
{
// CS1689: Attribute '{0}' is only valid on methods or attribute classes
diagnostics.Add(ErrorCode.ERR_ConditionalOnNonAttributeClass, node.Location, node.GetErrorDisplayName());
}
else
{
string name = attribute.GetConstructorArgument<string>(0, SpecialType.System_String);
if (name == null || !SyntaxFacts.IsValidIdentifier(name))
{
// CS0633: The argument to the '{0}' attribute must be a valid identifier
diagnostics.Add(ErrorCode.ERR_BadArgumentToAttribute, attribute.GetAttributeArgumentLocation(0), node.GetErrorDisplayName());
}
}
}
internal override bool HasSpecialName
{
get
{
var data = GetDecodedWellKnownAttributeData();
return data != null && data.HasSpecialNameAttribute;
}
}
internal override bool HasCodeAnalysisEmbeddedAttribute
{
get
{
var data = GetEarlyDecodedWellKnownAttributeData();
return data != null && data.HasCodeAnalysisEmbeddedAttribute;
}
}
#nullable enable
internal sealed override bool IsInterpolatedStringHandlerType
=> GetEarlyDecodedWellKnownAttributeData()?.HasInterpolatedStringHandlerAttribute == true;
#nullable disable
internal sealed override bool ShouldAddWinRTMembers
{
get { return false; }
}
internal sealed override bool IsWindowsRuntimeImport
{
get
{
TypeWellKnownAttributeData data = this.GetDecodedWellKnownAttributeData();
return data != null && data.HasWindowsRuntimeImportAttribute;
}
}
public sealed override bool IsSerializable
{
get
{
var data = this.GetDecodedWellKnownAttributeData();
return data != null && data.HasSerializableAttribute;
}
}
public sealed override bool AreLocalsZeroed
{
get
{
var data = this.GetDecodedWellKnownAttributeData();
return data?.HasSkipLocalsInitAttribute != true && (ContainingType?.AreLocalsZeroed ?? ContainingModule.AreLocalsZeroed);
}
}
internal override bool GetGuidString(out string guidString)
{
guidString = GetDecodedWellKnownAttributeData()?.GuidString;
return guidString != null;
}
internal override bool IsDirectlyExcludedFromCodeCoverage =>
GetDecodedWellKnownAttributeData()?.HasExcludeFromCodeCoverageAttribute == true;
private bool HasInstanceFields()
{
var fields = this.GetFieldsToEmit();
foreach (var field in fields)
{
if (!field.IsStatic)
{
return true;
}
}
return false;
}
internal sealed override TypeLayout Layout
{
get
{
var data = GetDecodedWellKnownAttributeData();
if (data != null && data.HasStructLayoutAttribute)
{
return data.Layout;
}
if (this.TypeKind == TypeKind.Struct)
{
// CLI spec 22.37.16:
// "A ValueType shall have a non-zero size - either by defining at least one field, or by providing a non-zero ClassSize"
//
// Dev11 compiler sets the value to 1 for structs with no instance fields and no size specified.
// It does not change the size value if it was explicitly specified to be 0, nor does it report an error.
return new TypeLayout(LayoutKind.Sequential, this.HasInstanceFields() ? 0 : 1, alignment: 0);
}
return default(TypeLayout);
}
}
internal bool HasStructLayoutAttribute
{
get
{
var data = GetDecodedWellKnownAttributeData();
return data != null && data.HasStructLayoutAttribute;
}
}
internal override CharSet MarshallingCharSet
{
get
{
var data = GetDecodedWellKnownAttributeData();
return (data != null && data.HasStructLayoutAttribute) ? data.MarshallingCharSet : DefaultMarshallingCharSet;
}
}
internal sealed override bool HasDeclarativeSecurity
{
get
{
var data = this.GetDecodedWellKnownAttributeData();
return data != null && data.HasDeclarativeSecurity;
}
}
internal bool HasSecurityCriticalAttributes
{
get
{
var data = this.GetDecodedWellKnownAttributeData();
return data != null && data.HasSecurityCriticalAttributes;
}
}
internal sealed override IEnumerable<Microsoft.Cci.SecurityAttribute> GetSecurityInformation()
{
var attributesBag = this.GetAttributesBag();
var wellKnownData = (TypeWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
if (wellKnownData != null)
{
SecurityWellKnownAttributeData securityData = wellKnownData.SecurityInformation;
if (securityData != null)
{
return securityData.GetSecurityAttributes(attributesBag.Attributes);
}
}
return null;
}
internal override ImmutableArray<string> GetAppliedConditionalSymbols()
{
var data = GetEarlyDecodedWellKnownAttributeData();
return data != null ? data.ConditionalSymbols : ImmutableArray<string>.Empty;
}
internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData)
{
Debug.Assert(!boundAttributes.IsDefault);
Debug.Assert(!allAttributeSyntaxNodes.IsDefault);
Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length);
Debug.Assert(_lazyCustomAttributesBag != null);
Debug.Assert(_lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed);
Debug.Assert(symbolPart == AttributeLocation.None);
var data = (TypeWellKnownAttributeData)decodedData;
if (this.IsComImport)
{
Debug.Assert(boundAttributes.Any());
// Symbol with ComImportAttribute must have a GuidAttribute
if (data == null || data.GuidString == null)
{
int index = boundAttributes.IndexOfAttribute(AttributeDescription.ComImportAttribute);
diagnostics.Add(ErrorCode.ERR_ComImportWithoutUuidAttribute, allAttributeSyntaxNodes[index].Name.Location);
}
if (this.TypeKind == TypeKind.Class)
{
var baseType = this.BaseTypeNoUseSiteDiagnostics;
if ((object)baseType != null && baseType.SpecialType != SpecialType.System_Object)
{
// CS0424: '{0}': a class with the ComImport attribute cannot specify a base class
diagnostics.Add(ErrorCode.ERR_ComImportWithBase, this.GetFirstLocation(), this.Name);
}
var initializers = this.StaticInitializers;
if (!initializers.IsDefaultOrEmpty)
{
foreach (var initializerGroup in initializers)
{
foreach (var singleInitializer in initializerGroup)
{
if (!singleInitializer.FieldOpt.IsMetadataConstant)
{
// CS8028: '{0}': a class with the ComImport attribute cannot specify field initializers.
diagnostics.Add(ErrorCode.ERR_ComImportWithInitializers, singleInitializer.Syntax.GetLocation(), this.Name);
}
}
}
}
initializers = this.InstanceInitializers;
if (!initializers.IsDefaultOrEmpty)
{
foreach (var initializerGroup in initializers)
{
foreach (var singleInitializer in initializerGroup)
{
// CS8028: '{0}': a class with the ComImport attribute cannot specify field initializers.
diagnostics.Add(ErrorCode.ERR_ComImportWithInitializers, singleInitializer.Syntax.GetLocation(), this.Name);
}
}
}
}
}
else if ((object)this.ComImportCoClass != null)
{
Debug.Assert(boundAttributes.Any());
// Symbol with CoClassAttribute must have a ComImportAttribute
int index = boundAttributes.IndexOfAttribute(AttributeDescription.CoClassAttribute);
diagnostics.Add(ErrorCode.WRN_CoClassWithoutComImport, allAttributeSyntaxNodes[index].Location, this.Name);
}
// Report ERR_DefaultMemberOnIndexedType if type has a default member attribute and has indexers.
if (data != null && data.HasDefaultMemberAttribute && this.Indexers.Any())
{
Debug.Assert(boundAttributes.Any());
int index = boundAttributes.IndexOfAttribute(AttributeDescription.DefaultMemberAttribute);
diagnostics.Add(ErrorCode.ERR_DefaultMemberOnIndexedType, allAttributeSyntaxNodes[index].Name.Location);
}
base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData);
}
internal override bool HasInlineArrayAttribute(out int length)
{
TypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData();
if (data?.InlineArrayLength is > 0 and var lengthFromAttribute)
{
length = lengthFromAttribute;
return true;
}
length = 0;
return false;
}
/// <remarks>
/// These won't be returned by GetAttributes on source methods, but they
/// will be returned by GetAttributes on metadata symbols.
/// </remarks>
internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
{
base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
CSharpCompilation compilation = this.DeclaringCompilation;
if (this.ContainsExtensionMethods)
{
// No need to check if [Extension] attribute was explicitly set since
// we'll issue CS1112 error in those cases and won't generate IL.
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor));
}
if (this.IsRefLikeType)
{
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsByRefLikeAttribute(this));
var obsoleteData = ObsoleteAttributeData;
Debug.Assert(obsoleteData != ObsoleteAttributeData.Uninitialized, "getting synthesized attributes before attributes are decoded");
if (!this.IsRestrictedType(ignoreSpanLikeTypes: true))
{
// If user specified an Obsolete attribute, we cannot emit ours.
// NB: we do not check the kind of deprecation.
// we will not emit Obsolete even if Deprecated or Experimental was used.
// we do not want to get into a scenario where different kinds of deprecation are combined together.
//
if (obsoleteData == null)
{
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_ObsoleteAttribute__ctor,
ImmutableArray.Create(
new TypedConstant(compilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, PEModule.ByRefLikeMarker), // message
new TypedConstant(compilation.GetSpecialType(SpecialType.System_Boolean), TypedConstantKind.Primitive, true)), // error=true
isOptionalUse: true));
}
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor,
ImmutableArray.Create(new TypedConstant(compilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, nameof(CompilerFeatureRequiredFeatures.RefStructs))),
isOptionalUse: true));
}
}
if (this.IsReadOnly)
{
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this));
}
if (this.Indexers.Any())
{
string defaultMemberName = this.Indexers.First().MetadataName; // UNDONE: IndexerNameAttribute
var defaultMemberNameConstant = new TypedConstant(compilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, defaultMemberName);
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(
WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor,
ImmutableArray.Create(defaultMemberNameConstant)));
}
if (this.declaration.Declarations.All(d => d.IsSimpleProgram))
{
AddSynthesizedAttribute(ref attributes,
this.DeclaringCompilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor));
}
if (HasDeclaredRequiredMembers)
{
AddSynthesizedAttribute(
ref attributes,
compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_RequiredMemberAttribute__ctor));
}
// Add MetadataUpdateOriginalTypeAttribute when a reloadable type is emitted to EnC delta
if (moduleBuilder.EncSymbolChanges?.IsReplaced(this) == true)
{
// Note that we use this source named type symbol in the attribute argument (of System.Type).
// We do not have access to the original symbol from this compilation. However, System.Type
// is encoded in the attribute as a string containing a fully qualified type name.
// The name of the current type symbol as provided by ISymbol.Name is the same as the name of
// the original type symbol that is being replaced by this type symbol.
// The "#{generation}" suffix is appended to the TypeDef name in the metadata writer,
// but not to the attribute value.
var originalType = this;
AddSynthesizedAttribute(
ref attributes,
compilation.TrySynthesizeAttribute(
WellKnownMember.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute__ctor,
ImmutableArray.Create(new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type), TypedConstantKind.Type, originalType)),
isOptionalUse: true));
}
}
#endregion
internal override NamedTypeSymbol AsNativeInteger()
{
Debug.Assert(this.SpecialType == SpecialType.System_IntPtr || this.SpecialType == SpecialType.System_UIntPtr);
if (ContainingAssembly.RuntimeSupportsNumericIntPtr)
{
return this;
}
return ContainingAssembly.GetNativeIntegerType(this);
}
internal override NamedTypeSymbol NativeIntegerUnderlyingType => null;
internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison)
{
return t2 is NativeIntegerTypeSymbol nativeInteger ?
nativeInteger.Equals(this, comparison) :
base.Equals(t2, comparison);
}
#nullable enable
internal bool IsSimpleProgram
{
get
{
return this.declaration.Declarations.Any(static d => d.IsSimpleProgram);
}
}
protected override void AfterMembersCompletedChecks(BindingDiagnosticBag diagnostics)
{
base.AfterMembersCompletedChecks(diagnostics);
// We need to give warnings if Obsolete is applied to any required members and there are constructors where a user would be forced to set that member,
// unless:
// 1. We're in an obsolete context ourselves, or
// 2. All constructors of this type are obsolete or attributed with SetsRequiredMembersAttribute
// We don't warn for obsolete required members from base types, as the user either has a warning that they're depending on an obsolete constructor/type
// already, or the original author ignored this warning.
// Obsolete states should have already been calculated by this point in the pipeline.
Debug.Assert(ObsoleteKind != ObsoleteAttributeKind.Uninitialized);
Debug.Assert(GetMembers().All(m => m.ObsoleteKind != ObsoleteAttributeKind.Uninitialized));
if (ObsoleteKind == ObsoleteAttributeKind.None
&& !GetMembers().All(m => m is not MethodSymbol { MethodKind: MethodKind.Constructor, ObsoleteKind: ObsoleteAttributeKind.None } method
|| !method.ShouldCheckRequiredMembers()))
{
foreach (var member in GetMembers())
{
if (!member.IsRequired())
{
continue;
}
if (member.ObsoleteKind != ObsoleteAttributeKind.None)
{
// Required member '{0}' should not be attributed with 'ObsoleteAttribute' unless the containing type is obsolete or all constructors are obsolete.
diagnostics.Add(ErrorCode.WRN_ObsoleteMembersShouldNotBeRequired, member.GetFirstLocation(), member);
}
}
}
if (Indexers.FirstOrDefault() is PropertySymbol indexerSymbol)
{
Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor, diagnostics, indexerSymbol.TryGetFirstLocation() ?? GetFirstLocation());
}
if (TypeKind == TypeKind.Struct && !IsRecordStruct && HasInlineArrayAttribute(out _))
{
if (Layout.Kind == LayoutKind.Explicit)
{
diagnostics.Add(ErrorCode.ERR_InvalidInlineArrayLayout, GetFirstLocation());
}
if (TryGetPossiblyUnsupportedByLanguageInlineArrayElementField() is FieldSymbol elementField)
{
bool reported_ERR_InlineArrayUnsupportedElementFieldModifier = false;
if (elementField.IsRequired || elementField.IsReadOnly || elementField.IsVolatile || elementField.IsFixedSizeBuffer)
{
diagnostics.Add(ErrorCode.ERR_InlineArrayUnsupportedElementFieldModifier, elementField.TryGetFirstLocation() ?? GetFirstLocation());
reported_ERR_InlineArrayUnsupportedElementFieldModifier = true;
}
NamedTypeSymbol? index = null;
NamedTypeSymbol? range = null;
foreach (PropertySymbol indexer in Indexers)
{
if (indexer.Parameters is [{ Type: { } type }] &&
(type.SpecialType == SpecialType.System_Int32 ||
type.Equals(index ??= DeclaringCompilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.AllIgnoreOptions) ||
type.Equals(range ??= DeclaringCompilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.AllIgnoreOptions)))
{
diagnostics.Add(ErrorCode.WRN_InlineArrayIndexerNotUsed, indexer.TryGetFirstLocation() ?? GetFirstLocation());
}
}
foreach (var slice in GetMembers(WellKnownMemberNames.SliceMethodName).OfType<MethodSymbol>())
{
if (Binder.MethodHasValidSliceSignature(slice))
{
diagnostics.Add(ErrorCode.WRN_InlineArraySliceNotUsed, slice.TryGetFirstLocation() ?? GetFirstLocation());
break;
}
}
NamedTypeSymbol? span = null;
NamedTypeSymbol? readOnlySpan = null;
TypeWithAnnotations elementType = elementField.TypeWithAnnotations;
bool fieldSupported = TypeSymbol.IsInlineArrayElementFieldSupported(elementField);
if (fieldSupported)
{
foreach (var conversion in GetMembers().OfType<SourceUserDefinedConversionSymbol>())
{
TypeSymbol returnType = conversion.ReturnType;
TypeSymbol returnTypeOriginalDefinition = returnType.OriginalDefinition;
if (conversion.ParameterCount == 1 &&
conversion.Parameters[0].Type.Equals(this, TypeCompareKind.AllIgnoreOptions) &&
(returnTypeOriginalDefinition.Equals(span ??= DeclaringCompilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions) ||
returnTypeOriginalDefinition.Equals(readOnlySpan ??= DeclaringCompilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)) &&
Conversions.HasIdentityConversion(((NamedTypeSymbol)returnTypeOriginalDefinition).Construct(ImmutableArray.Create(elementType)), returnType))
{
diagnostics.Add(ErrorCode.WRN_InlineArrayConversionOperatorNotUsed, conversion.TryGetFirstLocation() ?? GetFirstLocation());
}
}
}
if (!reported_ERR_InlineArrayUnsupportedElementFieldModifier)
{
if (!fieldSupported || elementType.Type.IsPointerOrFunctionPointer() || elementType.IsRestrictedType(ignoreSpanLikeTypes: true))
{
diagnostics.Add(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, elementField.TryGetFirstLocation() ?? GetFirstLocation());
}
else if (this.IsRestrictedType())
{
diagnostics.Add(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, GetFirstLocation());
}
}
}
else
{
diagnostics.Add(ErrorCode.ERR_InvalidInlineArrayFields, GetFirstLocation());
}
if (!ContainingAssembly.RuntimeSupportsInlineArrayTypes)
{
diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportInlineArrayTypes, GetFirstLocation());
}
}
}
}
}
|