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);
            }
        }
Example #2
0
        /// <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);
            }
        }