Skip to content

fix: annotate HtmlToWmlConverterCore.cs and HtmlToWmlConverter.cs for nullable reference types - #663

Merged
JSv4 merged 1 commit into
mainfrom
fix/647-nullable-htmltowml-core
Sep 2, 2026
Merged

fix: annotate HtmlToWmlConverterCore.cs and HtmlToWmlConverter.cs for nullable reference types#663
JSv4 merged 1 commit into
mainfrom
fix/647-nullable-htmltowml-core

Conversation

@JSv4

@JSv4 JSv4 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

Completes issue #647: the HTML→DOCX converter family is now fully nullable-annotated. Steps 1–2 (the CSS parser/applier) landed in #661; this PR is steps 3–4, the transform/updater core and its public facade — the last two of the four files in scope.

  • HtmlToWmlConverterCore.cs (5,361 lines, the largest file touched in this whole campaign, ~438 distinct warnings): #nullable disable removed. The bulk of the ripple comes from LocalExtensions.GetProp now returning CssExpression? (it can genuinely return null when a property isn't set), which touches ~55 call sites. Several methods had their return types widened to nullable where they can legitimately produce no markup for a given input (Transform, GetParagraphProperties, GetTableArray, GetCellShading, and similar). Where a call site dereferences a GetProp(...) result immediately with no pre-existing null-check, a ! documents that CSS shorthand/cascade normalization guarantees the property is set by the time it reaches that point. Where the original code already had a defensive fallback (e.g. table cell margin properties defaulting when null), the fix widens the local to nullable instead of asserting non-null over an existing guard — the guard was there for a reason. One provably unreachable branch in FontMerge (both inputs null, already handled by an earlier early-return) was removed as dead code.
  • HtmlToWmlConverter.cs (534 lines, 52 warnings): HtmlToWmlConverterSettings's 8 reference-type fields are widened nullable rather than made required, because GetDefaultSettings() — the library's own trusted factory — leaves BaseUriForImages null even on the happy path. GetDefaultFontInfo keeps its pre-existing graceful ?. fallback for StyleDefinitionsPart rather than asserting non-null, matching the method's own designed behavior.
  • HtmlToWmlCssApplier.cs (already nullable-clean from fix: annotate nullable reference types in the CSS layer of the HTML->DOCX family #661): small ripple fix — three settings.SectPr accesses now assert non-null now that SectPr itself became nullable in this PR.
  • CLAUDE.md: warning-baseline prose updated — 4 legacy #nullable disable files remain (down from 6), library baseline 117 (was 119), test project baseline 693 (was 689, verified stable across three independent measurements including a clean rebuild).

Test plan

  • dotnet build Docxodus/Docxodus.csproj --no-incremental — 0 errors, 0 warnings in the four touched files
  • grep -l "^#nullable disable" Docxodus/*.cs — none of the 4 target files remain
  • dotnet test --filter "FullyQualifiedName~HwTests" — 232/232 passed
  • Full Docxodus.Tests suite — 3923 passed, 3 skipped, 0 failed
  • ./scripts/build-wasm.sh — succeeds, within the wire-size budget
  • dotnet build Docxodus.sln — 0 errors, 117 warnings
  • All out-of-solution tool/benchmark projects build clean

Closes #647

🤖 Generated with Claude Code

https://claude.ai/code/session_01VSnwkK1Nx6zZb2RnnoxKdx

… nullable reference types

Removes #nullable disable from the two remaining HTML->DOCX files (the
transform/updater core and its public facade), completing the four-file
scope of issue #647 (steps 1-2, the CSS parser/applier, landed in #661).

HtmlToWmlConverterCore.cs (5361 lines, ~438 distinct warnings) required the
most structural work: LocalExtensions.GetProp now returns CssExpression?,
rippling through ~55 call sites; several return types widened to nullable
(Transform, GetParagraphProperties, GetTableArray, GetCellShading, etc.) to
honestly reflect paths that can produce no markup; CharStyleAttributes and
HeadingInfo fields widened or promoted to `required` based on how they're
actually constructed. Where a call site immediately dereferences a
GetProp(...) result with no existing null-check, the CSS cascade/shorthand
normalization guarantees non-null and a `!` assertion documents why. Where
the original code already had a defensive null fallback (e.g. table cell
margin properties), the fix widens the local to nullable instead of
asserting non-null over an existing guard. One provably unreachable branch
in FontMerge was removed as dead code.

HtmlToWmlConverter.cs (534 lines, 52 warnings) required widening
HtmlToWmlConverterSettings' 8 reference-type fields to nullable, since
GetDefaultSettings() leaves BaseUriForImages null even in the trusted
factory path. GetDefaultFontInfo keeps its pre-existing graceful `?.`
fallback for StyleDefinitionsPart rather than asserting non-null.

HtmlToWmlCssApplier.cs (already nullable-clean from #661) needed a small
ripple fix: three settings.SectPr accesses now assert non-null now that
SectPr itself is nullable.

CLAUDE.md's warning-baseline prose is updated to match: 4 legacy
#nullable-disable files remain (down from 6), library baseline 117 (was
119), test project baseline 693 (was 689, verified stable across three
independent measurements including a clean rebuild).

Validated: dotnet build (0 errors, 0 warnings in the touched files),
full Docxodus.Tests suite (3923 passed, 3 skipped, 0 failed, including all
232 HwTests), WASM build (within size budget), and all out-of-solution
tool/benchmark projects build clean.

Closes #647

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VSnwkK1Nx6zZb2RnnoxKdx
@JSv4
JSv4 merged commit 10ba24f into main Sep 2, 2026
14 checks passed
@JSv4
JSv4 deleted the fix/647-nullable-htmltowml-core branch September 2, 2026 09:59
JSv4 pushed a commit that referenced this pull request Sep 2, 2026
#663-#666 continue the #650 nullable run. Two of them land on this
demo's measured path: #665 and #666 annotate WmlToHtmlConverter.cs and
.Charts.cs, which are the conversion stage of the full+HTML depth. They
are not cosmetic either — removing the #nullable disable header added 27
null-handling lines to the converter, which is real runtime code, so
this got a rebuild and the full browser run rather than a wave-through.
14/14 pass.

Measured, and the numbers are NOT republished, deliberately. A single
controlled session read 12-25% faster across most rows (revisions 50->38,
redline 67->53, full 127->106, conversion stage in isolation 60->53) —
but oneCall, which IS the redline depth's call, came back flat at 51->50.
Redline cannot drop 21% while docxDiffCompareProducts holds; the pattern
contradicts itself. Added null-guards should also cost time rather than
save it, so the direction is wrong for the change too.

That is variance, on one session, and the published figures came from a
pooled two-session controlled measurement. The README asks for pooling
before believing a movement and for labelling one that fits nothing as
the machine; publishing this would break both rules on my own page.

Also merges #663 (HtmlToWmlConverter/Core) and #664 (DocumentBuilder),
neither of which this demo calls.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nullable: annotate the HTML→DOCX family (600 sites across 4 self-contained files)

1 participant