|
// 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: { } });
}
}
}
|