From 182fb4e8145a1846ecb525c069c2f806fc526a46 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jun 2026 00:35:49 +0200 Subject: [PATCH] Compare PDB sequence points against the compiler's breakpoint map The PdbGen tests compared the reconstructed PDB to the C# compiler's byte-for-byte, so any non-trivial method failed on reconstructed IL ranges, hidden sequence points and local scopes - none of which the decompiler can reproduce exactly. That left four of seven fixtures [Ignore]d and the suite with almost no coverage. Compare only what a debugging user actually feels: the visible (non-hidden) breakpoint map, parsed straight from the sequence-point blobs and keyed by method-definition row (shared between the PDB and the PE it describes). IL offsets, hidden points, local scopes and the embedded source are dropped. The compiler's own PDB is the oracle, so the tests assert correct debugging behavior rather than the decompiler's past output. Methods where the decompiler legitimately diverges pin an auto-derived residual snapshot, the same accept-the-diff workflow as the pretty tests; a separate oracle-free check rejects duplicate or overlapping sequence points. Un-ignores ForLoopTests, LambdaCapturing and Members (its source is regenerated to match the decompiler's per-type output, collapsing ~50 lines of indentation-induced coordinate noise to two genuine differences). Assisted-by: Claude:claude-opus-4-8:Claude Code --- ICSharpCode.Decompiler.Tests/CLAUDE.md | 22 +- .../Helpers/PdbSequencePoints.cs | 198 ++++++++++++++++++ .../PdbGenerationTestRunner.cs | 148 ++++++------- .../TestCases/PdbGen/.gitignore | 1 + .../TestCases/PdbGen/ForLoopTests.xml | 41 ---- .../TestCases/PdbGen/HelloWorld.xml | 16 -- .../PdbGen/LambdaCapturing.residual.txt | 2 + .../TestCases/PdbGen/LambdaCapturing.xml | 38 ---- .../TestCases/PdbGen/Members.residual.txt | 6 + .../TestCases/PdbGen/Members.xml | 113 +++++----- 10 files changed, 356 insertions(+), 229 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/Helpers/PdbSequencePoints.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.residual.txt create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.residual.txt diff --git a/ICSharpCode.Decompiler.Tests/CLAUDE.md b/ICSharpCode.Decompiler.Tests/CLAUDE.md index d5655de3e..7b5f72d1c 100644 --- a/ICSharpCode.Decompiler.Tests/CLAUDE.md +++ b/ICSharpCode.Decompiler.Tests/CLAUDE.md @@ -42,7 +42,7 @@ root `CLAUDE.md` section on the submodule). | Ugly | `UglyTestRunner` / `Ugly/*.cs` | compile -> decompile with sugar settings disabled | sibling `.Expected.cs` file | | Disassembler | `DisassemblerPrettyTestRunner` / `Disassembler/Pretty/*.il` | ilasm -> disassemble with our `ReflectionDisassembler` | the `.il` source (or `.expected.il`, e.g. `SortedOutput`) | | VBPretty | `VBPrettyTestRunner` / `VBPretty/*.vb` | vbc -> decompile to C# | sibling `.cs` file | -| PdbGen | `PdbGenerationTestRunner` / `PdbGen/*.xml` | in-proc Roslyn compile, generate portable PDB with `PortablePdbWriter`, dump both PDBs with `PdbToXmlConverter` | `.expected.xml` | +| PdbGen | `PdbGenerationTestRunner` / `PdbGen/*.xml` | in-proc Roslyn compile (real PDB = oracle), decompile, generate portable PDB with `PortablePdbWriter`, parse both PDBs' sequence-point blobs | the compiler's PDB, projected to the visible breakpoint map (see below) | | Roundtrip | `RoundtripAssembly` (inputs from `ILSpy-tests/`) | whole-project decompile -> MSBuild rebuild -> run original NUnit tests against the rebuilt assembly | test-run success | | Unit tests | `TypeSystem/`, `Semantics/`, `Output/`, `Util/`, `DataFlowTest`, `Metadata/`, `ProjectDecompiler/` | plain in-process NUnit | assertions | @@ -69,8 +69,24 @@ helper, which picks the file via `[CallerMemberName]`. - **ILPretty / Disassembler**: add a `.il` file (assembled with the NuGet ilasm) and the expected `.cs` (`ILPretty`) or rely on round-tripping the `.il` itself (`Disassembler`). - **VBPretty**: add `MyTest.vb` and the expected C# decompilation `MyTest.cs`. -- **PdbGen**: add `MyTest.xml` containing the source files inside `` elements; - the expected PDB dump lives in `MyTest.expected.xml`. +- **PdbGen** (the reconstructed PDB's breakpoints match the C# compiler's): add `MyTest.xml` + containing the source inside `` elements, written exactly as ILSpy + pretty-prints a *single type* (one document per type, no assembly-attribute header - the same + discipline as Pretty). The runner compiles it with Roslyn (whose PDB is the oracle), decompiles, + reconstructs a PDB, and compares only the **visible breakpoint map**: per method, the ordered + source locations of the non-hidden sequence points. IL offsets, hidden sequence points, local + scopes and the embedded source are all dropped, because the decompiler reconstructs them + differently and they never match byte-for-byte. The comparer also runs an oracle-free + well-formedness check (strictly increasing IL offsets = no duplicate/overlapping points). + `TestSequencePoints()` asserts the map matches the compiler exactly; for the handful of methods + where the decompiler legitimately diverges (e.g. it breakpoints a method's opening brace where + the compiler keeps it hidden), call `TestSequencePoints(knownResidual: true)`; the residual is + auto-derived and committed as `MyTest.residual.txt`, so improvements and regressions both flip + the test like a pretty diff. On a mismatch the test writes `MyTest.residual.txt.generated` and + fails; accept a deliberate change by re-running with `ILSPY_ACCEPT_PDB_RESIDUAL=1` set (which + overwrites the snapshot in place) or by copying the `.generated` file over it. `Tolerance.Lines` drops column comparison + for statements whose column placement differs. Nothing is hand-maintained except the `.cs` + source and, rarely, the residual snapshot. No `.expected.*` is committed (all regenerated). ## Conditional expectations (#if) and comparison rules diff --git a/ICSharpCode.Decompiler.Tests/Helpers/PdbSequencePoints.cs b/ICSharpCode.Decompiler.Tests/Helpers/PdbSequencePoints.cs new file mode 100644 index 000000000..224281327 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/Helpers/PdbSequencePoints.cs @@ -0,0 +1,198 @@ +// Copyright (c) 2026 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Text; + +namespace ICSharpCode.Decompiler.Tests.Helpers +{ + /// + /// Reads the sequence points out of a portable PDB and compares two PDBs (typically the one + /// the original C# compiler produced versus the one PortablePdbWriter reconstructs) + /// at the granularity that actually matters to a debugging user: the breakpoint map. + /// + /// + /// The decompiler reconstructs IL ranges, hidden sequence points and local scopes from the + /// ILAst, so those never match the original compiler byte-for-byte. What it can and should + /// get right is where the visible (non-hidden) breakpoints land in the source. + /// The comparison therefore projects each PDB onto the ordered list of visible sequence-point + /// source locations per method (keyed by method-definition row, which is shared between the + /// PDB and the PE it describes) and reports the residual difference. + /// + static class PdbSequencePoints + { + internal readonly record struct RawSequencePoint(int Offset, bool IsHidden, + int StartLine, int StartColumn, int EndLine, int EndColumn) + { + /// Visible source location, formatted with or without columns. + public string Format(bool includeColumns) + { + return includeColumns + ? $"({StartLine},{StartColumn})-({EndLine},{EndColumn})" + : $"L{StartLine}-{EndLine}"; + } + } + + /// + /// Reads every method's sequence points (hidden ones included, in document order) from a + /// portable PDB, keyed by the row number of the owning method definition. + /// + public static Dictionary> Read(Stream portablePdb) + { + portablePdb.Position = 0; + using var provider = MetadataReaderProvider.FromPortablePdbStream(portablePdb, MetadataStreamOptions.LeaveOpen); + var reader = provider.GetMetadataReader(); + var result = new Dictionary>(); + foreach (var handle in reader.MethodDebugInformation) + { + var info = reader.GetMethodDebugInformation(handle); + if (info.SequencePointsBlob.IsNil) + continue; + var list = new List(); + foreach (var sp in info.GetSequencePoints()) + { + list.Add(new RawSequencePoint(sp.Offset, sp.IsHidden, + sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn)); + } + if (list.Count > 0) + { + // A MethodDebugInformation row is parallel to its MethodDefinition row. + result[MetadataTokens.GetRowNumber(handle)] = list; + } + } + return result; + } + + /// + /// Resolves a readable "Namespace.Type.Method" label for each method-definition row, + /// for use in comparison failure messages. + /// + public static Dictionary ReadMethodNames(string peFileName) + { + using var peStream = File.OpenRead(peFileName); + using var peReader = new PEReader(peStream); + var md = peReader.GetMetadataReader(); + var names = new Dictionary(); + foreach (var handle in md.MethodDefinitions) + { + var method = md.GetMethodDefinition(handle); + var type = md.GetTypeDefinition(method.GetDeclaringType()); + string typeName = md.GetString(type.Name); + if (!type.Namespace.IsNil && md.GetString(type.Namespace) is { Length: > 0 } ns) + typeName = ns + "." + typeName; + names[MetadataTokens.GetRowNumber(handle)] = typeName + "." + md.GetString(method.Name); + } + return names; + } + + /// + /// Compares the visible breakpoint maps of two PDBs and returns the residual difference as + /// a deterministic, human-readable report. An empty string means the maps are identical. + /// + /// PDB used as the oracle (the original compiler's PDB). + /// PDB under test (the decompiler's reconstruction). + /// Row-to-label map from . + /// + /// When true, breakpoints must match on line and column; when false, only the + /// line span is compared (use for methods where the decompiler's column placement within a + /// statement legitimately differs). + /// + public static string CompareBreakpointMaps( + Dictionary> expected, + Dictionary> actual, + Dictionary methodNames, + bool includeColumns) + { + var report = new StringBuilder(); + foreach (int row in expected.Keys.Union(actual.Keys).OrderBy(r => r)) + { + var expectedPoints = Visible(expected, row, includeColumns); + var actualPoints = Visible(actual, row, includeColumns); + + var expectedOnly = MultisetDifference(expectedPoints, actualPoints); + var actualOnly = MultisetDifference(actualPoints, expectedPoints); + if (expectedOnly.Count == 0 && actualOnly.Count == 0) + continue; + + methodNames.TryGetValue(row, out var name); + report.Append($"0x{0x06000000 | row:x8} {name}").Append('\n'); + foreach (var p in expectedOnly.OrderBy(p => p)) + report.Append(" - compiler only: ").Append(p).Append('\n'); + foreach (var p in actualOnly.OrderBy(p => p)) + report.Append(" + decompiler only: ").Append(p).Append('\n'); + } + return report.ToString(); + } + + static List Visible(Dictionary> map, int row, bool includeColumns) + { + if (!map.TryGetValue(row, out var list)) + return new List(); + return list.Where(sp => !sp.IsHidden).Select(sp => sp.Format(includeColumns)).ToList(); + } + + static List MultisetDifference(List a, List b) + { + var counts = new Dictionary(); + foreach (var x in b) + counts[x] = counts.GetValueOrDefault(x) + 1; + var result = new List(); + foreach (var x in a) + { + if (counts.TryGetValue(x, out int c) && c > 0) + counts[x] = c - 1; + else + result.Add(x); + } + return result; + } + + /// + /// Checks that the decompiler's own PDB is structurally well-formed, independent of any + /// oracle: within each method the sequence points' IL offsets must be strictly increasing, + /// i.e. no duplicate or overlapping sequence points (a duplicate is exactly the failure + /// that aborts PortablePdbWriter in debug builds). Returns the offending methods, or + /// an empty string when well-formed. + /// + public static string CheckWellFormed( + Dictionary> pdb, + Dictionary methodNames) + { + var report = new StringBuilder(); + foreach (int row in pdb.Keys.OrderBy(r => r)) + { + var list = pdb[row]; + for (int i = 1; i < list.Count; i++) + { + if (list[i].Offset <= list[i - 1].Offset) + { + methodNames.TryGetValue(row, out var name); + report.Append($"0x{0x06000000 | row:x8} {name}: non-increasing IL offset 0x{list[i].Offset:x} after 0x{list[i - 1].Offset:x}").Append('\n'); + break; + } + } + } + return report.ToString(); + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index e2661e557..8719e4cc8 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; -using System.Text; using System.Xml.Linq; using ICSharpCode.Decompiler.CSharp; @@ -13,8 +12,6 @@ using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Tests.Helpers; -using Microsoft.DiaSymReader.Tools; - using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests @@ -24,31 +21,41 @@ namespace ICSharpCode.Decompiler.Tests { static readonly string TestCasePath = Tester.TestCasePath + "/PdbGen"; + /// + /// How strictly a reconstructed breakpoint must match the original compiler's. + /// + enum Tolerance + { + /// Visible breakpoints must match on source line and column. + LinesAndColumns, + /// Only the source line span must match; column placement may differ. + Lines + } + [Test] public void HelloWorld() { - TestGeneratePdb(); + TestSequencePoints(); } [Test] - [Ignore("Missing nested local scopes for loops, differences in IL ranges")] public void ForLoopTests() { - TestGeneratePdb(); + TestSequencePoints(); } [Test] - [Ignore("Differences in IL ranges")] public void LambdaCapturing() { - TestGeneratePdb(); + // The decompiler emits one extra visible breakpoint that the C# compiler keeps hidden; + // the recorded residual pins that known, deliberate difference. + TestSequencePoints(knownResidual: true); } [Test] - [Ignore("Duplicate sequence points for local function")] public void Members() { - TestGeneratePdb(); + TestSequencePoints(knownResidual: true); } [Test] @@ -194,41 +201,78 @@ namespace ICSharpCode.Decompiler.Tests } } - private void TestGeneratePdb([CallerMemberName] string testName = null) + /// + /// Compiles the fixture with the C# compiler (producing a real PDB used as the oracle), + /// decompiles it and reconstructs a PDB with , then compares + /// the two PDBs' visible breakpoint maps. The decompiler reconstructs IL ranges, hidden + /// sequence points and local scopes differently from the compiler, so the comparison drops + /// those and asserts only on the source location of each visible (non-hidden) sequence point. + /// + /// + /// Whether visible breakpoints must match on column as well as line. + /// + /// + /// When false, the breakpoint map must match the compiler exactly (after the projection + /// above). When true, the residual difference must match the committed + /// <testName>.residual.txt snapshot - this pins the known imperfections the + /// decompiler cannot yet reproduce, so an improvement or a regression both flip the test and + /// prompt a deliberate snapshot update (the same accept-the-diff workflow as the pretty tests). + /// + private void TestSequencePoints(Tolerance tolerance = Tolerance.LinesAndColumns, bool knownResidual = false, + [CallerMemberName] string testName = null) { - const PdbToXmlOptions options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.ThrowOnError | PdbToXmlOptions.IncludeTokens | PdbToXmlOptions.ResolveTokens | PdbToXmlOptions.IncludeMethodSpans; - - string xmlFile = Path.Combine(TestCasePath, testName + ".xml"); (string peFileName, string pdbFileName) = CompileTestCase(testName); - var moduleDefinition = new PEFile(peFileName); - var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); - var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); - using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, testName + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) + var module = new PEFile(peFileName); + var resolver = new UniversalAssemblyResolver(peFileName, false, module.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); + var decompiler = new CSharpDecompiler(module, resolver, new DecompilerSettings()); + + using var generatedPdb = new MemoryStream(); + new PortablePdbWriter { NoLogo = true } + .WritePdb(module, decompiler, new DecompilerSettings(), generatedPdb); + + var methodNames = PdbSequencePoints.ReadMethodNames(peFileName); + var actual = PdbSequencePoints.Read(generatedPdb); + Dictionary> expected; + using (var compilerPdb = File.OpenRead(pdbFileName)) + expected = PdbSequencePoints.Read(compilerPdb); + + // Oracle-free structural check on the decompiler's own PDB. + string wellFormed = PdbSequencePoints.CheckWellFormed(actual, methodNames); + Assert.That(wellFormed, Is.Empty, "the reconstructed PDB is not well-formed:\n" + wellFormed); + + string residual = PdbSequencePoints.CompareBreakpointMaps( + expected, actual, methodNames, includeColumns: tolerance == Tolerance.LinesAndColumns); + + if (knownResidual) { - pdbStream.SetLength(0); - new PortablePdbWriter { NoLogo = true } - .WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream); - pdbStream.Position = 0; - using (Stream peStream = File.OpenRead(peFileName)) - using (Stream expectedPdbStream = File.OpenRead(pdbFileName)) + string snapshotFile = Path.Combine(TestCasePath, testName + ".residual.txt"); + string expectedResidual = File.Exists(snapshotFile) + ? File.ReadAllText(snapshotFile).Replace("\r\n", "\n") + : ""; + if (residual != expectedResidual) { - using (StreamWriter writer = new StreamWriter(Path.ChangeExtension(pdbFileName, ".xml"), false, Encoding.UTF8)) + // Setting ILSPY_ACCEPT_PDB_RESIDUAL=1 accepts the new residual in place, so a + // deliberate change is committed by re-running the test with the variable set + // rather than by hand-copying files. + if (Environment.GetEnvironmentVariable("ILSPY_ACCEPT_PDB_RESIDUAL") == "1") { - PdbToXmlConverter.ToXml(writer, expectedPdbStream, peStream, options); + File.WriteAllText(snapshotFile, residual); } - peStream.Position = 0; - using (StreamWriter writer = new StreamWriter(Path.ChangeExtension(xmlFile, ".generated.xml"), false, Encoding.UTF8)) + else { - PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options); + File.WriteAllText(snapshotFile + ".generated", residual); + Assert.Fail($"the breakpoint-map residual changed; review the difference and, if intended, " + + $"accept it by re-running this test with ILSPY_ACCEPT_PDB_RESIDUAL=1 set (or copy " + + $"'{testName}.residual.txt.generated' over '{testName}.residual.txt')." + + $"\n\nExpected:\n{expectedResidual}\nActual:\n{residual}"); } } } - string expectedFileName = Path.ChangeExtension(xmlFile, ".expected.xml"); - ProcessXmlFile(expectedFileName); - string generatedFileName = Path.ChangeExtension(xmlFile, ".generated.xml"); - ProcessXmlFile(generatedFileName); - CodeAssert.AreEqual(Normalize(expectedFileName), Normalize(generatedFileName)); + else + { + Assert.That(residual, Is.Empty, "the reconstructed breakpoint map differs from the compiler's:\n" + residual); + } } private (string peFileName, string pdbFileName) CompileTestCase(string testName) @@ -244,41 +288,5 @@ namespace ICSharpCode.Decompiler.Tests return (peFileName, pdbFileName); } - - private void ProcessXmlFile(string fileName) - { - var document = XDocument.Load(fileName); - foreach (var file in document.Descendants("file")) - { - file.Attribute("checksum").Remove(); - file.Attribute("embeddedSourceLength")?.Remove(); - var name = file.Attribute("name"); - if (name != null) - { - // Generated document names use the platform's directory separator; - // the expected files were produced on Windows. - name.Value = name.Value.Replace('/', '\\'); - } - file.ReplaceNodes(new XCData(file.Value.Replace("\uFEFF", ""))); - } - document.Save(fileName, SaveOptions.None); - } - - private string Normalize(string inputFileName) - { - return File.ReadAllText(inputFileName).Replace("\r\n", "\n").Replace("\r", "\n"); - } - } - - class StringWriterWithEncoding : StringWriter - { - readonly Encoding encoding; - - public StringWriterWithEncoding(Encoding encoding) - { - this.encoding = encoding ?? throw new ArgumentNullException("encoding"); - } - - public override Encoding Encoding => encoding; } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore index ae2ac11dc..96f4c7ba8 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore @@ -2,3 +2,4 @@ /*.pdb /*.expected.xml /*.generated.xml +/*.residual.txt.generated diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml index 492f58104..39e8fc01e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml @@ -28,45 +28,4 @@ public class ForLoopTests } ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml index 90f2d542b..e0708da65 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml @@ -16,20 +16,4 @@ public class HelloWorld } ]]> - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.residual.txt b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.residual.txt new file mode 100644 index 000000000..e30fd63ec --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.residual.txt @@ -0,0 +1,2 @@ +0x06000001 ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing.Main + + decompiler only: (8,2)-(8,3) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml index ac478edb0..5446ad889 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml @@ -21,42 +21,4 @@ public class LambdaCapturing } ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.residual.txt b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.residual.txt new file mode 100644 index 000000000..54e218660 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.residual.txt @@ -0,0 +1,6 @@ +0x0600000b C..ctor + - compiler only: (50,2)-(50,12) + + decompiler only: (51,2)-(51,3) +0x0600000c C.Finalize + - compiler only: (56,2)-(56,3) + - compiler only: (57,2)-(57,3) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml index 8e25eed1c..7c35954e4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml @@ -1,95 +1,86 @@ - - 42; - private int Property - { - get + private int Property + { + get { return 0; } - set + set { } - } + } - private C this[int index] => null; + private C this[int index] => null; - private C this[string s] - { - get + private C this[string s] + { + get { return null; } - set + set { } - } + } - public event Action Event - { - add - { - } - remove - { - } - } + public event Action Event + { + add + { + } + remove + { + } + } - public static implicit operator C(int i) - { - return null; - } + public static implicit operator C(int i) + { + return null; + } - static C() - { - } + static C() + { + } - public C() - { + public C() + { Console.WriteLine(); - } - - ~C() - { - } + } - void IDisposable.Dispose() - { - } + ~C() + { + } - private static void Main() - { - C c = new C(); + void IDisposable.Dispose() + { + } - c.Event += delegate + private static void Main() + { + C c = new C(); + c.Event += delegate { }; - _ = c.Property; - _ = c.ExpressionProperty; - _ = c[0]; - _ = c[""]; - - _ = (C)1; - - Local(); - - static void Local() - { + _ = c.Property; + _ = c.ExpressionProperty; + _ = c[0]; + _ = c[""]; + _ = (C)1; + Local(); + static void Local() + { Console.WriteLine(); - } - } + } + } } ]]> - - - - - \ No newline at end of file +