diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 5bb191410..53887e822 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -651,6 +651,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task SealedRecordToString([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task Issue3610([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SealedRecordToString.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SealedRecordToString.cs new file mode 100644 index 000000000..0e4e21ff1 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SealedRecordToString.cs @@ -0,0 +1,119 @@ +using System.Text; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class SealedRecordToString + { + public abstract record AbstractBase(int X); + + public record Middle(int X, int Y) : AbstractBase(X) + { + public sealed override string ToString() + { + return "middle"; + } + } + + public record Leaf(int X, int Y, int Z) : Middle(X, Y); + + public record WithSealedToString(int A) + { + public sealed override string ToString() + { + return "custom"; + } + } + + public record DerivedAddsMembers(int A, string B) : WithSealedToString(A); + + public record DerivedAddsNoMembers(int A) : WithSealedToString(A); + + public record GenericDerived(int A, T Extra) : WithSealedToString(A); + + public record WithNonSealedToString(int A) + { + public override string ToString() + { + return "custom"; + } + } + + public record DerivedFromNonSealed(int A, string B) : WithNonSealedToString(A); + + public record WithSealedToStringAndPrintMembers(int A) + { + protected virtual bool PrintMembers(StringBuilder builder) + { + builder.Append("CustomA = "); + builder.Append(A); + return true; + } + + public sealed override string ToString() + { + return "custom"; + } + } + + public record EquivalentPrintMembers(int A) + { + // A user-written PrintMembers whose body is IL-equivalent to the + // compiler-generated one is folded into the synthesized record members + // and therefore not printed. Recompilation regenerates an identical + // PrintMembers, so semantics are preserved. +#if !EXPECTED_OUTPUT + protected virtual bool PrintMembers(StringBuilder builder) + { + builder.Append("A = "); + builder.Append(A); + return true; + } +#endif + + public sealed override string ToString() + { + return "custom"; + } + } + + public record MimicsGeneratedToString(int A) + { + public sealed override string ToString() + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("MimicsGeneratedToString"); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(' '); + } + stringBuilder.Append('}'); + return stringBuilder.ToString(); + } + } + + public sealed record SealedRecordWithSealedToString(int A) + { + public sealed override string ToString() + { + return "sealed"; + } + } + + public record GenericWithSealedToString(T Value) + { + public sealed override string ToString() + { + return typeof(T).Name; + } + } + + public record struct StructWithToString(int A) + { + public override string ToString() + { + return "structcustom"; + } + } + } +}