File: CodeGeneration\AbstractCodeGenerationTests.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.EditorFeatures.UnitTests)
// 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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeGeneration;
 
[UseExportProvider]
public abstract class AbstractCodeGenerationTests
{
    private static SyntaxNode Simplify(
        AdhocWorkspace workspace,
        SyntaxNode syntaxNode,
        string languageName)
    {
        var projectId = ProjectId.CreateNewId();
 
        var project = workspace.CurrentSolution
            .AddProject(projectId, languageName, $"{languageName}.dll", languageName).GetRequiredProject(projectId);
 
        var normalizedSyntax = syntaxNode.NormalizeWhitespace().ToFullString();
        var document = project.AddMetadataReference(NetFramework.mscorlib)
            .AddDocument("Fake Document", SourceText.From(normalizedSyntax));
 
        var root = document.GetRequiredSyntaxRootAsync(default).AsTask().Result;
        var annotatedDocument = document.WithSyntaxRoot(
                root.WithAdditionalAnnotations(Simplifier.Annotation));
 
        var options = document.Project.Services.GetRequiredService<ISimplificationService>().DefaultOptions;
        var simplifiedDocument = Simplifier.ReduceAsync(annotatedDocument, options, CancellationToken.None).Result;
 
        var rootNode = simplifiedDocument.GetRequiredSyntaxRootAsync(default).AsTask().Result;
 
        return rootNode;
    }
 
    private static SyntaxNode WrapExpressionInBoilerplate(SyntaxNode expression, SyntaxGenerator codeDefFactory)
    {
        return codeDefFactory.CompilationUnit(
            codeDefFactory.NamespaceImportDeclaration(codeDefFactory.IdentifierName("System")),
            codeDefFactory.ClassDeclaration(
                "C",
                members:
                [
                    codeDefFactory.MethodDeclaration(
                        "Dummy",
                        returnType: null,
                        statements:
                        [
                            codeDefFactory.LocalDeclarationStatement("test", expression)
                        ])
                ])
            );
    }
 
    internal static void Test(
        Func<SyntaxGenerator, SyntaxNode> nodeCreator,
        string cs, string csSimple,
        string vb, string vbSimple)
    {
        Assert.True(cs != null || csSimple != null || vb != null || vbSimple != null,
            $"At least one of {nameof(cs)}, {nameof(csSimple)}, {nameof(vb)}, {nameof(vbSimple)} must be provided");
 
        using var workspace = new AdhocWorkspace();
 
        if (cs != null || csSimple != null)
        {
            var codeDefFactory = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService<SyntaxGenerator>();
 
            var node = nodeCreator(codeDefFactory);
            node = node.NormalizeWhitespace();
 
            if (cs != null)
            {
                TokenUtilities.AssertTokensEqual(cs, node.ToFullString(), LanguageNames.CSharp);
            }
 
            if (csSimple != null)
            {
                var simplifiedRootNode = Simplify(workspace, WrapExpressionInBoilerplate(node, codeDefFactory), LanguageNames.CSharp);
                var expression = simplifiedRootNode.DescendantNodes().OfType<EqualsValueClauseSyntax>().First().Value;
 
                TokenUtilities.AssertTokensEqual(csSimple, expression.NormalizeWhitespace().ToFullString(), LanguageNames.CSharp);
            }
        }
 
        if (vb != null || vbSimple != null)
        {
            var codeDefFactory = workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetRequiredService<SyntaxGenerator>();
 
            var node = nodeCreator(codeDefFactory);
            node = node.NormalizeWhitespace();
 
            if (vb != null)
            {
                TokenUtilities.AssertTokensEqual(vb, node.ToFullString(), LanguageNames.VisualBasic);
            }
 
            if (vbSimple != null)
            {
                var simplifiedRootNode = Simplify(workspace, WrapExpressionInBoilerplate(node, codeDefFactory), LanguageNames.VisualBasic);
                var expression = simplifiedRootNode.DescendantNodes().OfType<EqualsValueSyntax>().First().Value;
 
                TokenUtilities.AssertTokensEqual(vbSimple, expression.NormalizeWhitespace().ToFullString(), LanguageNames.VisualBasic);
            }
        }
    }
 
    protected static ITypeSymbol CreateClass(string name)
    {
        return CodeGenerationSymbolFactory.CreateNamedTypeSymbol(
            attributes: default, accessibility: default, modifiers: default, TypeKind.Class, name);
    }
}