File: Binder\EarlyWellKnownAttributeBinder.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// This is a special binder used for decoding some special well-known attributes very early in the attribute binding phase.
    /// It only binds those attribute argument syntax which can produce valid attribute arguments, but doesn't report any diagnostics.
    /// Subsequent binding phase will rebind such erroneous attributes and generate appropriate diagnostics.
    /// </summary>
    internal sealed class EarlyWellKnownAttributeBinder : Binder
    {
        internal EarlyWellKnownAttributeBinder(Binder enclosing)
            : base(enclosing, enclosing.Flags | BinderFlags.EarlyAttributeBinding)
        {
        }
 
        internal (CSharpAttributeData, BoundAttribute) GetAttribute(
            AttributeSyntax node, NamedTypeSymbol boundAttributeType,
            Action<AttributeSyntax> beforeAttributePartBound,
            Action<AttributeSyntax> afterAttributePartBound,
            out bool generatedDiagnostics)
        {
            var dummyDiagnosticBag = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
            var result = base.GetAttribute(node, boundAttributeType, beforeAttributePartBound, afterAttributePartBound, dummyDiagnosticBag);
            generatedDiagnostics = !dummyDiagnosticBag.DiagnosticBag.IsEmptyWithoutResolution;
            dummyDiagnosticBag.Free();
            return result;
        }
 
        // Hide the GetAttribute overload which takes a diagnostic bag.
        // This ensures that diagnostics from the early bound attributes are never preserved.
        [Obsolete("EarlyWellKnownAttributeBinder has a better overload - GetAttribute(AttributeSyntax, NamedTypeSymbol, out bool)", true)]
        internal new (CSharpAttributeData, BoundAttribute) GetAttribute(
            AttributeSyntax node, NamedTypeSymbol boundAttributeType,
            Action<AttributeSyntax> beforeAttributePartBound,
            Action<AttributeSyntax> afterAttributePartBound,
            BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(false, "Don't call this overload.");
            diagnostics.Add(ErrorCode.ERR_InternalError, node.Location);
            return base.GetAttribute(node, boundAttributeType, beforeAttributePartBound, afterAttributePartBound, diagnostics);
        }
 
        /// <remarks>
        /// Since this method is expected to be called on every nested expression of the argument, it doesn't
        /// need to recurse (directly).
        /// </remarks>
        internal static bool CanBeValidAttributeArgument(ExpressionSyntax node)
        {
            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:
                case SyntaxKind.ImplicitObjectCreationExpression:
                    {
                        var objectCreation = (BaseObjectCreationExpressionSyntax)node;
                        return objectCreation.Initializer == null && (objectCreation.ArgumentList?.Arguments.Count ?? 0) == 0;
                    }
 
                // 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.Utf8StringLiteralExpression:
                case SyntaxKind.InterpolatedStringExpression:
                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.UnsignedRightShiftExpression:
                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;
            }
        }
    }
}