Skip to content

Render compile-time constant values in C# and C++ emitters - #48

Merged
Malcolmnixon merged 3 commits into
mainfrom
feature/render-constant-values
Oct 4, 2026
Merged

Malcolmnixon merged 3 commits into
mainfrom
feature/render-constant-values

Conversation

@Malcolmnixon

Copy link
Copy Markdown
Member

Summary

Implements the feature request: render the values of constants whose values are known at compile time (primarily numbers and strings), across the languages ApiMark supports.

  • C# (DotNetEmitter): const fields and enum members now render their value (e.g. public const string DefaultName = "default", public const SampleStatus Active = 0) via a new FormatConstantValue helper that formats Mono.Cecil's folded ield.Constant — string escaping/quoting, char escaping (including control characters such as \0), lowercase �ool, plain numeric literals, and enum-typed constants rendered as their underlying numeric value.
  • C++ (ClangAstParser / CppAstModel / both C++ emitters): CppField gains a nullable Value, populated only for constexpr fields whose initializer is a simple literal (int/ loat/char/�ool/string) or a leading unary +/- directly over such a literal (e.g. -5). Compound/computed expressions (e.g. 2 * 21) and named-constant references are deliberately left unrendered — folding them correctly was out of scope, to avoid shipping a partial/incorrect evaluator (guarded by a regression test).
  • VHDL: already rendered constant values; no changes needed.

Review process

  • A code-review pass caught and fixed two real bugs before merge: the C# NUL character ('\0') was rendered as a raw control byte instead of an escaped literal, and C++ negative constexpr literals (e.g. -5) were incorrectly treated as "computed" and silently dropped.
  • A formal-review pass initially failed on 5 documentation/test-coverage gaps (per-unit design docs not updated, an under-described requirement/user-guide scope, missing C++ �ool/char constexpr test coverage, a missing CppField.Value construction test, missing verification Test Scenarios entries). All were remediated and the formal review passed on re-verification.

Testing

  • ApiMark.DotNet.Tests: 342/342 passing (net8.0/net9.0/net10.0)
  • ApiMark.Cpp.Tests: 189/189 passing with clang actually exercised, 0 skipped (net8.0/net9.0/net10.0)
  • pwsh ./lint.ps1, dotnet reviewmark --lint, dotnet reqstream --lint: all pass

Out of scope / follow-up ideas

  • C++ compound/computed-expression constant folding (e.g. arithmetic, cross-references to other constants, casts) is intentionally not attempted; a regression test guards against this being silently "fixed" incorrectly in the future.

Renders the values of constants whose values are known at compile time:

- C#: const fields and enum members now show their value (e.g.
  public const string DefaultName = "default",
  public const SampleStatus Active = 0), via a new FormatConstantValue
  helper that formats Mono.Cecil's folded field.Constant (string escaping,
  char escaping including control characters, lowercase bool, numeric
  literals, enum underlying values).
- C++: CppField gains a nullable Value populated only for constexpr fields
  whose initializer is a simple literal (int/float/char/bool/string) or a
  leading unary +/- directly over such a literal (e.g. -5). Compound or
  computed expressions (e.g. 2 * 21) and named-constant references are
  deliberately left unrendered to avoid an incorrect/partial evaluator.
  Rendered in both the gradual-disclosure and single-file C++ emitters.
- VHDL already rendered constant values; left unchanged.

