File: SyntaxExtensions.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj (Microsoft.Interop.SourceGeneration)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
 
namespace Microsoft.Interop
{
    public static class SyntaxExtensions
    {
        private static FixedStatementSyntax AddStatementWithoutEmptyStatements(this FixedStatementSyntax fixedStatement, StatementSyntax childStatement)
        {
            if (fixedStatement.Statement.IsKind(SyntaxKind.EmptyStatement))
            {
                return fixedStatement.WithStatement(childStatement);
            }
 
            BlockSyntax block;
            if (fixedStatement.Statement.IsKind(SyntaxKind.Block))
            {
                block = (BlockSyntax)fixedStatement.Statement;
                if (block.Statements.Count == 0)
                {
                    return fixedStatement.WithStatement(childStatement);
                }
            }
            else
            {
                block = SyntaxFactory.Block(fixedStatement.Statement);
            }
 
            if (childStatement.IsKind(SyntaxKind.Block))
            {
                block = block.WithStatements(block.Statements.AddRange(((BlockSyntax)childStatement).Statements));
            }
            else
            {
                block = block.AddStatements(childStatement);
            }
 
            return fixedStatement.WithStatement(block);
        }
 
        public static StatementSyntax NestFixedStatements(this ImmutableArray<FixedStatementSyntax> fixedStatements, StatementSyntax innerStatement)
        {
            StatementSyntax nestedStatement = innerStatement;
            if (!fixedStatements.IsEmpty)
            {
                int i = fixedStatements.Length - 1;
                nestedStatement = fixedStatements[i].AddStatementWithoutEmptyStatements(WrapStatementInBlock(nestedStatement));
                i--;
                for (; i >= 0; i--)
                {
                    nestedStatement = fixedStatements[i].AddStatementWithoutEmptyStatements(nestedStatement);
                }
            }
            return nestedStatement;
 
            static StatementSyntax WrapStatementInBlock(StatementSyntax statement)
            {
                if (statement.IsKind(SyntaxKind.Block))
                {
                    return statement;
                }
                return SyntaxFactory.Block(statement);
            }
        }
 
        public static SyntaxTokenList StripTriviaFromTokens(this SyntaxTokenList tokenList)
        {
            SyntaxToken[] strippedTokens = new SyntaxToken[tokenList.Count];
            for (int i = 0; i < tokenList.Count; i++)
            {
                strippedTokens[i] = tokenList[i].WithoutTrivia();
            }
            return new SyntaxTokenList(strippedTokens);
        }
 
        public static SyntaxTokenList StripAccessibilityModifiers(this SyntaxTokenList tokenList)
        {
            List<SyntaxToken> strippedTokens = new();
            for (int i = 0; i < tokenList.Count; i++)
            {
                if (tokenList[i].Kind() is SyntaxKind.PublicKeyword or SyntaxKind.InternalKeyword or SyntaxKind.ProtectedKeyword or SyntaxKind.PrivateKeyword)
                {
                    continue;
                }
                strippedTokens.Add(tokenList[i]);
            }
            return new SyntaxTokenList(strippedTokens);
        }
 
        public static SyntaxTokenList AddToModifiers(this SyntaxTokenList modifiers, SyntaxKind modifierToAdd)
        {
            if (modifiers.IndexOf(modifierToAdd) >= 0)
            {
                return modifiers;
            }
 
            // https://github.com/dotnet/csharplang/blob/main/meetings/2018/LDM-2018-04-04.md#ordering-of-ref-and-partial-keywords
            int idxPartial = modifiers.IndexOf(SyntaxKind.PartialKeyword);
            int idxRef = modifiers.IndexOf(SyntaxKind.RefKeyword);
 
            int idxInsert = (idxPartial, idxRef) switch
            {
                (-1, -1) => modifiers.Count,
                (-1, _) => idxRef,
                (_, -1) => idxPartial,
                (_, _) => Math.Min(idxPartial, idxRef)
            };
 
            return modifiers.Insert(idxInsert, SyntaxFactory.Token(modifierToAdd));
        }
 
        public static bool IsInPartialContext(this TypeDeclarationSyntax syntax, [NotNullWhen(false)] out SyntaxToken? nonPartialIdentifier)
        {
            for (SyntaxNode? parentNode = syntax; parentNode is TypeDeclarationSyntax typeDecl; parentNode = parentNode.Parent)
            {
                if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
                {
                    nonPartialIdentifier = typeDecl.Identifier;
                    return false;
                }
            }
            nonPartialIdentifier = null;
            return true;
        }
    }
}