/// <summary> /// Verify if the attribute type can be applied to given owner symbol. /// Generate a diagnostic if it cannot be applied /// </summary> /// <param name="ownerSymbol">Symbol on which the attribute is applied</param> /// <param name="attributeType">Attribute class for the attribute</param> /// <param name="node">Syntax node for attribute specification</param> /// <param name="attributeLocation">Attribute target specifier location</param> /// <param name="diagnostics">Diagnostics</param> /// <returns>Whether attribute specification is allowed for the given symbol</returns> internal bool VerifyAttributeUsageTarget(Symbol ownerSymbol, NamedTypeSymbol attributeType, AttributeSyntax node, AttributeLocation attributeLocation, DiagnosticBag diagnostics) { var attributeUsageInfo = attributeType.AttributeUsageInfo(Compilation); if (attributeUsageInfo != null) { AttributeTargets attributeTarget; if (attributeLocation == AttributeLocation.Return) { // attribute on return type attributeTarget = AttributeTargets.ReturnValue; } else { attributeTarget = ownerSymbol.GetAttributeTarget(); } if ((attributeTarget & attributeUsageInfo.ValidTargets) != 0) { return true; } // generate error Error(diagnostics, ErrorCode.ERR_AttributeOnBadSymbolType, node, GetAttributeNameFromSyntax(node), attributeUsageInfo.GetValidTargetsString()); } return false; }
public void Join_LocalAttributeToRelatedAttribute_MatchesExpected() { var definitionProvider = new DataAnnotationsDefinitionProvider(); var actual = new EntityRelation(EntityRelationType.InnerJoin); actual.Join <ChildRaisedRow>(row => row.ComplexEntityId, row => row.ComplexEntity.ComplexEntityId); var childDefinition = definitionProvider.Resolve <ChildRaisedRow>(); var childReference = new EntityReference { EntityType = typeof(ChildRaisedRow) }; var childLocation = definitionProvider.GetEntityLocation(childReference); var childAttributeLocation = new AttributeLocation(typeof(ChildRaisedRow).GetProperty("ComplexEntityId"), childReference); var childComplexIdAttribute = childDefinition.Find(childAttributeLocation); Assert.AreEqual(childLocation, childDefinition.Find(actual.SourceExpression).Entity); Assert.AreEqual(childComplexIdAttribute, childDefinition.Find(actual.SourceExpression)); //// actual.SourceAttribute); var complexDefinition = definitionProvider.Resolve <ComplexRaisedRow>(); var complexReference = new EntityReference { EntityType = typeof(ComplexRaisedRow) }; var complexLocation = definitionProvider.GetEntityLocation(complexReference); var complexAttributeLocation = new AttributeLocation(typeof(ComplexRaisedRow).GetProperty("ComplexEntityId"), complexReference); var complexIdAttribute = complexDefinition.Find(complexAttributeLocation); Assert.AreEqual(complexLocation, complexDefinition.Find(actual.RelationExpression).Entity); //// actual.RelationLocation); Assert.AreEqual(complexIdAttribute, complexDefinition.Find(actual.RelationExpression)); }
public void Join_ExplicitSourceAndRelationTypes_EntityRelationMatchesExpected() { var definitionProvider = new DataAnnotationsDefinitionProvider(); var actual = new EntityRelation(EntityRelationType.InnerJoin); actual.Join <FakeRelatedRow, DependencyRow>(row => row.RelatedId, row => row.FakeDependencyEntityId); var relatedDefinition = definitionProvider.Resolve <FakeRelatedRow>(); var leftDefinition = relatedDefinition; var leftReference = new EntityReference { EntityType = typeof(FakeRelatedRow) }; var leftLocation = definitionProvider.GetEntityLocation(leftReference); var leftAttributeLocation = new AttributeLocation(typeof(FakeRelatedRow).GetProperty(nameof(FakeRelatedRow.RelatedId)), leftReference); var leftAttribute = leftDefinition.Find(leftAttributeLocation); Assert.AreEqual(leftLocation, relatedDefinition.Find(actual.SourceExpression).Entity); Assert.AreEqual(leftAttribute, relatedDefinition.Find(actual.SourceExpression)); var rightDefinition = definitionProvider.Resolve <DependencyRow>(); var rightReference = new EntityReference { EntityType = typeof(DependencyRow) }; var rightLocation = definitionProvider.GetEntityLocation(rightReference); var rightAttributeLocation = new AttributeLocation(typeof(DependencyRow).GetProperty(nameof(DependencyRow.FakeDependencyEntityId)), rightReference); var rightAttribute = rightDefinition.Find(rightAttributeLocation); Assert.AreEqual(rightLocation, rightDefinition.Find(actual.RelationExpression).Entity); Assert.AreEqual(rightAttribute, rightDefinition.Find(actual.RelationExpression)); }
/// <summary> /// Validate attribute usage target and duplicate attributes. /// </summary> /// <param name="attribute">Bound attribute</param> /// <param name="node">Syntax node for attribute specification</param> /// <param name="compilation">Compilation</param> /// <param name="symbolPart">Symbol part to which the attribute has been applied.</param> /// <param name="diagnostics">Diagnostics</param> /// <param name="uniqueAttributeTypes">Set of unique attribute types applied to the symbol</param> private bool ValidateAttributeUsage( CSharpAttributeData attribute, AttributeSyntax node, CSharpCompilation compilation, AttributeLocation symbolPart, DiagnosticBag diagnostics, HashSet <NamedTypeSymbol> uniqueAttributeTypes) { Debug.Assert(!attribute.HasErrors); NamedTypeSymbol attributeType = attribute.AttributeClass; AttributeUsageInfo attributeUsageInfo = attributeType.GetAttributeUsageInfo(); // Given attribute can't be specified more than once if AllowMultiple is false. if (!uniqueAttributeTypes.Add(attributeType) && !attributeUsageInfo.AllowMultiple) { diagnostics.Add(ErrorCode.ERR_DuplicateAttribute, node.Name.Location, node.GetErrorDisplayName()); return(false); } // Verify if the attribute type can be applied to given owner symbol. StarkAttributeTargets attributeTarget; if (symbolPart == AttributeLocation.Return) { // attribute on return type Debug.Assert(this.Kind == SymbolKind.Method); attributeTarget = StarkAttributeTargets.ReturnValue; } else { attributeTarget = this.GetAttributeTarget(); } if ((attributeTarget & attributeUsageInfo.ValidTargets) == 0) { // generate error diagnostics.Add(ErrorCode.ERR_AttributeOnBadSymbolType, node.Name.Location, node.GetErrorDisplayName(), attributeUsageInfo.GetValidTargetsErrorArgument()); return(false); } if (attribute.IsSecurityAttribute(compilation)) { switch (this.Kind) { case SymbolKind.Assembly: case SymbolKind.NamedType: case SymbolKind.Method: break; default: // CS7070: Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. diagnostics.Add(ErrorCode.ERR_SecurityAttributeInvalidTarget, node.Name.Location, node.GetErrorDisplayName()); return(false); } } return(true); }
internal virtual void PostDecodeWellKnownAttributes( ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData ) { }
private WellKnownAttributeData ValidateAttributeUsageAndDecodeWellKnownAttributes( ImmutableArray <Binder> binders, ImmutableArray <AttributeSyntax> attributeSyntaxList, ImmutableArray <CSharpAttributeData> boundAttributes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart ) { Debug.Assert(binders.Any()); Debug.Assert(attributeSyntaxList.Any()); Debug.Assert(boundAttributes.Any()); Debug.Assert(binders.Length == boundAttributes.Length); Debug.Assert(attributeSyntaxList.Length == boundAttributes.Length); int totalAttributesCount = boundAttributes.Length; HashSet <NamedTypeSymbol> uniqueAttributeTypes = new HashSet <NamedTypeSymbol>(); var arguments = new DecodeWellKnownAttributeArguments < AttributeSyntax, CSharpAttributeData, AttributeLocation >(); arguments.Diagnostics = diagnostics; arguments.AttributesCount = totalAttributesCount; arguments.SymbolPart = symbolPart; for (int i = 0; i < totalAttributesCount; i++) { CSharpAttributeData boundAttribute = boundAttributes[i]; AttributeSyntax attributeSyntax = attributeSyntaxList[i]; Binder binder = binders[i]; // Decode attribute as a possible well-known attribute only if it has no binding errors and has valid AttributeUsage. if ( !boundAttribute.HasErrors && ValidateAttributeUsage( boundAttribute, attributeSyntax, binder.Compilation, symbolPart, diagnostics, uniqueAttributeTypes ) ) { arguments.Attribute = boundAttribute; arguments.AttributeSyntaxOpt = attributeSyntax; arguments.Index = i; this.DecodeWellKnownAttribute(ref arguments); } } return(arguments.HasDecodedData ? arguments.DecodedData : null); }
internal EarlyWellKnownAttributeData EarlyDecodeWellKnownAttributes( ImmutableArray <Binder> binders, ImmutableArray <NamedTypeSymbol> boundAttributeTypes, ImmutableArray <AttributeSyntax> attributesToBind, AttributeLocation symbolPart, CSharpAttributeData[] boundAttributesBuilder ) { Debug.Assert(boundAttributeTypes.Any()); Debug.Assert(attributesToBind.Any()); Debug.Assert(binders.Any()); Debug.Assert(boundAttributesBuilder != null); Debug.Assert(!boundAttributesBuilder.Contains((attr) => attr != null)); var earlyBinder = new EarlyWellKnownAttributeBinder(binders[0]); var arguments = new EarlyDecodeWellKnownAttributeArguments < EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation >(); arguments.SymbolPart = symbolPart; for (int i = 0; i < boundAttributeTypes.Length; i++) { NamedTypeSymbol boundAttributeType = boundAttributeTypes[i]; if (!boundAttributeType.IsErrorType()) { if (binders[i] != earlyBinder.Next) { earlyBinder = new EarlyWellKnownAttributeBinder(binders[i]); } arguments.Binder = earlyBinder; arguments.AttributeType = boundAttributeType; arguments.AttributeSyntax = attributesToBind[i]; // Early bind some well-known attributes CSharpAttributeData earlyBoundAttributeOpt = this.EarlyDecodeWellKnownAttribute( ref arguments ); Debug.Assert( earlyBoundAttributeOpt == null || !earlyBoundAttributeOpt.HasErrors ); boundAttributesBuilder[i] = earlyBoundAttributeOpt; } } return(arguments.HasDecodedData ? arguments.DecodedData : null); }
public void AttributeLocation_ConstructWithLambda_MatchesExpected() { Expression <Func <Document, object> > expr = document => document.Identifier; var expected = new AttributeLocation( expr.GetProperty(), new EntityReference { EntityType = typeof(Document) }); var actual = new AttributeLocation(expr); Assert.AreEqual(expected, actual, string.Join(Environment.NewLine, expected.GetDifferences(actual))); }
internal override void PostDecodeWellKnownAttributes( ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData ) { base.PostDecodeWellKnownAttributes( boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData ); if (!allAttributeSyntaxNodes.IsEmpty && _property.IsAutoPropertyWithGetAccessor) { CheckForFieldTargetedAttribute(diagnostics); } }
internal override void PostDecodeWellKnownAttributes(ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { Debug.Assert(!boundAttributes.IsDefault); Debug.Assert(!allAttributeSyntaxNodes.IsDefault); Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length); Debug.Assert(this.lazyCustomAttributesBag != null); Debug.Assert(this.lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed); Debug.Assert(symbolPart == AttributeLocation.None); var data = (CommonFieldWellKnownAttributeData)decodedData; int?fieldOffset = data != null ? data.Offset : null; if (fieldOffset.HasValue) { if (this.ContainingType.Layout.Kind != LayoutKind.Explicit) { Debug.Assert(boundAttributes.Any()); // error CS0636: The FieldOffset attribute can only be placed on members of types marked with the StructLayout(LayoutKind.Explicit) int i = boundAttributes.IndexOfAttribute(this, AttributeDescription.FieldOffsetAttribute); diagnostics.Add(ErrorCode.ERR_StructOffsetOnBadStruct, allAttributeSyntaxNodes[i].Name.Location); } } else if (!this.IsStatic && !this.IsConst) { if (this.ContainingType.Layout.Kind == LayoutKind.Explicit) { // error CS0625: '<field>': instance field types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute diagnostics.Add(ErrorCode.ERR_MissingStructOffset, this.ErrorLocation, this); } } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
internal override void PostDecodeWellKnownAttributes(ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { Debug.Assert(!boundAttributes.IsDefault); Debug.Assert(!allAttributeSyntaxNodes.IsDefault); Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length); Debug.Assert(this.lazyCustomAttributesBag != null); Debug.Assert(this.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(this, AttributeDescription.ComImportAttribute); diagnostics.Add(ErrorCode.ERR_ComImportWithoutUuidAttribute, allAttributeSyntaxNodes[index].Name.Location, this.Name); } 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.Locations[0], 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(this, 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(this, AttributeDescription.DefaultMemberAttribute); diagnostics.Add(ErrorCode.ERR_DefaultMemberOnIndexedType, allAttributeSyntaxNodes[index].Name.Location); } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
internal override void PostDecodeWellKnownAttributes(ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { Debug.Assert(!boundAttributes.IsDefault); Debug.Assert(!allAttributeSyntaxNodes.IsDefault); Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length); Debug.Assert(this.lazyCustomAttributesBag != null); Debug.Assert(this.lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed); Debug.Assert(symbolPart == AttributeLocation.None); var data = (CommonParameterWellKnownAttributeData)decodedData; if (data != null) { if (this.RefKind == RefKind.Ref && data.HasOutAttribute && !data.HasInAttribute) { // error CS0662: '...' cannot specify only Out attribute on a ref parameter. Use both In and Out attributes, or neither. diagnostics.Add(ErrorCode.ERR_OutAttrOnRefParam, this.Locations[0]); } } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
private ImmutableArray <AttributeSyntax> GetAttributesToBind( OneOrMany <SyntaxList <AttributeListSyntax> > attributeDeclarationSyntaxLists, AttributeLocation symbolPart, BindingDiagnosticBag diagnostics, CSharpCompilation compilation, Func <AttributeSyntax, bool> attributeMatchesOpt, Binder rootBinderOpt, out ImmutableArray <Binder> binders) { var attributeTarget = (IAttributeTargetSymbol)this; ArrayBuilder <AttributeSyntax> syntaxBuilder = null; ArrayBuilder <Binder> bindersBuilder = null; int attributesToBindCount = 0; for (int listIndex = 0; listIndex < attributeDeclarationSyntaxLists.Count; listIndex++) { var attributeDeclarationSyntaxList = attributeDeclarationSyntaxLists[listIndex]; if (attributeDeclarationSyntaxList.Any()) { int prevCount = attributesToBindCount; foreach (var attributeDeclarationSyntax in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. if (MatchAttributeTarget(attributeTarget, symbolPart, attributeDeclarationSyntax.Target, diagnostics)) { if (syntaxBuilder == null) { syntaxBuilder = new ArrayBuilder <AttributeSyntax>(); bindersBuilder = new ArrayBuilder <Binder>(); } var attributesToBind = attributeDeclarationSyntax.Attributes; if (attributeMatchesOpt is null) { syntaxBuilder.AddRange(attributesToBind); attributesToBindCount += attributesToBind.Count; } else { foreach (var attribute in attributesToBind) { if (attributeMatchesOpt(attribute)) { syntaxBuilder.Add(attribute); attributesToBindCount++; } } } } } if (attributesToBindCount != prevCount) { Debug.Assert(attributeDeclarationSyntaxList.Node != null); Debug.Assert(bindersBuilder != null); var syntaxTree = attributeDeclarationSyntaxList.Node.SyntaxTree; var binder = rootBinderOpt ?? compilation.GetBinderFactory(syntaxTree).GetBinder(attributeDeclarationSyntaxList.Node); binder = new ContextualAttributeBinder(binder, this); Debug.Assert(!binder.InAttributeArgument || this is MethodSymbol { MethodKind: MethodKind.LambdaMethod }, "Possible cycle in attribute binding");
internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag 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(this, AttributeDescription.ComImportAttribute); diagnostics.Add(ErrorCode.ERR_ComImportWithoutUuidAttribute, allAttributeSyntaxNodes[index].Name.Location, this.Name); } 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.Locations[0], 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(this, 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(this, AttributeDescription.DefaultMemberAttribute); diagnostics.Add(ErrorCode.ERR_DefaultMemberOnIndexedType, allAttributeSyntaxNodes[index].Name.Location); } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
/// <summary> /// Method to merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target. /// This is the first step in attribute binding. /// </summary> /// <remarks> /// This method can generate diagnostics for few cases where we have an invalid target specifier and the parser hasn't generated the necessary diagnostics. /// It should not perform any bind operations as it can lead to an attribute binding cycle. /// </remarks> private ImmutableArray <AttributeSyntax> GetAttributesToBind( OneOrMany <SyntaxList <AttributeSyntax> > attributeDeclarationSyntaxLists, AttributeLocation symbolPart, DiagnosticBag diagnostics, CSharpCompilation compilation, Func <AttributeSyntax, bool> attributeMatchesOpt, Binder rootBinderOpt, out ImmutableArray <Binder> binders, Func <Binder, Binder> contextualBinder = null) { var attributeTarget = (IAttributeTargetSymbol)this; ArrayBuilder <AttributeSyntax> syntaxBuilder = null; ArrayBuilder <Binder> bindersBuilder = null; int attributesToBindCount = 0; for (int listIndex = 0; listIndex < attributeDeclarationSyntaxLists.Count; listIndex++) { var attributeDeclarationSyntaxList = attributeDeclarationSyntaxLists[listIndex]; if (attributeDeclarationSyntaxList.Any()) { int prevCount = attributesToBindCount; foreach (var attribute in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. if (MatchAttributeTarget(attributeTarget, symbolPart, attribute.Target, diagnostics)) { if (syntaxBuilder == null) { syntaxBuilder = new ArrayBuilder <AttributeSyntax>(); bindersBuilder = new ArrayBuilder <Binder>(); } if (attributeMatchesOpt is null || attributeMatchesOpt(attribute)) { syntaxBuilder.Add(attribute); attributesToBindCount++; } } } if (attributesToBindCount != prevCount) { Debug.Assert(attributeDeclarationSyntaxList.Node != null); Debug.Assert(bindersBuilder != null); var syntaxTree = attributeDeclarationSyntaxList.Node.SyntaxTree; var binder = rootBinderOpt ?? compilation.GetBinderFactory(syntaxTree).GetBinder(attributeDeclarationSyntaxList.Node); // We are wrapping the binder with a contextual binder if (contextualBinder != null) { binder = contextualBinder(binder); } binder = new ContextualAttributeBinder(binder, this); Debug.Assert(!binder.InAttributeArgument, "Possible cycle in attribute binding"); for (int i = 0; i < attributesToBindCount - prevCount; i++) { bindersBuilder.Add(binder); } } } } if (syntaxBuilder != null) { binders = bindersBuilder.ToImmutableAndFree(); return(syntaxBuilder.ToImmutableAndFree()); } else { binders = ImmutableArray <Binder> .Empty; return(ImmutableArray <AttributeSyntax> .Empty); } }
/// <summary> /// Verify if the attribute type can be applied to given owner symbol. /// Generate a diagnostic if it cannot be applied /// </summary> /// <param name="ownerSymbol">Symbol on which the attribute is applied</param> /// <param name="attributeType">Attribute class for the attribute</param> /// <param name="node">Syntax node for attribute specification</param> /// <param name="attributeLocation">Attribute target specifier location</param> /// <param name="diagnostics">Diagnostics</param> /// <returns>Whether attribute specification is allowed for the given symbol</returns> internal bool VerifyAttributeUsageTarget(Symbol ownerSymbol, NamedTypeSymbol attributeType, AttributeSyntax node, AttributeLocation attributeLocation, DiagnosticBag diagnostics) { var attributeUsageInfo = attributeType.AttributeUsageInfo(Compilation); if (attributeUsageInfo != null) { AttributeTargets attributeTarget; if (attributeLocation == AttributeLocation.Return) { // attribute on return type attributeTarget = AttributeTargets.ReturnValue; } else { attributeTarget = ownerSymbol.GetAttributeTarget(); } if ((attributeTarget & attributeUsageInfo.ValidTargets) != 0) { return(true); } // generate error Error(diagnostics, ErrorCode.ERR_AttributeOnBadSymbolType, node, GetAttributeNameFromSyntax(node), attributeUsageInfo.GetValidTargetsString()); } return(false); }
/// <summary> /// Validate attribute usage target and duplicate attributes. /// </summary> /// <param name="attribute">Bound attribute</param> /// <param name="node">Syntax node for attribute specification</param> /// <param name="compilation">Compilation</param> /// <param name="symbolPart">Symbol part to which the attribute has been applied.</param> /// <param name="diagnostics">Diagnostics</param> /// <param name="uniqueAttributeTypes">Set of unique attribute types applied to the symbol</param> private bool ValidateAttributeUsage( CSharpAttributeData attribute, AttributeSyntax node, CSharpCompilation compilation, AttributeLocation symbolPart, DiagnosticBag diagnostics, HashSet<NamedTypeSymbol> uniqueAttributeTypes) { Debug.Assert(!attribute.HasErrors); NamedTypeSymbol attributeType = attribute.AttributeClass; AttributeUsageInfo attributeUsageInfo = attributeType.GetAttributeUsageInfo(); // Given attribute can't be specified more than once if AllowMultiple is false. if (!uniqueAttributeTypes.Add(attributeType) && !attributeUsageInfo.AllowMultiple) { diagnostics.Add(ErrorCode.ERR_DuplicateAttribute, node.Name.Location, node.Name); return false; } // Verify if the attribute type can be applied to given owner symbol. AttributeTargets attributeTarget; if (symbolPart == AttributeLocation.Return) { // attribute on return type Debug.Assert(this.Kind == SymbolKind.Method); attributeTarget = AttributeTargets.ReturnValue; } else { attributeTarget = this.GetAttributeTarget(); } if ((attributeTarget & attributeUsageInfo.ValidTargets) == 0) { // generate error diagnostics.Add(ErrorCode.ERR_AttributeOnBadSymbolType, node.Name.Location, node.Name, attributeUsageInfo.GetValidTargetsErrorArgument()); return false; } if (attribute.IsSecurityAttribute(compilation)) { switch (this.Kind) { case SymbolKind.Assembly: case SymbolKind.NamedType: case SymbolKind.Method: break; default: // CS7070: Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. diagnostics.Add(ErrorCode.ERR_SecurityAttributeInvalidTarget, node.Name.Location, node.GetErrorDisplayName()); return false; } } return true; }
/// <summary> /// Method to early decode certain well-known attributes which can be queried by the binder. /// This method is called during attribute binding after we have bound the attribute types for all attributes, /// but haven't yet bound the attribute arguments/attribute constructor. /// Early decoding certain well-known attributes enables the binder to use this decoded information on this symbol /// when binding the attribute arguments/attribute constructor without causing attribute binding cycle. /// </summary> internal EarlyWellKnownAttributeData EarlyDecodeWellKnownAttributes( ImmutableArray<Binder> binders, ImmutableArray<NamedTypeSymbol> boundAttributeTypes, ImmutableArray<AttributeSyntax> attributesToBind, AttributeLocation symbolPart, CSharpAttributeData[] boundAttributesBuilder) { Debug.Assert(boundAttributeTypes.Any()); Debug.Assert(attributesToBind.Any()); Debug.Assert(binders.Any()); Debug.Assert(boundAttributesBuilder != null); Debug.Assert(!boundAttributesBuilder.Contains((attr) => attr != null)); var earlyBinder = new EarlyWellKnownAttributeBinder(binders[0]); var arguments = new EarlyDecodeWellKnownAttributeArguments<EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation>(); arguments.SymbolPart = symbolPart; for (int i = 0; i < boundAttributeTypes.Length; i++) { NamedTypeSymbol boundAttributeType = boundAttributeTypes[i]; if (!boundAttributeType.IsErrorType()) { if (binders[i] != earlyBinder.Next) { earlyBinder = new EarlyWellKnownAttributeBinder(binders[i]); } arguments.Binder = earlyBinder; arguments.AttributeType = boundAttributeType; arguments.AttributeSyntax = attributesToBind[i]; // Early bind some well-known attributes CSharpAttributeData earlyBoundAttributeOpt = this.EarlyDecodeWellKnownAttribute(ref arguments); Debug.Assert(earlyBoundAttributeOpt == null || !earlyBoundAttributeOpt.HasErrors); boundAttributesBuilder[i] = earlyBoundAttributeOpt; } } return arguments.HasDecodedData ? arguments.DecodedData : null; }
/// <summary> /// Method to merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target. /// This is the first step in attribute binding. /// </summary> /// <remarks> /// This method can generate diagnostics for few cases where we have an invalid target specifier and the parser hasn't generated the necessary diagnostics. /// It should not perform any bind operations as it can lead to an attribute binding cycle. /// </remarks> private ImmutableArray<AttributeSyntax> GetAttributesToBind( OneOrMany<SyntaxList<AttributeListSyntax>> attributeDeclarationSyntaxLists, AttributeLocation symbolPart, DiagnosticBag diagnostics, CSharpCompilation compilation, out ImmutableArray<Binder> binders) { var attributeTarget = (IAttributeTargetSymbol)this; ArrayBuilder<AttributeSyntax> syntaxBuilder = null; ArrayBuilder<Binder> bindersBuilder = null; int attributesToBindCount = 0; for (int listIndex = 0; listIndex < attributeDeclarationSyntaxLists.Count; listIndex++) { var attributeDeclarationSyntaxList = attributeDeclarationSyntaxLists[listIndex]; if (attributeDeclarationSyntaxList.Any()) { int prevCount = attributesToBindCount; foreach (var attributeDeclarationSyntax in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. if (MatchAttributeTarget(attributeTarget, symbolPart, attributeDeclarationSyntax.Target, diagnostics)) { if (syntaxBuilder == null) { syntaxBuilder = new ArrayBuilder<AttributeSyntax>(); bindersBuilder = new ArrayBuilder<Binder>(); } var attributesToBind = attributeDeclarationSyntax.Attributes; syntaxBuilder.AddRange(attributesToBind); attributesToBindCount += attributesToBind.Count; } } if (attributesToBindCount != prevCount) { Debug.Assert(attributeDeclarationSyntaxList.Node != null); Debug.Assert(bindersBuilder != null); var syntaxTree = attributeDeclarationSyntaxList.Node.SyntaxTree; var binder = compilation.GetBinderFactory(syntaxTree).GetBinder((CSharpSyntaxNode)attributeDeclarationSyntaxList.Node); binder = new ContextualAttributeBinder(binder, this); Debug.Assert(!binder.InAttributeArgument, "Possible cycle in attribute binding"); for (int i = 0; i < attributesToBindCount - prevCount; i++) { bindersBuilder.Add(binder); } } } } if (syntaxBuilder != null) { binders = bindersBuilder.ToImmutableAndFree(); return syntaxBuilder.ToImmutableAndFree(); } else { binders = ImmutableArray<Binder>.Empty; return ImmutableArray<AttributeSyntax>.Empty; } }
/// <summary> /// Called to report attribute related diagnostics after all attributes have been bound and decoded. /// Called even if there are no attributes. /// </summary> /// <remarks> /// This method is called by the binder from <see cref="LoadAndValidateAttributes"/> after it has finished binding attributes on the symbol, /// has executed <see cref="DecodeWellKnownAttribute"/> for attributes applied on the symbol and has stored the decoded data in the /// lazyCustomAttributesBag on the symbol. Bound attributes haven't been stored on the bag yet. /// /// Post-validation for attributes that is dependant on other attributes can be done here. /// /// This method should not have any side effects on the symbol, i.e. it SHOULD NOT change the symbol state. /// </remarks> /// <param name="boundAttributes">Bound attributes.</param> /// <param name="allAttributeSyntaxNodes">Syntax nodes of attributes in order they are specified in source, or null if there are no attributes.</param> /// <param name="diagnostics">Diagnostic bag.</param> /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param> /// <param name="decodedData">Decoded well-known attribute data, could be null.</param> internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { }
internal static string ToDisplayString(this AttributeLocation locations) { StringBuilder result = new StringBuilder(); for (int i = 1; i < (int)AttributeLocation.Unknown; i <<= 1) { if ((locations & (AttributeLocation)i) != 0) { if (result.Length > 0) { result.Append(", "); } switch ((AttributeLocation)i) { case AttributeLocation.Assembly: result.Append("assembly"); break; case AttributeLocation.Module: result.Append("module"); break; case AttributeLocation.Type: result.Append("type"); break; case AttributeLocation.Method: result.Append("method"); break; case AttributeLocation.Field: result.Append("field"); break; case AttributeLocation.Property: result.Append("property"); break; case AttributeLocation.Event: result.Append("event"); break; case AttributeLocation.Return: result.Append("return"); break; case AttributeLocation.Parameter: result.Append("param"); break; case AttributeLocation.TypeParameter: result.Append("typevar"); break; default: throw ExceptionUtilities.UnexpectedValue(i); } } } return(result.ToString()); }
/// <summary> /// This method does the following set of operations in the specified order: /// (1) GetAttributesToBind: Merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target. /// (2) BindAttributeTypes: Bind all the attribute types to enable early decode of certain well-known attributes by type. /// (3) EarlyDecodeWellKnownAttributes: Perform early decoding of certain well-known attributes that could be queried by the binder in subsequent steps. /// (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes). /// (4) GetAttributes: Bind the attributes (attribute arguments and constructor) using bound attribute types. /// (5) DecodeWellKnownAttributes: Decode and validate bound well known attributes. /// (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes). /// (6) StoreBoundAttributesAndDoPostValidation: /// (a) Store the bound attributes in lazyCustomAttributes in a thread safe manner. /// (b) Perform some additional post attribute validations, such as /// 1) Duplicate attributes, attribute usage target validation, etc. /// 2) Post validation for attributes dependant on other attributes /// These validations cannot be performed prior to step 6(a) as we might need to /// perform a GetAttributes() call on a symbol which can introduce a cycle in attribute binding. /// We avoid this cycle by performing such validations in PostDecodeWellKnownAttributes after lazyCustomAttributes have been set. /// NOTE: PostDecodeWellKnownAttributes SHOULD NOT change the symbol state. /// </summary> /// <remarks> /// Current design of early decoding well-known attributes doesn't permit decoding attribute arguments/constructor as this can lead to binding cycles. /// For well-known attributes used by the binder, where we need the decoded arguments, we must handle them specially in one of the following possible ways: /// (a) Avoid decoding the attribute arguments during binding and delay the corresponding binder tasks to a separate post-pass executed after binding. /// (b) As the cycles can be caused only when we are binding attribute arguments/constructor, special case the corresponding binder tasks based on the current BinderFlags. /// </remarks> /// <param name="attributesSyntaxLists"></param> /// <param name="lazyCustomAttributesBag"></param> /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param> /// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param> /// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns> internal bool LoadAndValidateAttributes( OneOrMany<SyntaxList<AttributeListSyntax>> attributesSyntaxLists, ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag, AttributeLocation symbolPart = AttributeLocation.None, bool earlyDecodingOnly = false) { var diagnostics = DiagnosticBag.GetInstance(); var compilation = this.DeclaringCompilation; ImmutableArray<Binder> binders; ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, out binders); Debug.Assert(!attributesToBind.IsDefault); ImmutableArray<CSharpAttributeData> boundAttributes; WellKnownAttributeData wellKnownAttributeData; if (attributesToBind.Any()) { Debug.Assert(!binders.IsDefault); Debug.Assert(binders.Length == attributesToBind.Length); // Initialize the bag so that data decoded from early attributes can be stored onto it. if (lazyCustomAttributesBag == null) { Interlocked.CompareExchange(ref lazyCustomAttributesBag, new CustomAttributesBag<CSharpAttributeData>(), null); } // Bind the attribute types and then early decode them. int totalAttributesCount = attributesToBind.Length; var attributeTypesBuilder = new NamedTypeSymbol[totalAttributesCount]; Binder.BindAttributeTypes(binders, attributesToBind, this, attributeTypesBuilder, diagnostics); ImmutableArray<NamedTypeSymbol> boundAttributeTypes = attributeTypesBuilder.AsImmutableOrNull(); this.EarlyDecodeWellKnownAttributeTypes(boundAttributeTypes, attributesToBind); this.PostEarlyDecodeWellKnownAttributeTypes(); // Bind the attribute in two stages - early and normal. var attributesBuilder = new CSharpAttributeData[totalAttributesCount]; // Early bind and decode some well-known attributes. EarlyWellKnownAttributeData earlyData = this.EarlyDecodeWellKnownAttributes(binders, boundAttributeTypes, attributesToBind, symbolPart, attributesBuilder); Debug.Assert(!attributesBuilder.Contains((attr) => attr != null && attr.HasErrors)); // Store data decoded from early bound well-known attributes. // TODO: what if this succeeds on another thread, not ours? lazyCustomAttributesBag.SetEarlyDecodedWellKnownAttributeData(earlyData); if (earlyDecodingOnly) { diagnostics.Free(); //NOTE: dropped. return false; } // Bind attributes. Binder.GetAttributes(binders, attributesToBind, boundAttributeTypes, attributesBuilder, diagnostics); boundAttributes = attributesBuilder.AsImmutableOrNull(); // All attributes must be bound by now. Debug.Assert(!boundAttributes.Any((attr) => attr == null)); // Validate attribute usage and Decode remaining well-known attributes. wellKnownAttributeData = this.ValidateAttributeUsageAndDecodeWellKnownAttributes(binders, attributesToBind, boundAttributes, diagnostics, symbolPart); // Store data decoded from remaining well-known attributes. // TODO: what if this succeeds on another thread but not this thread? lazyCustomAttributesBag.SetDecodedWellKnownAttributeData(wellKnownAttributeData); } else if (earlyDecodingOnly) { diagnostics.Free(); //NOTE: dropped. return false; } else { boundAttributes = ImmutableArray<CSharpAttributeData>.Empty; wellKnownAttributeData = null; Interlocked.CompareExchange(ref lazyCustomAttributesBag, CustomAttributesBag<CSharpAttributeData>.WithEmptyData(), null); this.PostEarlyDecodeWellKnownAttributeTypes(); } this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData); // Store attributes into the bag. bool lazyAttributesStoredOnThisThread = false; if (lazyCustomAttributesBag.SetAttributes(boundAttributes)) { this.RecordPresenceOfBadAttributes(boundAttributes); this.AddDeclarationDiagnostics(diagnostics); lazyAttributesStoredOnThisThread = true; if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty; } Debug.Assert(lazyCustomAttributesBag.IsSealed); diagnostics.Free(); return lazyAttributesStoredOnThisThread; }
internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag 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 = (CommonParameterWellKnownAttributeData)decodedData; if (data != null) { if (this.RefKind == RefKind.Ref && data.HasOutAttribute && !data.HasInAttribute) { // error CS0662: '...' cannot specify only Out attribute on a ref parameter. Use both In and Out attributes, or neither. diagnostics.Add(ErrorCode.ERR_OutAttrOnRefParam, this.Locations[0]); } } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
private static bool MatchAttributeTarget(IAttributeTargetSymbol attributeTarget, AttributeLocation symbolPart, AttributeTargetSpecifierSyntax targetOpt, DiagnosticBag diagnostics) { IAttributeTargetSymbol attributesOwner = attributeTarget.AttributesOwner; // Determine if the target symbol owns the attribute declaration. // We need to report diagnostics only once, so do it when visiting attributes for the owner. bool isOwner = symbolPart == AttributeLocation.None && ReferenceEquals(attributesOwner, attributeTarget); if (targetOpt == null) { // only attributes with an explicit target match if the symbol doesn't own the attributes: return isOwner; } AttributeLocation explicitTarget = targetOpt.GetAttributeLocation(); if (explicitTarget == AttributeLocation.None) { // error: unknown attribute location if (isOwner) { //NOTE: ValueText so that we accept targets like "@return", to match dev10 (DevDiv #2591). diagnostics.Add(ErrorCode.WRN_InvalidAttributeLocation, targetOpt.Identifier.GetLocation(), targetOpt.Identifier.ValueText); } return false; } AttributeLocation allowedTargets = attributesOwner.AllowedAttributeLocations; if ((explicitTarget & allowedTargets) == 0) { // error: invalid target for symbol if (isOwner) { if (allowedTargets == AttributeLocation.None) { switch (attributeTarget.DefaultAttributeLocation) { case AttributeLocation.Assembly: case AttributeLocation.Module: // global attributes are disallowed in interactive code: diagnostics.Add(ErrorCode.ERR_GlobalAttributesNotAllowed, targetOpt.Identifier.GetLocation()); break; default: // currently this can't happen throw ExceptionUtilities.UnexpectedValue(attributeTarget.DefaultAttributeLocation); } } else { diagnostics.Add(ErrorCode.WRN_AttributeLocationOnBadDeclaration, targetOpt.Identifier.GetLocation(), targetOpt.Identifier.ToString(), allowedTargets.ToDisplayString()); } } return false; } if (symbolPart == AttributeLocation.None) { return explicitTarget == attributeTarget.DefaultAttributeLocation; } else { return explicitTarget == symbolPart; } }
internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { Debug.Assert(!boundAttributes.IsDefault); Debug.Assert(!allAttributeSyntaxNodes.IsDefault); Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length); Debug.Assert(symbolPart == AttributeLocation.None || symbolPart == AttributeLocation.Return); if (symbolPart != AttributeLocation.Return) { Debug.Assert(this.lazyCustomAttributesBag != null); Debug.Assert(this.lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed); if (this.containingType.IsComImport && this.containingType.TypeKind == TypeKind.Class) { switch (this.MethodKind) { case MethodKind.Constructor: case MethodKind.StaticConstructor: if (!this.IsImplicitlyDeclared) { // CS0669: A class with the ComImport attribute cannot have a user-defined constructor diagnostics.Add(ErrorCode.ERR_ComImportWithUserCtor, this.Locations[0]); } break; default: if (!this.IsAbstract && !this.IsExtern) { // CS0423: Since '{1}' has the ComImport attribute, '{0}' must be extern or abstract diagnostics.Add(ErrorCode.ERR_ComImportWithImpl, this.Locations[0], this, this.containingType); } break; } } } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
/// <summary> /// This method validates attribute usage for each bound attribute and calls <see cref="DecodeWellKnownAttribute"/> /// on attributes with valid attribute usage. /// This method is called by the binder when it is finished binding a set of attributes on the symbol so that /// the symbol can extract data from the attribute arguments and potentially perform validation specific to /// some well known attributes. /// </summary> private WellKnownAttributeData ValidateAttributeUsageAndDecodeWellKnownAttributes( ImmutableArray<Binder> binders, ImmutableArray<AttributeSyntax> attributeSyntaxList, ImmutableArray<CSharpAttributeData> boundAttributes, DiagnosticBag diagnostics, AttributeLocation symbolPart) { Debug.Assert(binders.Any()); Debug.Assert(attributeSyntaxList.Any()); Debug.Assert(boundAttributes.Any()); Debug.Assert(binders.Length == boundAttributes.Length); Debug.Assert(attributeSyntaxList.Length == boundAttributes.Length); int totalAttributesCount = boundAttributes.Length; HashSet<NamedTypeSymbol> uniqueAttributeTypes = new HashSet<NamedTypeSymbol>(); var arguments = new DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation>(); arguments.Diagnostics = diagnostics; arguments.AttributesCount = totalAttributesCount; arguments.SymbolPart = symbolPart; for (int i = 0; i < totalAttributesCount; i++) { CSharpAttributeData boundAttribute = boundAttributes[i]; AttributeSyntax attributeSyntax = attributeSyntaxList[i]; Binder binder = binders[i]; // Decode attribute as a possible well-known attribute only if it has no binding errors and has valid AttributeUsage. if (!boundAttribute.HasErrors && ValidateAttributeUsage(boundAttribute, attributeSyntax, binder.Compilation, symbolPart, diagnostics, uniqueAttributeTypes)) { arguments.Attribute = boundAttribute; arguments.AttributeSyntaxOpt = attributeSyntax; arguments.Index = i; this.DecodeWellKnownAttribute(ref arguments); } } return arguments.HasDecodedData ? arguments.DecodedData : null; }
internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag 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 = (CommonFieldWellKnownAttributeData)decodedData; int? fieldOffset = data != null ? data.Offset : null; if (fieldOffset.HasValue) { if (this.ContainingType.Layout.Kind != LayoutKind.Explicit) { Debug.Assert(boundAttributes.Any()); // error CS0636: The FieldOffset attribute can only be placed on members of types marked with the StructLayout(LayoutKind.Explicit) int i = boundAttributes.IndexOfAttribute(this, AttributeDescription.FieldOffsetAttribute); diagnostics.Add(ErrorCode.ERR_StructOffsetOnBadStruct, allAttributeSyntaxNodes[i].Name.Location); } } else if (!this.IsStatic && !this.IsConst) { if (this.ContainingType.Layout.Kind == LayoutKind.Explicit) { // error CS0625: '<field>': instance field types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute diagnostics.Add(ErrorCode.ERR_MissingStructOffset, this.ErrorLocation, this); } } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
/// <summary> /// This method does the following set of operations in the specified order: /// (1) GetAttributesToBind: Merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target. /// (2) BindAttributeTypes: Bind all the attribute types to enable early decode of certain well-known attributes by type. /// (3) EarlyDecodeWellKnownAttributes: Perform early decoding of certain well-known attributes that could be queried by the binder in subsequent steps. /// (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes). /// (4) GetAttributes: Bind the attributes (attribute arguments and constructor) using bound attribute types. /// (5) DecodeWellKnownAttributes: Decode and validate bound well known attributes. /// (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes). /// (6) StoreBoundAttributesAndDoPostValidation: /// (a) Store the bound attributes in lazyCustomAttributes in a thread safe manner. /// (b) Perform some additional post attribute validations, such as /// 1) Duplicate attributes, attribute usage target validation, etc. /// 2) Post validation for attributes dependent on other attributes /// These validations cannot be performed prior to step 6(a) as we might need to /// perform a GetAttributes() call on a symbol which can introduce a cycle in attribute binding. /// We avoid this cycle by performing such validations in PostDecodeWellKnownAttributes after lazyCustomAttributes have been set. /// NOTE: PostDecodeWellKnownAttributes SHOULD NOT change the symbol state. /// </summary> /// <remarks> /// Current design of early decoding well-known attributes doesn't permit decoding attribute arguments/constructor as this can lead to binding cycles. /// For well-known attributes used by the binder, where we need the decoded arguments, we must handle them specially in one of the following possible ways: /// (a) Avoid decoding the attribute arguments during binding and delay the corresponding binder tasks to a separate post-pass executed after binding. /// (b) As the cycles can be caused only when we are binding attribute arguments/constructor, special case the corresponding binder tasks based on the current BinderFlags. /// </remarks> /// <param name="attributesSyntaxLists"></param> /// <param name="lazyCustomAttributesBag"></param> /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param> /// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param> /// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param> /// <param name="attributeMatchesOpt">If specified, only load attributes that match this predicate, and any diagnostics produced will be dropped.</param> /// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns> internal bool LoadAndValidateAttributes( OneOrMany <SyntaxList <AttributeSyntax> > attributesSyntaxLists, ref CustomAttributesBag <CSharpAttributeData> lazyCustomAttributesBag, AttributeLocation symbolPart = AttributeLocation.None, bool earlyDecodingOnly = false, Binder binderOpt = null, Func <AttributeSyntax, bool> attributeMatchesOpt = null, Func <Binder, Binder> contextualBinder = null ) { var diagnostics = DiagnosticBag.GetInstance(); var compilation = this.DeclaringCompilation; ImmutableArray <Binder> binders; ImmutableArray <AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, attributeMatchesOpt, binderOpt, out binders, contextualBinder); Debug.Assert(!attributesToBind.IsDefault); ImmutableArray <CSharpAttributeData> boundAttributes; WellKnownAttributeData wellKnownAttributeData; if (attributesToBind.Any()) { Debug.Assert(!binders.IsDefault); Debug.Assert(binders.Length == attributesToBind.Length); // Initialize the bag so that data decoded from early attributes can be stored onto it. if (lazyCustomAttributesBag == null) { Interlocked.CompareExchange(ref lazyCustomAttributesBag, new CustomAttributesBag <CSharpAttributeData>(), null); } // Bind the attribute types and then early decode them. int totalAttributesCount = attributesToBind.Length; var attributeTypesBuilder = new NamedTypeSymbol[totalAttributesCount]; Binder.BindAttributeTypes(binders, attributesToBind, this, attributeTypesBuilder, diagnostics); ImmutableArray <NamedTypeSymbol> boundAttributeTypes = attributeTypesBuilder.AsImmutableOrNull(); this.EarlyDecodeWellKnownAttributeTypes(boundAttributeTypes, attributesToBind); this.PostEarlyDecodeWellKnownAttributeTypes(); // Bind the attribute in two stages - early and normal. var attributesBuilder = new CSharpAttributeData[totalAttributesCount]; // Early bind and decode some well-known attributes. EarlyWellKnownAttributeData earlyData = this.EarlyDecodeWellKnownAttributes(binders, boundAttributeTypes, attributesToBind, symbolPart, attributesBuilder); Debug.Assert(!attributesBuilder.Contains((attr) => attr != null && attr.HasErrors)); // Store data decoded from early bound well-known attributes. // TODO: what if this succeeds on another thread, not ours? lazyCustomAttributesBag.SetEarlyDecodedWellKnownAttributeData(earlyData); if (earlyDecodingOnly) { diagnostics.Free(); //NOTE: dropped. return(false); } // Bind attributes. Binder.GetAttributes(binders, attributesToBind, boundAttributeTypes, attributesBuilder, diagnostics); boundAttributes = attributesBuilder.AsImmutableOrNull(); // All attributes must be bound by now. Debug.Assert(!boundAttributes.Any((attr) => attr == null)); // Validate attribute usage and Decode remaining well-known attributes. wellKnownAttributeData = this.ValidateAttributeUsageAndDecodeWellKnownAttributes(binders, attributesToBind, boundAttributes, diagnostics, symbolPart); // Store data decoded from remaining well-known attributes. // TODO: what if this succeeds on another thread but not this thread? lazyCustomAttributesBag.SetDecodedWellKnownAttributeData(wellKnownAttributeData); } else if (earlyDecodingOnly) { diagnostics.Free(); //NOTE: dropped. return(false); } else { boundAttributes = ImmutableArray <CSharpAttributeData> .Empty; wellKnownAttributeData = null; Interlocked.CompareExchange(ref lazyCustomAttributesBag, CustomAttributesBag <CSharpAttributeData> .WithEmptyData(), null); this.PostEarlyDecodeWellKnownAttributeTypes(); } this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData); // Store attributes into the bag. bool lazyAttributesStoredOnThisThread = false; if (lazyCustomAttributesBag.SetAttributes(boundAttributes)) { if (attributeMatchesOpt is null) { this.RecordPresenceOfBadAttributes(boundAttributes); AddDeclarationDiagnostics(diagnostics); } lazyAttributesStoredOnThisThread = true; if (lazyCustomAttributesBag.IsEmpty) { lazyCustomAttributesBag = CustomAttributesBag <CSharpAttributeData> .Empty; } } Debug.Assert(lazyCustomAttributesBag.IsSealed); diagnostics.Free(); return(lazyAttributesStoredOnThisThread); }
/// <summary> /// Validate attribute usage target and duplicate attributes. /// </summary> /// <param name="attribute">Bound attribute</param> /// <param name="node">Syntax node for attribute specification</param> /// <param name="compilation">Compilation</param> /// <param name="symbolPart">Symbol part to which the attribute has been applied.</param> /// <param name="diagnostics">Diagnostics</param> /// <param name="uniqueAttributeTypes">Set of unique attribute types applied to the symbol</param> private bool ValidateAttributeUsage( CSharpAttributeData attribute, AttributeSyntax node, CSharpCompilation compilation, AttributeLocation symbolPart, DiagnosticBag diagnostics, HashSet <NamedTypeSymbol> uniqueAttributeTypes) { Debug.Assert(!attribute.HasErrors); NamedTypeSymbol attributeType = attribute.AttributeClass; AttributeUsageInfo attributeUsageInfo = attributeType.GetAttributeUsageInfo(); // Given attribute can't be specified more than once if AllowMultiple is false. if (!uniqueAttributeTypes.Add(attributeType) && !attributeUsageInfo.AllowMultiple) { diagnostics.Add(ErrorCode.ERR_DuplicateAttribute, node.Name.Location, node.GetErrorDisplayName()); return(false); } // Verify if the attribute type can be applied to given owner symbol. AttributeTargets attributeTarget; if (symbolPart == AttributeLocation.Return) { // attribute on return type Debug.Assert(this.Kind == SymbolKind.Method); attributeTarget = AttributeTargets.ReturnValue; } else { attributeTarget = this.GetAttributeTarget(); } if ((attributeTarget & attributeUsageInfo.ValidTargets) == 0) { #if XSHARP // The ClipperCallingConvention Attribute in VulcanRT has incorrect Targets (Method, but should also be allowed on Constructors) if (node.Name.ToString().ToLower().IndexOf(OurTypeNames.ClipperCallingConventionAttribute, XSharpString.Comparison) == -1) { #endif // generate error diagnostics.Add(ErrorCode.ERR_AttributeOnBadSymbolType, node.Name.Location, node.GetErrorDisplayName(), attributeUsageInfo.GetValidTargetsErrorArgument()); return(false); #if XSHARP } #endif } if (attribute.IsSecurityAttribute(compilation)) { switch (this.Kind) { case SymbolKind.Assembly: case SymbolKind.NamedType: case SymbolKind.Method: break; default: // CS7070: Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. diagnostics.Add(ErrorCode.ERR_SecurityAttributeInvalidTarget, node.Name.Location, node.GetErrorDisplayName()); return(false); } } return(true); }
private static bool MatchAttributeTarget(IAttributeTargetSymbol attributeTarget, AttributeLocation symbolPart, AttributeTargetSpecifierSyntax targetOpt, DiagnosticBag diagnostics) { IAttributeTargetSymbol attributesOwner = attributeTarget.AttributesOwner; // Determine if the target symbol owns the attribute declaration. // We need to report diagnostics only once, so do it when visiting attributes for the owner. bool isOwner = symbolPart == AttributeLocation.None && ReferenceEquals(attributesOwner, attributeTarget); if (targetOpt == null) { // only attributes with an explicit target match if the symbol doesn't own the attributes: return(isOwner); } AttributeLocation allowedTargets = attributesOwner.AllowedAttributeLocations; AttributeLocation explicitTarget = targetOpt.GetAttributeLocation(); if (explicitTarget == AttributeLocation.None) { // error: unknown attribute location if (isOwner) { //NOTE: ValueText so that we accept targets like "@return", to match dev10 (DevDiv #2591). diagnostics.Add(ErrorCode.WRN_InvalidAttributeLocation, targetOpt.Identifier.GetLocation(), targetOpt.Identifier.ValueText, allowedTargets.ToDisplayString()); } return(false); } if ((explicitTarget & allowedTargets) == 0) { // error: invalid target for symbol if (isOwner) { if (allowedTargets == AttributeLocation.None) { switch (attributeTarget.DefaultAttributeLocation) { case AttributeLocation.Assembly: case AttributeLocation.Module: // global attributes are disallowed in interactive code: diagnostics.Add(ErrorCode.ERR_GlobalAttributesNotAllowed, targetOpt.Identifier.GetLocation()); break; default: // currently this can't happen throw ExceptionUtilities.UnexpectedValue(attributeTarget.DefaultAttributeLocation); } } else { diagnostics.Add(ErrorCode.WRN_AttributeLocationOnBadDeclaration, targetOpt.Identifier.GetLocation(), targetOpt.Identifier.ToString(), allowedTargets.ToDisplayString()); } } return(false); } if (symbolPart == AttributeLocation.None) { return(explicitTarget == attributeTarget.DefaultAttributeLocation); } else { return(explicitTarget == symbolPart); } }
internal override void PostDecodeWellKnownAttributes(ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag 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; // 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(this, AttributeDescription.DefaultMemberAttribute); diagnostics.Add(ErrorCode.ERR_DefaultMemberOnIndexedType, allAttributeSyntaxNodes[index].Name.Location); } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag 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); if (this.IsAutoProperty && !this.IsStatic && this.ContainingType.Layout.Kind == LayoutKind.Explicit) { // error CS0842: '<property>': Automatically implemented properties cannot be used inside a type marked with StructLayout(LayoutKind.Explicit) diagnostics.Add(ErrorCode.ERR_ExplicitLayoutAndAutoImplementedProperty, this.Location, this); } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }
internal override void PostDecodeWellKnownAttributes(ImmutableArray <CSharpAttributeData> boundAttributes, ImmutableArray <AttributeSyntax> allAttributeSyntaxNodes, DiagnosticBag 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 = (CommonParameterWellKnownAttributeData)decodedData; if (data != null) { switch (RefKind) { case RefKind.Ref: if (data.HasOutAttribute && !data.HasInAttribute) { // error CS0662: Cannot specify the Out attribute on a ref parameter without also specifying the In attribute. diagnostics.Add(ErrorCode.ERR_OutAttrOnRefParam, this.Locations[0]); } break; case RefKind.Out: if (data.HasInAttribute) { // error CS0036: An out parameter cannot have the In attribute. diagnostics.Add(ErrorCode.ERR_InAttrOnOutParam, this.Locations[0]); } break; case RefKind.In: if (data.HasOutAttribute) { // error CS8355: An in parameter cannot have the Out attribute. diagnostics.Add(ErrorCode.ERR_OutAttrOnInParam, this.Locations[0]); } break; } } base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); }