File: Syntax\SyntaxFacts.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.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    public static partial class SyntaxFacts
    {
        /// <summary>
        /// Returns true if the node is the alias of an AliasQualifiedNameSyntax
        /// </summary>
        public static bool IsAliasQualifier(SyntaxNode node)
        {
            var p = node.Parent as AliasQualifiedNameSyntax;
            return p != null && p.Alias == node;
        }
 
        public static bool IsAttributeName(SyntaxNode node)
        {
            var parent = node.Parent;
            if (parent == null || !IsName(node.Kind()))
            {
                return false;
            }
 
            switch (parent.Kind())
            {
                case QualifiedName:
                    var qn = (QualifiedNameSyntax)parent;
                    return qn.Right == node ? IsAttributeName(parent) : false;
 
                case AliasQualifiedName:
                    var an = (AliasQualifiedNameSyntax)parent;
                    return an.Name == node ? IsAttributeName(parent) : false;
            }
 
            var p = node.Parent as AttributeSyntax;
            return p != null && p.Name == node;
        }
 
        /// <summary>
        /// Returns true if the node is the object of an invocation expression.
        /// </summary>
        public static bool IsInvoked(ExpressionSyntax node)
        {
            node = (ExpressionSyntax)SyntaxFactory.GetStandaloneExpression(node);
            var inv = node.Parent as InvocationExpressionSyntax;
            return inv != null && inv.Expression == node;
        }
 
        /// <summary>
        /// Returns true if the node is the object of an element access expression.
        /// </summary>
        public static bool IsIndexed(ExpressionSyntax node)
        {
            node = (ExpressionSyntax)SyntaxFactory.GetStandaloneExpression(node);
            var indexer = node.Parent as ElementAccessExpressionSyntax;
            return indexer != null && indexer.Expression == node;
        }
 
        public static bool IsNamespaceAliasQualifier(ExpressionSyntax node)
        {
            var parent = node.Parent as AliasQualifiedNameSyntax;
            return parent != null && parent.Alias == node;
        }
 
        /// <summary>
        /// Returns true if the node is in a tree location that is expected to be a type
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static bool IsInTypeOnlyContext(ExpressionSyntax node)
        {
            node = SyntaxFactory.GetStandaloneExpression(node);
            var parent = node.Parent;
            if (parent != null)
            {
                switch (parent.Kind())
                {
                    case Attribute:
                        return ((AttributeSyntax)parent).Name == node;
 
                    case ArrayType:
                        return ((ArrayTypeSyntax)parent).ElementType == node;
 
                    case PointerType:
                        return ((PointerTypeSyntax)parent).ElementType == node;
 
                    case FunctionPointerType:
                        // FunctionPointerTypeSyntax has no direct children that are ExpressionSyntaxes
                        throw ExceptionUtilities.Unreachable();
 
                    case PredefinedType:
                        return true;
 
                    case NullableType:
                        return ((NullableTypeSyntax)parent).ElementType == node;
 
                    case TypeArgumentList:
                        // all children of GenericNames are type arguments
                        return true;
 
                    case CastExpression:
                        return ((CastExpressionSyntax)parent).Type == node;
 
                    case ObjectCreationExpression:
                        return ((ObjectCreationExpressionSyntax)parent).Type == node;
 
                    case StackAllocArrayCreationExpression:
                        return ((StackAllocArrayCreationExpressionSyntax)parent).Type == node;
 
                    case FromClause:
                        return ((FromClauseSyntax)parent).Type == node;
 
                    case JoinClause:
                        return ((JoinClauseSyntax)parent).Type == node;
 
                    case VariableDeclaration:
                        return ((VariableDeclarationSyntax)parent).Type == node;
 
                    case ForEachStatement:
                        return ((ForEachStatementSyntax)parent).Type == node;
 
                    case CatchDeclaration:
                        return ((CatchDeclarationSyntax)parent).Type == node;
 
                    case AsExpression:
                    case IsExpression:
                        return ((BinaryExpressionSyntax)parent).Right == node;
 
                    case TypeOfExpression:
                        return ((TypeOfExpressionSyntax)parent).Type == node;
 
                    case SizeOfExpression:
                        return ((SizeOfExpressionSyntax)parent).Type == node;
 
                    case DefaultExpression:
                        return ((DefaultExpressionSyntax)parent).Type == node;
 
                    case RefValueExpression:
                        return ((RefValueExpressionSyntax)parent).Type == node;
 
                    case RefType:
                        return ((RefTypeSyntax)parent).Type == node;
 
                    case ScopedType:
                        return ((ScopedTypeSyntax)parent).Type == node;
 
                    case Parameter:
                    case FunctionPointerParameter:
                        return ((BaseParameterSyntax)parent).Type == node;
 
                    case TypeConstraint:
                        return ((TypeConstraintSyntax)parent).Type == node;
 
                    case MethodDeclaration:
                        return ((MethodDeclarationSyntax)parent).ReturnType == node;
 
                    case IndexerDeclaration:
                        return ((IndexerDeclarationSyntax)parent).Type == node;
 
                    case OperatorDeclaration:
                        return ((OperatorDeclarationSyntax)parent).ReturnType == node;
 
                    case ConversionOperatorDeclaration:
                        return ((ConversionOperatorDeclarationSyntax)parent).Type == node;
 
                    case PropertyDeclaration:
                        return ((PropertyDeclarationSyntax)parent).Type == node;
 
                    case DelegateDeclaration:
                        return ((DelegateDeclarationSyntax)parent).ReturnType == node;
 
                    case EventDeclaration:
                        return ((EventDeclarationSyntax)parent).Type == node;
 
                    case LocalFunctionStatement:
                        return ((LocalFunctionStatementSyntax)parent).ReturnType == node;
 
                    case ParenthesizedLambdaExpression:
                        return ((ParenthesizedLambdaExpressionSyntax)parent).ReturnType == node;
 
                    case SimpleBaseType:
                        return true;
 
                    case PrimaryConstructorBaseType:
                        return ((PrimaryConstructorBaseTypeSyntax)parent).Type == node;
 
                    case CrefParameter:
                        return true;
 
                    case ConversionOperatorMemberCref:
                        return ((ConversionOperatorMemberCrefSyntax)parent).Type == node;
 
                    case ExplicitInterfaceSpecifier:
                        // #13.4.1 An explicit member implementation is a method, property, event or indexer
                        // declaration that references a fully qualified interface member name.
                        // A ExplicitInterfaceSpecifier represents the left part (QN) of the member name, so it
                        // should be treated like a QualifiedName.
                        return ((ExplicitInterfaceSpecifierSyntax)parent).Name == node;
 
                    case DeclarationPattern:
                        return ((DeclarationPatternSyntax)parent).Type == node;
 
                    case RecursivePattern:
                        return ((RecursivePatternSyntax)parent).Type == node;
 
                    case TupleElement:
                        return ((TupleElementSyntax)parent).Type == node;
 
                    case DeclarationExpression:
                        return ((DeclarationExpressionSyntax)parent).Type == node;
 
                    case IncompleteMember:
                        return ((IncompleteMemberSyntax)parent).Type == node;
 
                    case TypePattern:
                        return ((TypePatternSyntax)parent).Type == node;
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Returns true if a node is in a tree location that is expected to be either a namespace or type
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static bool IsInNamespaceOrTypeContext(ExpressionSyntax? node)
        {
            if (node != null)
            {
                node = SyntaxFactory.GetStandaloneExpression(node);
                var parent = node.Parent;
                if (parent != null)
                {
                    switch (parent.Kind())
                    {
                        case UsingDirective:
                            return ((UsingDirectiveSyntax)parent).NamespaceOrType == node;
 
                        case QualifiedName:
                            // left of QN is namespace or type.  Note: when you have "a.b.c()", then
                            // "a.b" is not a qualified name, it is a member access expression.
                            // Qualified names are only parsed when the parser knows it's a type only
                            // context.
                            return ((QualifiedNameSyntax)parent).Left == node;
 
                        default:
                            return IsInTypeOnlyContext(node);
                    }
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Is the node the name of a named argument of an invocation, object creation expression, 
        /// constructor initializer, or element access, but not an attribute.
        /// </summary>
        public static bool IsNamedArgumentName(SyntaxNode node)
        {
            // An argument name is an IdentifierName inside a NameColon, inside an Argument, inside an ArgumentList, inside an
            // Invocation, ObjectCreation, ObjectInitializer, ElementAccess or Subpattern.
 
            if (!node.IsKind(IdentifierName))
            {
                return false;
            }
 
            var parent1 = node.Parent;
            if (parent1 == null || !parent1.IsKind(NameColon))
            {
                return false;
            }
 
            var parent2 = parent1.Parent;
            if (parent2.IsKind(SyntaxKind.Subpattern))
            {
                return true;
            }
 
            if (parent2 == null || !(parent2.IsKind(Argument) || parent2.IsKind(AttributeArgument)))
            {
                return false;
            }
 
            var parent3 = parent2.Parent;
            if (parent3 == null)
            {
                return false;
            }
 
            if (parent3.IsKind(SyntaxKind.TupleExpression))
            {
                return true;
            }
 
            if (!(parent3 is BaseArgumentListSyntax || parent3.IsKind(AttributeArgumentList)))
            {
                return false;
            }
 
            var parent4 = parent3.Parent;
            if (parent4 == null)
            {
                return false;
            }
 
            switch (parent4.Kind())
            {
                case InvocationExpression:
                case TupleExpression:
                case ObjectCreationExpression:
                case ImplicitObjectCreationExpression:
                case ObjectInitializerExpression:
                case ElementAccessExpression:
                case Attribute:
                case BaseConstructorInitializer:
                case ThisConstructorInitializer:
                case PrimaryConstructorBaseType:
                    return true;
                default:
                    return false;
            }
        }
 
        /// <summary>
        /// Is the expression the initializer in a fixed statement?
        /// </summary>
        public static bool IsFixedStatementExpression(SyntaxNode node)
        {
            var current = node.Parent;
            // Dig through parens because dev10 does (even though the spec doesn't say so)
            // Dig through casts because there's a special error code (CS0254) for such casts.
            while (current != null && (current.IsKind(ParenthesizedExpression) || current.IsKind(CastExpression))) current = current.Parent;
            if (current == null || !current.IsKind(EqualsValueClause)) return false;
            current = current.Parent;
            if (current == null || !current.IsKind(VariableDeclarator)) return false;
            current = current.Parent;
            if (current == null || !current.IsKind(VariableDeclaration)) return false;
            current = current.Parent;
            return current != null && current.IsKind(FixedStatement);
        }
 
        public static string GetText(Accessibility accessibility)
        {
            switch (accessibility)
            {
                case Accessibility.NotApplicable:
                    return string.Empty;
                case Accessibility.Private:
                    return SyntaxFacts.GetText(PrivateKeyword);
                case Accessibility.ProtectedAndInternal:
                    return SyntaxFacts.GetText(PrivateKeyword) + " " + SyntaxFacts.GetText(ProtectedKeyword);
                case Accessibility.Internal:
                    return SyntaxFacts.GetText(InternalKeyword);
                case Accessibility.Protected:
                    return SyntaxFacts.GetText(ProtectedKeyword);
                case Accessibility.ProtectedOrInternal:
                    return SyntaxFacts.GetText(ProtectedKeyword) + " " + SyntaxFacts.GetText(InternalKeyword);
                case Accessibility.Public:
                    return SyntaxFacts.GetText(PublicKeyword);
                default:
                    throw ExceptionUtilities.UnexpectedValue(accessibility);
            }
        }
 
        internal static bool IsStatementExpression(SyntaxNode syntax)
        {
            // The grammar gives:
            //
            // expression-statement:
            //     statement-expression ;
            //
            // statement-expression:
            //     invocation-expression
            //     object-creation-expression
            //     assignment
            //     post-increment-expression
            //     post-decrement-expression
            //     pre-increment-expression
            //     pre-decrement-expression
            //     await-expression
 
            switch (syntax.Kind())
            {
                case InvocationExpression:
                case ObjectCreationExpression:
                case SimpleAssignmentExpression:
                case AddAssignmentExpression:
                case SubtractAssignmentExpression:
                case MultiplyAssignmentExpression:
                case DivideAssignmentExpression:
                case ModuloAssignmentExpression:
                case AndAssignmentExpression:
                case OrAssignmentExpression:
                case ExclusiveOrAssignmentExpression:
                case LeftShiftAssignmentExpression:
                case RightShiftAssignmentExpression:
                case UnsignedRightShiftAssignmentExpression:
                case CoalesceAssignmentExpression:
                case PostIncrementExpression:
                case PostDecrementExpression:
                case PreIncrementExpression:
                case PreDecrementExpression:
                case AwaitExpression:
                    return true;
 
                case ConditionalAccessExpression:
                    var access = (ConditionalAccessExpressionSyntax)syntax;
                    return IsStatementExpression(access.WhenNotNull);
 
                // Allow missing IdentifierNames; they will show up in error cases
                // where there is no statement whatsoever.
 
                case IdentifierName:
                    return syntax.IsMissing;
 
                default:
                    return false;
            }
        }
 
        [System.Obsolete("IsLambdaBody API is obsolete", true)]
        public static bool IsLambdaBody(SyntaxNode node)
        {
            return LambdaUtilities.IsLambdaBody(node);
        }
 
        internal static bool IsIdentifierVar(this Syntax.InternalSyntax.SyntaxToken node)
        {
            return node.ContextualKind == SyntaxKind.VarKeyword;
        }
 
        internal static bool IsIdentifierVarOrPredefinedType(this Syntax.InternalSyntax.SyntaxToken node)
        {
            return node.IsIdentifierVar() || IsPredefinedType(node.Kind);
        }
 
        internal static bool IsDeclarationExpressionType(SyntaxNode node, [NotNullWhen(true)] out DeclarationExpressionSyntax? parent)
        {
            parent = node.ModifyingScopedOrRefTypeOrSelf().Parent as DeclarationExpressionSyntax;
            return node == parent?.Type.SkipScoped(out _).SkipRef();
        }
 
        /// <summary>
        /// Given an initializer expression infer the name of anonymous property or tuple element.
        /// Returns null if unsuccessful
        /// </summary>
        public static string? TryGetInferredMemberName(this SyntaxNode syntax)
        {
            SyntaxToken nameToken;
            switch (syntax.Kind())
            {
                case SyntaxKind.SingleVariableDesignation:
                    nameToken = ((SingleVariableDesignationSyntax)syntax).Identifier;
                    break;
 
                case SyntaxKind.DeclarationExpression:
                    var declaration = (DeclarationExpressionSyntax)syntax;
                    var designationKind = declaration.Designation.Kind();
                    if (designationKind == SyntaxKind.ParenthesizedVariableDesignation ||
                        designationKind == SyntaxKind.DiscardDesignation)
                    {
                        return null;
                    }
 
                    nameToken = ((SingleVariableDesignationSyntax)declaration.Designation).Identifier;
                    break;
 
                case SyntaxKind.ParenthesizedVariableDesignation:
                case SyntaxKind.DiscardDesignation:
                    return null;
 
                default:
                    if (syntax is ExpressionSyntax expr)
                    {
                        nameToken = expr.ExtractAnonymousTypeMemberName();
                        break;
                    }
                    return null;
            }
 
            return nameToken.RawKind != 0 ? nameToken.ValueText : null;
        }
 
        /// <summary>
        /// Checks whether the element name is reserved.
        ///
        /// For example:
        /// "Item3" is reserved (at certain positions).
        /// "Rest", "ToString" and other members of System.ValueTuple are reserved (in any position).
        /// Names that are not reserved return false.
        /// </summary>
        public static bool IsReservedTupleElementName(string elementName)
        {
            return NamedTypeSymbol.IsTupleElementNameReserved(elementName) != -1;
        }
 
        internal static bool HasAnyBody(this BaseMethodDeclarationSyntax declaration)
        {
            return (declaration.Body ?? (SyntaxNode?)declaration.ExpressionBody) != null;
        }
 
        internal static bool IsExpressionBodied(this BaseMethodDeclarationSyntax declaration)
        {
            return declaration.Body == null && declaration.ExpressionBody != null;
        }
 
        internal static bool IsVarArg(this BaseMethodDeclarationSyntax declaration)
        {
            return IsVarArg(declaration.ParameterList);
        }
 
        internal static bool IsVarArg(this ParameterListSyntax parameterList)
        {
            return parameterList.Parameters.Any(static p => p.IsArgList);
        }
 
        internal static bool IsTopLevelStatement([NotNullWhen(true)] GlobalStatementSyntax? syntax)
        {
            return syntax?.Parent?.IsKind(SyntaxKind.CompilationUnit) == true;
        }
 
        internal static bool IsSimpleProgramTopLevelStatement(GlobalStatementSyntax? syntax)
        {
            return IsTopLevelStatement(syntax) && syntax.SyntaxTree.Options.Kind == SourceCodeKind.Regular;
        }
 
        internal static bool HasAwaitOperations(SyntaxNode node)
        {
            // Do not descend into functions
            return node.DescendantNodesAndSelf(child => !IsNestedFunction(child)).Any(
                            node =>
                            {
                                switch (node)
                                {
                                    case AwaitExpressionSyntax _:
                                    case LocalDeclarationStatementSyntax local when local.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword):
                                    case CommonForEachStatementSyntax @foreach when @foreach.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword):
                                    case UsingStatementSyntax @using when @using.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword):
                                        return true;
                                    default:
                                        return false;
                                }
                            });
        }
 
        private static bool IsNestedFunction(SyntaxNode child)
            => IsNestedFunction(child.Kind());
 
        private static bool IsNestedFunction(SyntaxKind kind)
            => kind is SyntaxKind.LocalFunctionStatement
                or SyntaxKind.AnonymousMethodExpression
                or SyntaxKind.SimpleLambdaExpression
                or SyntaxKind.ParenthesizedLambdaExpression;
 
        [PerformanceSensitive("https://github.com/dotnet/roslyn/pull/66970", Constraint = "Use Green nodes for walking to avoid heavy allocations.")]
        internal static bool HasYieldOperations(SyntaxNode? node)
        {
            if (node is null)
                return false;
 
            var stack = ArrayBuilder<GreenNode>.GetInstance();
            stack.Push(node.Green);
 
            while (stack.Count > 0)
            {
                var current = stack.Pop();
                Debug.Assert(node.Green == current || current is not Syntax.InternalSyntax.MemberDeclarationSyntax and not Syntax.InternalSyntax.TypeDeclarationSyntax);
 
                if (current is null)
                    continue;
 
                // Do not descend into functions and expressions
                if (IsNestedFunction((SyntaxKind)current.RawKind) ||
                    current is Syntax.InternalSyntax.ExpressionSyntax)
                {
                    continue;
                }
 
                if (current is Syntax.InternalSyntax.YieldStatementSyntax)
                {
                    stack.Free();
                    return true;
                }
 
                foreach (var child in current.ChildNodesAndTokens())
                {
                    if (!child.IsToken)
                        stack.Push(child);
                }
            }
 
            stack.Free();
            return false;
        }
 
        internal static bool HasReturnWithExpression(SyntaxNode? node)
        {
            // Do not descend into functions and expressions
            return node is object &&
                   node.DescendantNodesAndSelf(child => !IsNestedFunction(child) && !(node is ExpressionSyntax)).Any(n => n is ReturnStatementSyntax { Expression: { } });
        }
    }
}