// Copyright (C) 2026 SharpEmu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using SharpEmu.HLE; namespace SharpEmu.SourceGenerators.Tests; /// /// Builds in-memory compilations for driving the generator and analyzer: the test-host /// runtime assemblies plus the real SharpEmu.HLE (for SysAbiExportAttribute, /// ExportedFunction, and friends), so generated output is compiled against the exact /// types it will target in the emulator. /// internal static class RoslynTestHost { private static readonly Lazy> References = new(static () => { var references = new List(); var trustedAssemblies = (string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!; foreach (var path in trustedAssemblies.Split(Path.PathSeparator)) { if (path.Length != 0) { references.Add(MetadataReference.CreateFromFile(path)); } } references.Add(MetadataReference.CreateFromFile(typeof(SysAbiExportAttribute).Assembly.Location)); return references; }); public static CSharpCompilation Compile(params string[] sources) { var trees = new SyntaxTree[sources.Length]; for (var index = 0; index < sources.Length; index++) { trees[index] = CSharpSyntaxTree.ParseText( sources[index], CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest)); } return CSharpCompilation.Create( "SysAbiGeneratorTests", trees, References.Value, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); } public static (Compilation Updated, string GeneratedSource) RunGenerator(CSharpCompilation compilation) { var driver = CSharpGeneratorDriver.Create(new SysAbiExportGenerator()); driver.RunGeneratorsAndUpdateCompilation(compilation, out var updated, out _); var generated = string.Empty; foreach (var tree in updated.SyntaxTrees) { if (tree.FilePath.EndsWith("SysAbiExportRegistry.g.cs", StringComparison.Ordinal)) { generated = tree.ToString(); } } return (updated, generated); } public static IReadOnlyList RunAnalyzer( CSharpCompilation compilation, params AdditionalText[] additionalFiles) { var withAnalyzers = compilation.WithAnalyzers( [new SysAbiExportAnalyzer()], new AnalyzerOptions([.. additionalFiles])); var diagnostics = withAnalyzers.GetAnalyzerDiagnosticsAsync().GetAwaiter().GetResult(); var result = new List(diagnostics.Length); foreach (var diagnostic in diagnostics) { result.Add(diagnostic); } return result; } public static void AssertCompiles(Compilation compilation) { var errors = new List(); foreach (var diagnostic in compilation.GetDiagnostics()) { if (diagnostic.Severity == DiagnosticSeverity.Error) { errors.Add(diagnostic.ToString()); } } Xunit.Assert.True(errors.Count == 0, string.Join(Environment.NewLine, errors)); } } internal sealed class InMemoryAdditionalText(string path, string content) : AdditionalText { public override string Path { get; } = path; public override SourceText GetText(CancellationToken cancellationToken = default) => SourceText.From(content); }