Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/typstsharp.tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,26 @@ public async Task WarningsFromADocumentCannotBeMutatedByCallers()
await Assert.That(document.Warnings is string[]).IsFalse();
}

/// <summary>
/// The warning-free case had no coverage. This pins the public contract rather than the
/// allocation, so it holds whichever way the empty list is produced: empty, and not a mutable
/// array handed out to callers.
/// </summary>
[Test]
public async Task WarningsFromACleanCompileAreEmptyAndStillImmutable()
{
using var compiler = TypstCompiler.FromSource("= No warnings here");
var document = compiler.CompileToDocument();

await Assert.That(document.Warnings.Count).IsEqualTo(0);
await Assert.That(document.Warnings is string[]).IsFalse();

document.Dispose();

// Warnings are copied out of native memory eagerly, so they outlive the document.
await Assert.That(document.Warnings.Count).IsEqualTo(0);
}

private const string TwoPageSource = """
First page
#pagebreak()
Expand Down
3 changes: 2 additions & 1 deletion src/typstsharp/TypstDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ internal unsafe TypstDocument(CsBindgen.CompileResult native)
}

// Warnings are small and are copied eagerly so that they stay usable after disposal.
var warnings = new string[warningCount];
// A clean compile is the common case, and Array.Empty spares it the only allocation here.
var warnings = warningCount == 0 ? Array.Empty<string>() : new string[warningCount];
for (int i = 0; i < warnings.Length; i++)
{
var warning = native.warnings[i];
Expand Down
Loading