File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\Services\SyntaxFacts\ISyntaxFactsExtensions.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.LanguageService;
 
internal static class ISyntaxFactsExtensions
{
    private static readonly ObjectPool<Stack<(SyntaxNodeOrToken nodeOrToken, bool leading, bool trailing)>> s_stackPool
        = SharedPools.Default<Stack<(SyntaxNodeOrToken nodeOrToken, bool leading, bool trailing)>>();
 
    public static bool IsMemberInitializerNamedAssignmentIdentifier(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => syntaxFacts.IsMemberInitializerNamedAssignmentIdentifier(node, out _);
 
    public static bool IsOnSingleLine(this ISyntaxFacts syntaxFacts, SyntaxNode node, bool fullSpan)
    {
        // The stack logic assumes the initial node is not null
        Contract.ThrowIfNull(node);
 
        // Use an actual Stack so we can write out deeply recursive structures without overflowing.
        // Note: algorithm is taken from GreenNode.WriteTo.
        //
        // General approach is that we recurse down the nodes, using a real stack object to
        // keep track of what node we're on.  If full-span is true we'll examine all tokens
        // and all the trivia on each token.  If full-span is false we'll examine all tokens
        // but we'll ignore the leading trivia on the very first trivia and the trailing trivia
        // on the very last token.
        using var _ = s_stackPool.GetPooledObject(out var stack);
        stack.Push((node, leading: fullSpan, trailing: fullSpan));
 
        var result = IsOnSingleLine(syntaxFacts, stack);
 
        return result;
    }
 
    private static bool IsOnSingleLine(
        ISyntaxFacts syntaxFacts, Stack<(SyntaxNodeOrToken nodeOrToken, bool leading, bool trailing)> stack)
    {
        while (stack.TryPop(out var tuple))
        {
            var (currentNodeOrToken, currentLeading, currentTrailing) = tuple;
            if (currentNodeOrToken.IsToken)
            {
                // If this token isn't on a single line, then the original node definitely
                // isn't on a single line.
                if (!IsOnSingleLine(syntaxFacts, currentNodeOrToken.AsToken(), currentLeading, currentTrailing))
                    return false;
            }
            else
            {
                var currentNode = currentNodeOrToken.AsNode()!;
 
                var childNodesAndTokens = currentNode.ChildNodesAndTokens();
                var childCount = childNodesAndTokens.Count;
 
                // Walk the children of this node in reverse, putting on the stack to process.
                // This way we process the children in the actual child-order they are in for
                // this node.
                var index = 0;
                foreach (var child in childNodesAndTokens.Reverse())
                {
                    // Since we're walking the children in reverse, if we're on hte 0th item,
                    // that's the last child.
                    var last = index == 0;
 
                    // Once we get all the way to the end of the reversed list, we're actually
                    // on the first.
                    var first = index == childCount - 1;
 
                    // We want the leading trivia if we've asked for it, or if we're not the first
                    // token being processed.  We want the trailing trivia if we've asked for it,
                    // or if we're not the last token being processed.
                    stack.Push((child, currentLeading | !first, currentTrailing | !last));
                    index++;
                }
            }
        }
 
        // All tokens were on a single line.  This node is on a single line.
        return true;
    }
 
    private static bool IsOnSingleLine(ISyntaxFacts syntaxFacts, SyntaxToken token, bool leading, bool trailing)
    {
        // If any of our trivia is not on a single line, then we're not on a single line.
        if (!IsOnSingleLine(syntaxFacts, token.LeadingTrivia, leading) ||
            !IsOnSingleLine(syntaxFacts, token.TrailingTrivia, trailing))
        {
            return false;
        }
 
        // Only string literals can span multiple lines.  Only need to check those.
        if (syntaxFacts.SyntaxKinds.StringLiteralToken == token.RawKind ||
            syntaxFacts.SyntaxKinds.InterpolatedStringTextToken == token.RawKind)
        {
            // This allocated.  But we only do it in the string case. For all other tokens
            // we don't need any allocations.
            if (!IsOnSingleLine(token.ToString()))
            {
                return false;
            }
        }
 
        // Any other type of token is on a single line.
        return true;
    }
 
    private static bool IsOnSingleLine(ISyntaxFacts syntaxFacts, SyntaxTriviaList triviaList, bool checkTrivia)
    {
        if (checkTrivia)
        {
            foreach (var trivia in triviaList)
            {
                if (trivia.HasStructure)
                {
                    // For structured trivia, we recurse into the trivia to see if it
                    // is on a single line or not.  If it isn't, then we're definitely
                    // not on a single line.
                    if (!IsOnSingleLine(syntaxFacts, trivia.GetStructure()!, fullSpan: true))
                    {
                        return false;
                    }
                }
                else if (syntaxFacts.IsEndOfLineTrivia(trivia))
                {
                    // Contained an end-of-line trivia.  Definitely not on a single line.
                    return false;
                }
                else if (!syntaxFacts.IsWhitespaceTrivia(trivia))
                {
                    // Was some other form of trivia (like a comment).  Easiest thing
                    // to do is just stringify this and count the number of newlines.
                    // these should be rare.  So the allocation here is ok.
                    if (!IsOnSingleLine(trivia.ToString()))
                    {
                        return false;
                    }
                }
            }
        }
 
        return true;
    }
 
    private static bool IsOnSingleLine(string value)
        => value.GetNumberOfLineBreaks() == 0;
 
    public static bool ContainsInterleavedDirective(
        this ISyntaxFacts syntaxFacts, ImmutableArray<SyntaxNode> nodes, CancellationToken cancellationToken)
    {
        if (nodes.Length > 0)
        {
            var span = TextSpan.FromBounds(nodes.First().Span.Start, nodes.Last().Span.End);
 
            foreach (var node in nodes)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (ContainsInterleavedDirective(syntaxFacts, span, node, cancellationToken))
                    return true;
            }
        }
 
        return false;
    }
 
