protected static string ReportAttribute(CSharpAttributeData attribute) { var builder = new StringBuilder(); builder.Append('['); Assert.NotNull(attribute.AttributeClass); var name = attribute.AttributeClass !.Name; if (name.EndsWith("Attribute")) { name = name.Substring(0, name.Length - 9); } builder.Append(name); var arguments = attribute.ConstructorArguments.ToImmutableArray(); if (arguments.Length > 0) { builder.Append('('); printValues(builder, arguments); builder.Append(')'); } builder.Append(']'); return(builder.ToString());
public static void VerifyValue <T>(this CSharpAttributeData attr, int i, TypedConstantKind kind, T v) { var arg = attr.CommonConstructorArguments[i]; Assert.Equal(kind, arg.Kind); Assert.True(IsEqual(arg, v)); }
private static string ReportAttribute(CSharpAttributeData attribute) { var builder = new StringBuilder(); builder.Append("["); var name = attribute.AttributeClass.Name; if (name.EndsWith("Attribute")) { name = name.Substring(0, name.Length - 9); } builder.Append(name); var arguments = attribute.ConstructorArguments.ToImmutableArray(); if (arguments.Length > 0) { builder.Append("("); printValues(builder, arguments); builder.Append(")"); } builder.Append("]"); return(builder.ToString());
/// <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); }
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); }
public static void VerifyNamedArgumentValue <T>(this CSharpAttributeData attr, int i, string name, TypedConstantKind kind, T v) { var namedArg = attr.CommonNamedArguments[i]; Assert.Equal(namedArg.Key, name); var arg = namedArg.Value; Assert.Equal(arg.Kind, kind); Assert.True(IsEqual(arg, v)); }
private IEnumerable <CSharpAttributeData> GetCustomAttributesToEmitIterator( ImmutableArray <CSharpAttributeData> userDefined, ArrayBuilder <SynthesizedAttributeData> synthesized, bool isReturnType, bool emittingAssemblyAttributesInNetModule ) { CheckDefinitionInvariant(); if (synthesized != null) { foreach (var attribute in synthesized) { // only synthesize attributes that are emitted: Debug.Assert( attribute.ShouldEmitAttribute( this, isReturnType, emittingAssemblyAttributesInNetModule ) ); yield return(attribute); } synthesized.Free(); } for (int i = 0; i < userDefined.Length; i++) { CSharpAttributeData attribute = userDefined[i]; if (this.Kind == SymbolKind.Assembly) { // We need to filter out duplicate assembly attributes (i.e. attributes that // bind to the same constructor and have identical arguments) and invalid // InternalsVisibleTo attributes. if (((SourceAssemblySymbol)this).IsIndexOfOmittedAssemblyAttribute(i)) { continue; } } if ( attribute.ShouldEmitAttribute( this, isReturnType, emittingAssemblyAttributesInNetModule ) ) { yield return(attribute); } } }
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 override void VisitMethod(MethodSymbol method) { var previousContext = _nullableContext; _nullableContext = GetNullableContextAttribute(method.GetAttributes()) ?? _nullableContext; ReportSymbol(method); VisitList(method.TypeParameters); VisitList(method.Parameters); _nullableContext = previousContext; }
internal static bool EarlyDecodeDeprecatedOrObsoleteAttribute( ref EarlyDecodeWellKnownAttributeArguments <EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation> arguments, out CSharpAttributeData attributeData, out ObsoleteAttributeData obsoleteData) { bool hasAnyDiagnostics; if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.ObsoleteAttribute)) { attributeData = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); if (!attributeData.HasErrors) { obsoleteData = attributeData.DecodeObsoleteAttribute(); if (hasAnyDiagnostics) { attributeData = null; } } else { obsoleteData = null; attributeData = null; } return(true); } if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.DeprecatedAttribute)) { attributeData = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); if (!attributeData.HasErrors) { obsoleteData = attributeData.DecodeDeprecatedAttribute(); if (hasAnyDiagnostics) { attributeData = null; } } else { obsoleteData = null; attributeData = null; } return(true); } obsoleteData = null; attributeData = null; return(false); }
internal static bool EarlyDecodeDeprecatedOrExperimentalOrObsoleteAttribute( ref EarlyDecodeWellKnownAttributeArguments <EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation> arguments, out CSharpAttributeData attributeData, out ObsoleteAttributeData obsoleteData) { var type = arguments.AttributeType; var syntax = arguments.AttributeSyntax; ObsoleteAttributeKind kind; if (CSharpAttributeData.IsTargetEarlyAttribute(type, syntax, AttributeDescription.ObsoleteAttribute)) { kind = ObsoleteAttributeKind.Obsolete; } else if (CSharpAttributeData.IsTargetEarlyAttribute(type, syntax, AttributeDescription.DeprecatedAttribute)) { kind = ObsoleteAttributeKind.Deprecated; } else if (CSharpAttributeData.IsTargetEarlyAttribute(type, syntax, AttributeDescription.ExperimentalAttribute)) { kind = ObsoleteAttributeKind.Experimental; } else { obsoleteData = null; attributeData = null; return(false); } bool hasAnyDiagnostics; attributeData = arguments.Binder.GetAttribute(syntax, type, out hasAnyDiagnostics); if (!attributeData.HasErrors) { obsoleteData = attributeData.DecodeObsoleteAttribute(kind); if (hasAnyDiagnostics) { attributeData = null; } } else { obsoleteData = null; attributeData = null; } return(true); }
public override void VisitNamedType(NamedTypeSymbol type) { var previousContext = _nullableContext; _nullableContext = GetNullableContextAttribute(type.GetAttributes()) ?? _nullableContext; ReportSymbol(type); VisitList(type.TypeParameters); foreach (var member in type.GetMembers()) { // Skip accessors since those are covered by associated symbol. if (member.IsAccessor()) { continue; } Visit(member); } _nullableContext = previousContext; }
/// <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); }