internal static bool CanBeValidAttributeArgument(ExpressionSyntax node, Binder typeBinder) { Debug.Assert(node != null); switch (node.Kind()) { // ObjectCreationExpression for primitive types, such as "new int()", are treated as constants and allowed in attribute arguments. case SyntaxKind.ObjectCreationExpression: { var objectCreation = (ObjectCreationExpressionSyntax)node; if (objectCreation.Initializer == null) { var unusedDiagnostics = DiagnosticBag.GetInstance(); var type = typeBinder.BindType(objectCreation.Type, unusedDiagnostics).Type; unusedDiagnostics.Free(); var kind = TypedConstant.GetTypedConstantKind(type, typeBinder.Compilation); switch (kind) { case TypedConstantKind.Primitive: case TypedConstantKind.Enum: switch (type.TypeKind) { case TypeKind.Struct: case TypeKind.Class: case TypeKind.Enum: return(true); default: return(false); } } } return(false); } // sizeof(int) case SyntaxKind.SizeOfExpression: // typeof(int) case SyntaxKind.TypeOfExpression: // constant expressions // SPEC: Section 7.19: Only the following constructs are permitted in constant expressions: // Literals (including the null literal). case SyntaxKind.NumericLiteralExpression: case SyntaxKind.StringLiteralExpression: case SyntaxKind.CharacterLiteralExpression: case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: case SyntaxKind.NullLiteralExpression: // References to const members of class and struct types. // References to members of enumeration types. case SyntaxKind.IdentifierName: case SyntaxKind.GenericName: case SyntaxKind.AliasQualifiedName: case SyntaxKind.QualifiedName: case SyntaxKind.PredefinedType: case SyntaxKind.SimpleMemberAccessExpression: // References to const parameters or local variables. Not valid for attribute arguments, so skipped here. // Parenthesized sub-expressions, which are themselves constant expressions. case SyntaxKind.ParenthesizedExpression: // Cast expressions, provided the target type is one of the types listed above. case SyntaxKind.CastExpression: // checked and unchecked expressions case SyntaxKind.UncheckedExpression: case SyntaxKind.CheckedExpression: // Default value expressions case SyntaxKind.DefaultExpression: // The predefined +, –, !, and ~ unary operators. case SyntaxKind.UnaryPlusExpression: case SyntaxKind.UnaryMinusExpression: case SyntaxKind.LogicalNotExpression: case SyntaxKind.BitwiseNotExpression: // The predefined +, –, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators, provided each operand is of a type listed above. case SyntaxKind.AddExpression: case SyntaxKind.MultiplyExpression: case SyntaxKind.SubtractExpression: case SyntaxKind.DivideExpression: case SyntaxKind.ModuloExpression: case SyntaxKind.LeftShiftExpression: case SyntaxKind.RightShiftExpression: case SyntaxKind.BitwiseAndExpression: case SyntaxKind.BitwiseOrExpression: case SyntaxKind.ExclusiveOrExpression: case SyntaxKind.LogicalAndExpression: case SyntaxKind.LogicalOrExpression: case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: case SyntaxKind.GreaterThanExpression: case SyntaxKind.LessThanExpression: case SyntaxKind.GreaterThanOrEqualExpression: case SyntaxKind.LessThanOrEqualExpression: case SyntaxKind.InvocationExpression: // To support nameof(); anything else will be a compile-time error case SyntaxKind.ConditionalExpression: // The ?: conditional operator. return(true); default: return(false); } }
/// <summary> /// Gets the typed constant kind for the given attribute parameter type. /// </summary> /// <param name="type">Type to validated</param> /// <param name="compilation">compilation</param> /// <returns>TypedConstantKind for the attribute parameter type.</returns> public static TypedConstantKind GetAttributeParameterTypedConstantKind(this TypeSymbol type, CSharpCompilation compilation) { // Spec (17.1.3) // The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are: // 1) One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort. // 2) The type object. // 3) The type System.Type. // 4) An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility. // 5) Single-dimensional arrays of the above types. // A constructor argument or public field which does not have one of these types, cannot be used as a positional // or named parameter in an attribute specification. TypedConstantKind kind = TypedConstantKind.Error; if ((object)type == null) { return(TypedConstantKind.Error); } if (type.Kind == SymbolKind.ArrayType) { var arrayType = (ArrayTypeSymbol)type; if (arrayType.Rank != 1) { return(TypedConstantKind.Error); } kind = TypedConstantKind.Array; type = arrayType.ElementType; } // enum or enum[] if (type.IsEnumType()) { // SPEC VIOLATION: Dev11 doesn't enforce either the Enum type or its enclosing types (if any) to have public accessibility. // We will be consistent with Dev11 behavior. if (kind == TypedConstantKind.Error) { // set only if kind is not already set (i.e. its not an array of enum) kind = TypedConstantKind.Enum; } type = type.GetEnumUnderlyingType(); } var typedConstantKind = TypedConstant.GetTypedConstantKind(type, compilation); switch (typedConstantKind) { case TypedConstantKind.Array: case TypedConstantKind.Enum: case TypedConstantKind.Error: return(TypedConstantKind.Error); default: if (kind == TypedConstantKind.Array || kind == TypedConstantKind.Enum) { // Array/Enum type with valid element/underlying type return(kind); } return(typedConstantKind); } }