    public static bool ContainsInterleavedDirective(this ISyntaxFacts syntaxFacts, SyntaxNode node, CancellationToken cancellationToken)
        => ContainsInterleavedDirective(syntaxFacts, node.Span, node, cancellationToken);
 
    public static bool ContainsInterleavedDirective(
        this ISyntaxFacts syntaxFacts, TextSpan span, SyntaxNode node, CancellationToken cancellationToken)
    {
        foreach (var token in node.DescendantTokens())
        {
            if (syntaxFacts.ContainsInterleavedDirective(span, token, cancellationToken))
                return true;
        }
 
        return false;
    }
 
    public static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, IEnumerable<SyntaxNode> nodes)
    {
        if (nodes == null || nodes.IsEmpty())
        {
            return false;
        }
 
        return SpansPreprocessorDirective(syntaxFacts, nodes.SelectMany(n => n.DescendantTokens()));
    }
 
    /// <summary>
    /// Determines if there is preprocessor trivia *between* any of the <paramref name="tokens"/>
    /// provided.  The <paramref name="tokens"/> will be deduped and then ordered by position.
    /// Specifically, the first token will not have it's leading trivia checked, and the last
    /// token will not have it's trailing trivia checked.  All other trivia will be checked to
    /// see if it contains a preprocessor directive.
    /// </summary>
    public static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, IEnumerable<SyntaxToken> tokens)
    {
        // we want to check all leading trivia of all tokens (except the 
        // first one), and all trailing trivia of all tokens (except the
        // last one).
 
        var first = true;
        var previousToken = default(SyntaxToken);
 
        // Allow duplicate nodes/tokens to be passed in.  Also, allow the nodes/tokens
        // to not be in any particular order when passed in.
        var orderedTokens = tokens.Distinct().OrderBy(t => t.SpanStart);
 
        foreach (var token in orderedTokens)
        {
            if (first)
            {
                first = false;
            }
            else
            {
                // check the leading trivia of this token, and the trailing trivia
                // of the previous token.
                if (SpansPreprocessorDirective(syntaxFacts, token.LeadingTrivia) ||
                    SpansPreprocessorDirective(syntaxFacts, previousToken.TrailingTrivia))
                {
                    return true;
                }
            }
 
            previousToken = token;
        }
 
        return false;
    }
 
    private static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxTriviaList list)
        => list.Any(syntaxFacts.IsPreprocessorDirective);
 
    public static bool IsLegalIdentifier(this ISyntaxFacts syntaxFacts, string name)
    {
        if (name.Length == 0)
        {
            return false;
        }
 
        if (!syntaxFacts.IsIdentifierStartCharacter(name[0]))
        {
            return false;
        }
 
        for (var i = 1; i < name.Length; i++)
        {
            if (!syntaxFacts.IsIdentifierPartCharacter(name[i]))
            {
                return false;
            }
        }
 
        return true;
    }
 
    public static bool IsReservedOrContextualKeyword(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => syntaxFacts.IsReservedKeyword(token) || syntaxFacts.IsContextualKeyword(token);
 
    public static bool IsWord(this ISyntaxFacts syntaxFacts, SyntaxToken token)
    {
        return syntaxFacts.IsIdentifier(token)
            || syntaxFacts.IsReservedOrContextualKeyword(token)
            || syntaxFacts.IsPreprocessorKeyword(token);
    }
 
    public static bool IsRegularOrDocumentationComment(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => syntaxFacts.IsRegularComment(trivia) || syntaxFacts.IsDocumentationComment(trivia);
 
    [return: NotNullIfNotNull(nameof(node))]
    public static SyntaxNode? WalkDownParentheses(this ISyntaxFacts syntaxFacts, SyntaxNode? node)
    {
        while (syntaxFacts.IsParenthesizedExpression(node))
        {
            syntaxFacts.GetPartsOfParenthesizedExpression(node, out _, out var child, out _);
            node = child;
        }
 
        return node;
    }
 
    [return: NotNullIfNotNull(nameof(node))]
    public static SyntaxNode? WalkUpParentheses(this ISyntaxFacts syntaxFacts, SyntaxNode? node)
    {
        while (syntaxFacts.IsParenthesizedExpression(node?.Parent))
            node = node.Parent;
 
        return node;
    }
 
    public static void GetPartsOfAssignmentStatement(
        this ISyntaxFacts syntaxFacts, SyntaxNode statement,
        out SyntaxNode left, out SyntaxNode right)
    {
        syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out _, out right);
    }
 
    public static SyntaxNode GetExpressionOfInvocationExpression(
        this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfInvocationExpression(node, out var expression, out _);
        return expression;
    }
 
    public static SyntaxNode Unparenthesize(
        this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        SyntaxToken openParenToken;
        SyntaxNode operand;
        SyntaxToken closeParenToken;
 
        if (syntaxFacts.IsParenthesizedPattern(node))
        {
            syntaxFacts.GetPartsOfParenthesizedPattern(node,
                out openParenToken, out operand, out closeParenToken);
        }
        else
        {
            syntaxFacts.GetPartsOfParenthesizedExpression(node,
                out openParenToken, out operand, out closeParenToken);
        }
 
        var leadingTrivia = openParenToken.LeadingTrivia
            .Concat(openParenToken.TrailingTrivia)
            .Where(t => !syntaxFacts.IsElastic(t))
            .Concat(operand.GetLeadingTrivia());
 
        var trailingTrivia = operand.GetTrailingTrivia()
            .Concat(closeParenToken.LeadingTrivia)
            .Where(t => !syntaxFacts.IsElastic(t))
            .Concat(closeParenToken.TrailingTrivia);
 
        var resultNode = operand
            .WithLeadingTrivia(leadingTrivia)
            .WithTrailingTrivia(trailingTrivia);
 
        // If there's no trivia between the original node and the tokens around it, then add
        // elastic markers so the formatting engine will spaces if necessary to keep things
        // parseable.
        if (resultNode.GetLeadingTrivia().Count == 0)
        {
            var previousToken = node.GetFirstToken().GetPreviousToken();
            if (previousToken.TrailingTrivia.Count == 0 &&
                syntaxFacts.IsWordOrNumber(previousToken) &&
                syntaxFacts.IsWordOrNumber(resultNode.GetFirstToken()))
            {
                resultNode = resultNode.WithPrependedLeadingTrivia(syntaxFacts.ElasticMarker);
            }
        }
 
        if (resultNode.GetTrailingTrivia().Count == 0)
        {
            var nextToken = node.GetLastToken().GetNextToken();
            if (nextToken.LeadingTrivia.Count == 0 &&
                syntaxFacts.IsWordOrNumber(nextToken) &&
                syntaxFacts.IsWordOrNumber(resultNode.GetLastToken()))
            {
                resultNode = resultNode.WithAppendedTrailingTrivia(syntaxFacts.ElasticMarker);
            }
        }
 
        return resultNode;
    }
 
    private static bool IsWordOrNumber(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => syntaxFacts.IsWord(token) || syntaxFacts.IsNumericLiteral(token);
 
    public static bool SpansPreprocessorDirective(this ISyntaxFacts service, SyntaxNode node)
        => service.SpansPreprocessorDirective([node]);
 
    public static bool SpansPreprocessorDirective(this ISyntaxFacts service, params SyntaxNode[] nodes)
        => service.SpansPreprocessorDirective((IEnumerable<SyntaxNode>)nodes);
 
    public static bool IsWhitespaceOrEndOfLineTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => syntaxFacts.IsWhitespaceTrivia(trivia) || syntaxFacts.IsEndOfLineTrivia(trivia);
 
    public static void GetPartsOfBinaryExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node, out SyntaxNode left, out SyntaxNode right)
        => syntaxFacts.GetPartsOfBinaryExpression(node, out left, out _, out right);
 
    public static SyntaxNode GetPatternOfParenthesizedPattern(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfParenthesizedPattern(node, out _, out var pattern, out _);
        return pattern;
    }
 
    public static SyntaxToken GetOperatorTokenOfBinaryExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfBinaryExpression(node, out _, out var token, out _);
        return token;
    }
 
    public static bool IsAnonymousOrLocalFunction(this ISyntaxFacts syntaxFacts, SyntaxNode node)
        => syntaxFacts.IsAnonymousFunctionExpression(node) ||
           syntaxFacts.IsLocalFunctionStatement(node);
 
    public static SyntaxNode? GetExpressionOfElementAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfElementAccessExpression(node, out var expression, out _);
        return expression;
    }
 
    public static SyntaxNode? GetArgumentListOfElementAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfElementAccessExpression(node, out _, out var argumentList);
        return argumentList;
    }
 
    public static SyntaxNode GetExpressionOfConditionalAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfConditionalAccessExpression(node, out var expression, out _);
        return expression;
    }
 
    public static SyntaxToken GetOperatorTokenOfMemberAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfMemberAccessExpression(node, out _, out var operatorToken, out _);
        return operatorToken;
    }
 
    public static void GetPartsOfMemberAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node, out SyntaxNode expression, out SyntaxNode name)
        => syntaxFacts.GetPartsOfMemberAccessExpression(node, out expression, out _, out name);
 
    public static void GetPartsOfConditionalAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node, out SyntaxNode expression, out SyntaxNode whenNotNull)
        => syntaxFacts.GetPartsOfConditionalAccessExpression(node, out expression, out _, out whenNotNull);
 
    public static TextSpan GetSpanWithoutAttributes(this ISyntaxFacts syntaxFacts, SyntaxNode root, SyntaxNode node)
    {
        // Span without AttributeLists
        // - No AttributeLists -> original .Span
        // - Some AttributeLists -> (first non-trivia/comment Token.Span.Begin, original.Span.End)
        //   - We need to be mindful about comments due to:
        //      // [Test1]
        //      //Comment1
        //      [||]object Property1 { get; set; }
        //     the comment node being part of the next token's (`object`) leading trivia and not the AttributeList's node.
        // - In case only attribute is written we need to be careful to not to use next (unrelated) token as beginning current the node.
        var attributeList = syntaxFacts.GetAttributeLists(node);
        if (attributeList.Any())
        {
            var endOfAttributeLists = attributeList.Last().Span.End;
            var afterAttributesToken = root.FindTokenOnRightOfPosition(endOfAttributeLists);
 
            var endOfNode = node.Span.End;
            var startOfNodeWithoutAttributes = Math.Min(afterAttributesToken.Span.Start, endOfNode);
 
            return TextSpan.FromBounds(startOfNodeWithoutAttributes, endOfNode);
        }
 
        return node.Span;
    }
 
    /// <summary>
    /// Similar to <see cref="ISyntaxFacts.GetStandaloneExpression(SyntaxNode)"/>, this gets the containing
    /// expression that is actually a language expression and not just typed as an ExpressionSyntax for convenience.
    /// However, this goes beyond that that method in that if this expression is the RHS of a conditional access
    /// (i.e. <c>a?.b()</c>) it will also return the root of the conditional access expression tree.
    /// <para/> The intuition here is that this will give the topmost expression node that could realistically be
    /// replaced with any other expression.  For example, with <c>a?.b()</c> technically <c>.b()</c> is an
    /// expression.  But that cannot be replaced with something like <c>(1 + 1)</c> (as <c>a?.(1 + 1)</c> is not
    /// legal).  However, in <c>a?.b()</c>, then <c>a</c> itself could be replaced with <c>(1 + 1)?.b()</c> to form
    /// a legal expression.
    /// </summary>
    public static SyntaxNode GetRootStandaloneExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        // First, make sure we're on a construct the language things is a standalone expression.
        var standalone = syntaxFacts.GetStandaloneExpression(node);
 
        // Then, if this is the RHS of a `?`, walk up to the top of that tree to get the final standalone expression.
        return syntaxFacts.GetRootConditionalAccessExpression(standalone) ?? standalone;
    }
 
    #region GetXXXOfYYY Members
 
    public static SyntaxNode? GetArgumentListOfInvocationExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfInvocationExpression(node, out _, out var argumentList);
        return argumentList;
    }
 
    public static SeparatedSyntaxList<SyntaxNode> GetArgumentsOfInvocationExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        var argumentList = syntaxFacts.GetArgumentListOfInvocationExpression(node);
        return argumentList is null ? default : syntaxFacts.GetArgumentsOfArgumentList(argumentList);
    }
 
    public static SyntaxNode? GetArgumentListOfBaseObjectCreationExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfBaseObjectCreationExpression(node, out var argumentList, out _);
        return argumentList;
    }
 
    public static SyntaxNode? GetDefaultOfParameter(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfParameter(node, out _, out var @default);
        return @default;
    }
 
    public static SyntaxNode GetExpressionOfParenthesizedExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfParenthesizedExpression(node, out _, out var expression, out _);
        return expression;
    }
 
    public static SyntaxToken GetIdentifierOfGenericName(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfGenericName(node, out var identifier, out _);
        return identifier;
    }
 
    public static SyntaxToken GetIdentifierOfIdentifierName(this ISyntaxFacts syntaxFacts, SyntaxNode node)
        => syntaxFacts.GetIdentifierOfSimpleName(node);
 
    public static SyntaxToken GetIdentifierOfParameter(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfParameter(node, out var identifier, out _);
        return identifier;
    }
 
    public static SyntaxList<SyntaxNode> GetImportsOfBaseNamespaceDeclaration(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfBaseNamespaceDeclaration(node, out _, out var imports, out _);
        return imports;
    }
 
    public static SyntaxList<SyntaxNode> GetImportsOfCompilationUnit(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfCompilationUnit(node, out var imports, out _, out _);
        return imports;
    }
 
    public static SyntaxNode? GetInitializerOfBaseObjectCreationExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfBaseObjectCreationExpression(node, out _, out var initializer);
        return initializer;
    }
 
    public static SyntaxList<SyntaxNode> GetMembersOfBaseNamespaceDeclaration(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfBaseNamespaceDeclaration(node, out _, out _, out var members);
        return members;
    }
 
    public static SyntaxList<SyntaxNode> GetMembersOfCompilationUnit(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfCompilationUnit(node, out _, out _, out var members);
        return members;
    }
 
    public static SyntaxNode GetNameOfAttribute(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfAttribute(node, out var name, out _);
        return name;
    }
 
    public static SyntaxNode GetNameOfBaseNamespaceDeclaration(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfBaseNamespaceDeclaration(node, out var name, out _, out _);
        return name;
    }
 
    public static SyntaxNode GetNameOfMemberAccessExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfMemberAccessExpression(node, out _, out var name);
        return name;
    }
 
    public static SyntaxNode GetOperandOfPrefixUnaryExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfPrefixUnaryExpression(node, out _, out var operand);
        return operand;
    }
 
    public static SyntaxToken GetOperatorTokenOfPrefixUnaryExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfPrefixUnaryExpression(node, out var operatorToken, out _);
        return operatorToken;
    }
 
    public static SeparatedSyntaxList<SyntaxNode> GetTypeArgumentsOfGenericName(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfGenericName(node, out _, out var typeArguments);
        return typeArguments;
    }
 
    public static SyntaxNode GetTypeOfObjectCreationExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node)
    {
        syntaxFacts.GetPartsOfObjectCreationExpression(node, out _, out var type, out _, out _);
        return type;
    }
 
    #endregion
 
    #region IsXXXOfYYY members
 
    public static bool IsExpressionOfAwaitExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
    {
        var parent = node?.Parent;
        if (!syntaxFacts.IsAwaitExpression(parent))
            return false;
 
        return node == syntaxFacts.GetExpressionOfAwaitExpression(parent);
    }
 
    public static bool IsExpressionOfInvocationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
    {
        var parent = node?.Parent;
        if (!syntaxFacts.IsInvocationExpression(parent))
            return false;
 
        syntaxFacts.GetPartsOfInvocationExpression(parent, out var expression, out _);
        return node == expression;
    }
 
    public static bool IsExpressionOfMemberAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
    {
        var parent = node?.Parent;
        if (!syntaxFacts.IsMemberAccessExpression(parent))
            return false;
 
        syntaxFacts.GetPartsOfMemberAccessExpression(parent, out var expression, out _);
        return node == expression;
    }
 
    public static bool IsNameOfAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
    {
        if (!syntaxFacts.IsAttribute(node?.Parent))
            return false;
 
        syntaxFacts.GetPartsOfAttribute(node.Parent, out var name, out _);
        return name == node;
    }
 
    public static bool IsRightOfQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
    {
        var parent = node?.Parent;
        if (!syntaxFacts.IsQualifiedName(parent))
            return false;
 
        syntaxFacts.GetPartsOfQualifiedName(parent, out _, out _, out var right);
        return node == right;
    }
 
    public static bool IsTypeOfObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
    {
        var parent = node?.Parent;
        if (!syntaxFacts.IsObjectCreationExpression(parent))
            return false;
 
        syntaxFacts.GetPartsOfObjectCreationExpression(parent, out _, out var type, out _, out _);
        return type == node;
    }
 
    #endregion
 
    #region ISyntaxKinds forwarding methods
 
    #region trivia
 
    public static bool IsEndOfLineTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.EndOfLineTrivia;
 
    public static bool IsMultiLineCommentTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.MultiLineCommentTrivia;
 
    public static bool IsMultiLineDocCommentTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.MultiLineDocCommentTrivia;
 
    public static bool IsShebangDirectiveTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.ShebangDirectiveTrivia;
 
    public static bool IsSingleLineCommentTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.SingleLineCommentTrivia;
 
    public static bool IsSingleLineDocCommentTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.SingleLineDocCommentTrivia;
 
    public static bool IsWhitespaceTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivia trivia)
        => trivia.RawKind == syntaxFacts.SyntaxKinds.WhitespaceTrivia;
 
    public static bool IsSkippedTokensTrivia(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.SkippedTokensTrivia;
 
    #endregion
 
    #region keywords
 
    public static bool IsAwaitKeyword(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.AwaitKeyword;
 
    public static bool IsGlobalNamespaceKeyword(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.GlobalKeyword;
 
    #endregion
 
    #region literal tokens
 
    public static bool IsCharacterLiteral(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.CharacterLiteralToken;
 
    public static bool IsStringLiteral(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.StringLiteralToken;
 
    #endregion
 
    #region tokens
 
    public static bool IsIdentifier(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.IdentifierToken;
 
    public static bool IsHashToken(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.HashToken;
 
    public static bool IsInterpolatedStringTextToken(this ISyntaxFacts syntaxFacts, SyntaxToken token)
        => token.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringTextToken;
 
    #endregion
 
    #region names
 
    public static bool IsGenericName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.GenericName;
 
    public static bool IsIdentifierName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IdentifierName;
 
    public static bool IsQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.QualifiedName;
 
    #endregion
 
    #region types
 
    public static bool IsTupleType(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.TupleType;
 
    #endregion
 
    #region literal expressions
 
    public static bool IsCharacterLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.CharacterLiteralExpression;
 
    public static bool IsDefaultLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.DefaultLiteralExpression;
 
    public static bool IsFalseLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.FalseLiteralExpression;
 
    public static bool IsNumericLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.NumericLiteralExpression;
 
    public static bool IsNullLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.NullLiteralExpression;
 
    public static bool IsStringLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.StringLiteralExpression;
 
    public static bool IsTrueLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.TrueLiteralExpression;
 
    #endregion
 
    #region expressions
 
    public static bool IsArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ArrayCreationExpression;
 
    public static bool IsAwaitExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.AwaitExpression;
 
    public static bool IsBaseExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.BaseExpression;
 
    public static bool IsConditionalExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalExpression;
 
    public static bool IsConditionalAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalAccessExpression;
 
    public static bool IsImplicitArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitArrayCreationExpression;
 
    public static bool IsImplicitObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitObjectCreationExpression;
 
    public static bool IsIndexExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IndexExpression;
 
    public static bool IsInterpolatedStringExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringExpression;
 
    public static bool IsInterpolation(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.Interpolation;
 
    public static bool IsInterpolatedStringText(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringText;
 
    public static bool IsInvocationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.InvocationExpression;
 
    public static bool IsIsTypeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IsTypeExpression;
 
    public static bool IsIsNotTypeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IsNotTypeExpression;
 
    public static bool IsIsPatternExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IsPatternExpression;
 
    public static bool IsLogicalAndExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalAndExpression;
 
    public static bool IsLogicalOrExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalOrExpression;
 
    public static bool IsLogicalNotExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalNotExpression;
 
    public static bool IsObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ObjectCreationExpression;
 
    public static bool IsParenthesizedExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedExpression;
 
    public static bool IsQueryExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.QueryExpression;
 
    public static bool IsRangeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.RangeExpression;
 
    public static bool IsRefExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.RefExpression;
 
    public static bool IsSimpleMemberAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.SimpleMemberAccessExpression;
 
    public static bool IsThisExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ThisExpression;
 
    public static bool IsThrowExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThrowExpression;
 
    public static bool IsTupleExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.TupleExpression;
 
    public static bool ContainsGlobalStatement(this ISyntaxFacts syntaxFacts, SyntaxNode node)
        => node.ChildNodes().Any(c => c.RawKind == syntaxFacts.SyntaxKinds.GlobalStatement);
 
    #endregion
 
    #region pattern
 
    public static bool IsAndPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.AndPattern;
 
    public static bool IsConstantPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ConstantPattern;
 
    public static bool IsDeclarationPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.DeclarationPattern;
 
    public static bool IsListPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ListPattern;
 
    public static bool IsNotPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.NotPattern;
 
    public static bool IsOrPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.OrPattern;
 
    public static bool IsParenthesizedPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedPattern;
 
    public static bool IsRecursivePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.RecursivePattern;
 
    public static bool IsRelationalPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.RelationalPattern;
 
    public static bool IsTypePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.TypePattern;
 
    public static bool IsVarPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.VarPattern;
 
    #endregion
 
    #region statements
 
    public static bool IsExpressionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ExpressionStatement;
 
    public static bool IsForEachStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ForEachStatement;
 
    public static bool IsForStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ForStatement;
 
    public static bool IsIfStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IfStatement;
 
    public static bool IsLocalDeclarationStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.LocalDeclarationStatement;
 
    public static bool IsLocalFunctionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LocalFunctionStatement;
 
    public static bool IsLockStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.LockStatement;
 
    public static bool IsReturnStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ReturnStatement;
 
    public static bool IsThrowStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ThrowStatement;
 
    public static bool IsUsingStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.UsingStatement;
 
    public static bool IsWhileStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.WhileStatement;
 
    public static bool IsYieldReturnStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.YieldReturnStatement;
 
    #endregion
 
    #region members/declarations
 
    public static bool IsAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.Attribute;
 
    public static bool IsClassDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ClassDeclaration;
 
    public static bool IsConstructorDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ConstructorDeclaration;
 
    public static bool IsEnumDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.EnumDeclaration;
 
    public static bool IsGlobalAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => syntaxFacts.IsGlobalAssemblyAttribute(node) || syntaxFacts.IsGlobalModuleAttribute(node);
 
    public static bool IsInterfaceDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.InterfaceDeclaration;
 
    public static bool IsParameter(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.Parameter;
 
    public static bool IsTypeConstraint(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.TypeConstraint;
 
    public static bool IsVariableDeclarator(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.VariableDeclarator;
 
    public static bool IsFieldDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.FieldDeclaration;
 
    public static bool IsPropertyDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.PropertyDeclaration;
 
    public static bool IsStructDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.StructDeclaration;
 
    public static bool IsTypeArgumentList(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.TypeArgumentList;
 
    #endregion
 
    #region clauses
 
    public static bool IsElseClause(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ElseClause;
    public static bool IsEqualsValueClause(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.EqualsValueClause;
 
    #endregion
 
    #region other
 
    public static bool IsImplicitElementAccess(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitElementAccess;
 
    public static bool IsIndexerMemberCref(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
        => node?.RawKind == syntaxFacts.SyntaxKinds.IndexerMemberCref;
 
    #endregion
 
    #endregion
}