diff --git a/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs b/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs index 41a281d6..c703825a 100644 --- a/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs +++ b/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs @@ -141,6 +141,12 @@ TempCollections tempCollections builder.AppendLine("// "); builder.AppendLine("#nullable enable"); + builder.AppendLine( + "#pragma warning disable CS0612, CS0618 // Wrappers reference user-selected obsolete types and members" + ); + builder.AppendLine( + "#pragma warning disable CS1591 // XML documentation is not emitted for generated members" + ); builder.AppendLine("#pragma warning disable CS0162 // Unreachable code"); builder.AppendLine( "#pragma warning disable CS0219 // Variable assigned but never used" @@ -249,6 +255,12 @@ TempCollections tempCollections builder.EndBlock(); } + builder.AppendLine( + "#pragma warning restore CS0612, CS0618 // Wrappers reference user-selected obsolete types and members" + ); + builder.AppendLine( + "#pragma warning restore CS1591 // XML documentation is not emitted for generated members" + ); builder.AppendLine("#pragma warning restore CS0162 // Unreachable code"); builder.AppendLine( "#pragma warning restore CS0219 // Variable assigned but never used" diff --git a/tests/Lua.SourceGenerator.Tests/LuaObjectGeneratorTests.cs b/tests/Lua.SourceGenerator.Tests/LuaObjectGeneratorTests.cs index 2a1084c9..bee67c48 100644 --- a/tests/Lua.SourceGenerator.Tests/LuaObjectGeneratorTests.cs +++ b/tests/Lua.SourceGenerator.Tests/LuaObjectGeneratorTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using Lua.SourceGenerator; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -6,6 +7,192 @@ namespace Lua.SourceGenerator.Tests; public class LuaObjectGeneratorTests { + [TestCase("class DocumentedObject", false)] + [TestCase("class DocumentedObject", true)] + [TestCase("class DocumentedObject", false)] + [TestCase("class DocumentedObject", true)] + [TestCase("record DocumentedObject", false)] + [TestCase("record DocumentedObject", true)] + public void GeneratedMembers_DoNotRequireXmlDocumentation( + string declaration, + bool warningsAsErrors + ) + { + var typeParameterDocumentation = declaration.Contains("") + ? "/// The type argument." + : ""; + var result = CompileGeneratedSource( + $$""" + using Lua; + + /// A documented Lua object. + {{typeParameterDocumentation}} + [LuaObject] + public partial {{declaration}} + { + /// Gets or sets the value. + [LuaMember] + public int Value { get; set; } + } + """, + warningsAsErrors + ); + + Assert.That(result.Success, Is.True, string.Join("\n", result.Diagnostics)); + Assert.That(result.Diagnostics, Is.Empty); + } + + [TestCase("Obsolete")] + [TestCase("Obsolete(\"Use the replacement member.\")")] + public void GeneratedWrappers_CanReferenceUserSelectedObsoleteMembers(string attribute) + { + var result = CompileGeneratedSource( + $$""" + using System; + using Lua; + + [LuaObject] + internal partial class LegacyObject + { + [LuaMember, {{attribute}}] + public int Field; + + [LuaMember, {{attribute}}] + public int Property { get; set; } + + [LuaMember, {{attribute}}] + public void Method() { } + + [LuaMetamethod(LuaObjectMetamethod.Add), {{attribute}}] + public static int Add(LegacyObject left, LegacyObject right) => 1; + } + """ + ); + + Assert.That(result.Success, Is.True, string.Join("\n", result.Diagnostics)); + Assert.That(result.Diagnostics, Is.Empty); + } + + [TestCase(false)] + [TestCase(true)] + public void GeneratedSuppressions_DoNotHideDiagnosticsInUserCode(bool warningsAsErrors) + { + var result = CompileGeneratedSource( + """ + using System; + using Lua; + + /// A Lua object with diagnostics in user-written code. + [LuaObject] + public partial class UserObject + { + [LuaMember] + public int Undocumented { get; set; } + + /// A legacy member without a message. + [LuaMember, Obsolete] + public void LegacyWithoutMessage() { } + + /// A legacy member with a message. + [LuaMember, Obsolete("Use the replacement member.")] + public void LegacyWithMessage() { } + + /// Calls the legacy members from user-written code. + public void CallLegacyMembers() + { + LegacyWithoutMessage(); + LegacyWithMessage(); + } + } + """, + warningsAsErrors + ); + + Assert.That( + result.Diagnostics.Select(x => x.Id), + Is.EquivalentTo(new[] { "CS1591", "CS0612", "CS0618" }) + ); + Assert.That( + result.Diagnostics.All(x => x.Location.SourceTree == result.UserSource), + Is.True + ); + Assert.That( + result.Diagnostics.All(x => + x.Severity + == (warningsAsErrors ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning) + ), + Is.True + ); + Assert.That(result.Success, Is.EqualTo(!warningsAsErrors)); + } + + [Test] + public void GeneratedSuppressions_DoNotHideObsoleteErrors() + { + var result = CompileGeneratedSource( + """ + using System; + using Lua; + + [LuaObject] + internal partial class UnsupportedObject + { + [LuaMember, Obsolete("This API must not be used.", true)] + public void Method() { } + } + """ + ); + + Assert.That(result.Success, Is.False); + Assert.That( + result.Diagnostics, + Has.Some.Matches(x => + x.Id == "CS0619" && x.Severity == DiagnosticSeverity.Error + ) + ); + } + + static ( + bool Success, + ImmutableArray Diagnostics, + SyntaxTree UserSource + ) CompileGeneratedSource(string source, bool warningsAsErrors = true) + { + var parseOptions = CSharpParseOptions + .Default.WithLanguageVersion(LanguageVersion.Preview) + .WithDocumentationMode(DocumentationMode.Diagnose); + var syntaxTree = CSharpSyntaxTree.ParseText(source, parseOptions, "UserObject.cs"); + var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!) + .Split(Path.PathSeparator) + .Select(path => MetadataReference.CreateFromFile(path)) + .Append(MetadataReference.CreateFromFile(typeof(LuaValue).Assembly.Location)) + .Append(MetadataReference.CreateFromFile(typeof(LuaObjectAttribute).Assembly.Location)); + var compilation = CSharpCompilation.Create( + "GeneratedWarningConsumer", + [syntaxTree], + references, + new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary + ).WithGeneralDiagnosticOption( + warningsAsErrors ? ReportDiagnostic.Error : ReportDiagnostic.Default + ) + ); + GeneratorDriver driver = CSharpGeneratorDriver.Create( + [new LuaObjectGenerator().AsSourceGenerator()], + parseOptions: parseOptions + ); + driver = driver.RunGeneratorsAndUpdateCompilation( + compilation, + out var output, + out var diagnostics + ); + Assert.That(driver.GetRunResult().GeneratedTrees, Is.Not.Empty); + using var assembly = new MemoryStream(); + using var documentation = new MemoryStream(); + var result = output.Emit(assembly, xmlDocumentationStream: documentation); + return (result.Success, diagnostics.AddRange(result.Diagnostics), syntaxTree); + } + [Test] public void Test_AllowNullRejectsNonNullableValueTypes() {