Includes updated design and verification docs, reqstream requirement
updates, user guide updates, and new fixture/regression tests (C# NUL-char
escaping, C++ negative-literal and bool/char constexpr fields, computed-
expression non-folding guard, and CppField.Value construction tests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 3, 2026 23:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved C# escaping and C++ parsing/coverage issues remain, along with traceability gaps.

Review effort: Lite
Findings: 3 Medium severity · 2 Low severity

Open (5)
What changed in this PR

Adds compile-time constant rendering for C# and C++ API documentation emitters, with parser/model updates, tests, and documentation traceability.

Changes:

  • Formats C# constants and enum values.
  • Extracts and renders simple C++ constexpr literals.
  • Adds fixtures, regression tests, and supporting documentation.
File Summary
test/​ApiMark.DotNet.Tests/​DotNetEmitterTests.cs Tests C# constant rendering.
test/​ApiMark.DotNet.Tests/​DocumentationCoverageCheckerTests.cs Updates coverage expectations.
test/​ApiMark.DotNet.Fixtures/​SampleEnum.cs Adds enum constant fixtures.
test/​ApiMark.DotNet.Fixtures/​SampleClass.cs Adds C# constant fixtures.
test/​ApiMark.Cpp.Tests/​CppEmitterSingleFileTests.cs Tests single-file rendering.
test/​ApiMark.Cpp.Tests/​CppEmitterGradualDisclosureTests.cs Tests gradual rendering.
test/​ApiMark.Cpp.Tests/​CppAstModelTests.cs Tests CppField.Value.
test/​ApiMark.Cpp.Tests/​ClangAstParserTests.cs Tests C++ literal extraction.
test/​ApiMark.Cpp.Fixtures/​include/​fixtures/​SampleClass.h Adds C++ constexpr fixtures.
src/​ApiMark.DotNet/​DotNetEmitter.cs Formats C# constant values.
src/​ApiMark.Cpp/​CppEmitterSingleFile.cs Renders values in single-file output.
src/​ApiMark.Cpp/​CppEmitterGradualDisclosure.cs Renders values in gradual output.
src/​ApiMark.Cpp/​CppAst/​CppAstModel.cs Adds nullable field values.
src/​ApiMark.Cpp/​CppAst/​ClangAstParser.cs Extracts C++ literal values.
docs/​verification/​api-mark-dot-net/​dot-net-emitter.md Adds C# verification scenarios.
docs/​verification/​api-mark-cpp/​cpp-emitter-single-file.md Adds single-file scenarios.
docs/​verification/​api-mark-cpp/​cpp-emitter-gradual-disclosure.md Adds gradual scenarios.
docs/​verification/​api-mark-cpp/​cpp-ast-model.md Adds model scenarios.
docs/​verification/​api-mark-cpp/​clang-ast-parser.md Adds parser scenarios.
docs/​user_guide/​dotnet.md Documents C# constant output.
docs/​user_guide/​cpp.md Documents C++ constant output.
docs/​reqstream/​api-mark-dot-net/​dot-net-emitter.yaml Adds C# traceability.
docs/​reqstream/​api-mark-cpp/​cpp-emitter-single-file.yaml Adds single-file traceability.
docs/​reqstream/​api-mark-cpp/​cpp-emitter-gradual-disclosure.yaml Adds gradual traceability.
docs/​reqstream/​api-mark-cpp/​clang-ast-parser.yaml Adds parser traceability.
docs/​design/​api-mark-dot-net/​dot-net-emitter.md Documents formatting helpers.
docs/​design/​api-mark-dot-net.md Updates .NET design documentation.
docs/​design/​api-mark-cpp/​cpp-emitter-single-file.md Documents single-file rendering.
docs/​design/​api-mark-cpp/​cpp-emitter-gradual-disclosure.md Documents gradual rendering.
docs/​design/​api-mark-cpp/​cpp-ast-model.md Documents CppField.Value.
docs/​design/​api-mark-cpp/​clang-ast-parser.md Documents literal extraction.
docs/​design/​api-mark-cpp.md Updates C++ design documentation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/ApiMark.Cpp/CppAst/ClangAstParser.cs
Comment thread src/ApiMark.Cpp/CppAst/ClangAstParser.cs
Comment thread src/ApiMark.DotNet/DotNetEmitter.cs Outdated
Comment thread docs/reqstream/api-mark-cpp/clang-ast-parser.yaml
Comment thread docs/reqstream/api-mark-dot-net/dot-net-emitter.yaml
Fixes all 5 findings from the automated PR #48 review:

- Medium: Add FloatingLiteral fixture/test coverage (ClangAstParser).
  New constexpr double field (Pi) in the C++ fixture, with a test
  asserting the display value matches clang's own decimal spelling.
- Medium: Reject nested unary operators in the constexpr literal
  formatter. A doubly-negated literal such as - -5 previously
  reconstructed as the misleading --5 (reads as a C++ decrement);
  ExtractConstexprUnaryLiteralValue now rejects a UnaryOperator operand
  (via a new UnwrapPassThroughKind helper) and yields null instead.
  New constexpr field (DoubleNegated) and test cover the guard.
- Medium: Escape all remaining control characters in C# string/char
  literals. EscapeStringLiteral and EscapeCharLiteral now share a new
  EscapeControlCharacter helper covering \a, \b, \f, \v (in addition to
  the existing \0, \n, \r, \t), with a \uXXXX fallback for any other
  non-printable control character (e.g. DEL/0x7f) so no raw control
  byte is ever embedded in generated documentation text. New direct
  unit tests on FormatConstantValue cover both the named-escape and
  Unicode-fallback paths without touching the compiled fixture
  assembly.
- Low: Add the negative-literal regression test to the
  clang-ast-parser.yaml requirement's traceability list.
- Low: Add the NUL-char regression test to the dot-net-emitter.yaml
  requirement's traceability list.

Also updates design and verification documentation for both affected
units to describe the new behavior, and adds the new floating-point
and double-negation tests to the C++ requirement's traceability list
for consistency.

Verified: 344/344 .NET tests pass (net8/net9/net10); 191/191 C++ tests
pass (net8/net9/net10, clang exercised, 0 skipped). fix.ps1 and
lint.ps1 both clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 3, 2026 23:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/ApiMark.DotNet/DotNetEmitter.cs Outdated
Fixes the single Medium finding from the latest automated PR #48
review: EscapeControlCharacter only recognized DEL (0x7f) as an
unnamed control character, so Unicode C1 controls (U+0080-U+009F,
e.g. NEL U+0085) fell through to value.ToString() and were embedded
raw, despite the documented guarantee that every non-printable
control uses a \uXXXX escape.

EscapeControlCharacter now falls back to char.IsControl(value) for
its catch-all branch, covering the full Unicode control range (ASCII
C0 controls, DEL, and C1 controls) instead of a hand-rolled range
check. New FormatConstantValue test covers a C1 control character.

Verified: 345/345 .NET tests pass (net8/net9/net10). fix.ps1 and
lint.ps1 both clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 3, 2026 23:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

No unresolved review issues remain, and all approval assessments are positive.

Review effort: Lite
Findings: None

Resolved since last review (1)

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.

2 participants