feat(codegen): propagate {.ffi.} proc doc comments to generated bindings#136
Open
gmelodie wants to merge 1 commit into
Open
feat(codegen): propagate {.ffi.} proc doc comments to generated bindings#136gmelodie wants to merge 1 commit into
gmelodie wants to merge 1 commit into
Conversation
gmelodie
force-pushed
the
feat/propagate-comments
branch
from
July 16, 2026 20:17
14cd10a to
301e24b
Compare
gmelodie
force-pushed
the
feat/propagate-comments
branch
from
July 17, 2026 11:56
301e24b to
8776bb8
Compare
gmelodie
marked this pull request as ready for review
July 17, 2026 11:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #127. Doc comments written on
{.ffi.}-annotated Nim procs were dropped during codegen, so the exported API had to be documented a second time by hand in each target language (or not at all). This carries them through to every generated binding, letting the API be documented once at the source.The macros lift the proc's leading
##comment out of the AST (extractDocComment,ffi/internal/ffi_macro.nim) into a newdocfield onFFIProcMeta/FFIEventMeta, and each generator renders it in its target's native comment syntax.Given this source:
the generated bindings now carry
/** Sleeps ... */on both the exportedmy_timer_echosymbol and themy_timer_ctx_echowrapper,/// ...on the C++echo()/echoAsync()methods and the Rustpub fn echo/echo_async, and a;comment in the CDDL schema.What carries a doc
cbor+abi = c){.ffi.}_ctx_*wrapper (incl. scalar fast path)Asyncvariantextern "C"+pub fn+_async;rule comment{.ffiCtor.}_ctx_createcreate+createAsynccreate+new_async;rule comment{.ffiDtor.}_ctx_destroyimpl Drop);rule comment{.ffiEvent.}_ctx_add_<ev>_listeneraddOn<X>Listeneradd_<ev>_listenerAffected Areas
ffi/internal/ffi_macro.nim—extractDocCommentreads the body's leadingnnkCommentStmt; wired into all four registration sites.ffi/codegen/meta.nim— newdocfield onFFIProcMetaandFFIEventMeta.ffi/codegen/string_helpers.nim—renderDocComment(line-prefix:///,;),renderMemberDocComment(the 4-space class/impl indent) andrenderBlockDocComment(C style). All return@[]for an empty doc, so undocumented procs emit byte-identical output to before.*/is escaped to* /so a doc comment cannot close its own block and splice text into the header as code.ffi/codegen/c.nim— both header paths: CBOR (extern decls, ctor, dtor, methods, listeners) andabi = c(extern decls, ctor, dtor, methods, scalar fast path).ffi/codegen/cpp.nim,ffi/codegen/rust.nim— extern decls, ctor, methods, their async variants, and the event-listener API.ffi/codegen/cddl.nim— a;line under each proc's existing rule header.ffi/codegen/c_cpp_common.nim—ClassifiedProcsnow carriesdtor: Option[FFIProcMeta]instead of a baredtorProcName, so the destructor emitters get the whole meta rather than a widening run of positional strings.examples/— a few#comments on annotated procs intimer.nimandecho.nimconverted to##so the feature is exercised by the checked-in bindings; the*_bindings/files are the regenerated output.Impact on Library Users
Additive and opt-in: a proc with no doc comment generates byte-identical output to before, which the Rust additivity test pins. Adding a
##comment to an annotated proc now changes the generated bindings, sonimble check_bindingswill flag them stale until regenerated — worth knowing, since a comment-only edit did not previously affect codegen.Only
##doc comments on procs propagate. Comments on{.ffi.}types and their fields do not, and cannot without parsing the source text: Nim's parser discards doc comments onnnkTypeDefand object fields before a pragma macro can observe them, and there is noNimNode.commentaccessor to recover them. Confirmed against the 2.2.6 AST; documented as a limitation in the README.Risk Assessment
Low. The change is confined to compile-time metadata and to string emission in codegen — no runtime, wire-format, ABI or memory-management surface is touched, and the doc text never crosses the FFI boundary. The one input that could corrupt a generated C header, a
*/inside a doc comment, is escaped and has regression tests both ways.Testing
tests/unit/test_doc_extraction.nim(new, 8 tests) — drives real annotated procs through the macros and asserts on the compile-time registry: single- and multi-line docs,#vs##, docs alongside a pragma spec, ctor/dtor/event.tests/unit/test_doc_propagation.nim(new, 14 tests) — asserts the exact rendered output per generator, that an undocumented registry leaks no doc text, and that docs are purely additive in the Rust output.tests/unit/test_string_helpers.nim— renderer tests: empty/blank docs, multi-line, indentation, trailing blanks, and the*/escape.--mm:orcand--mm:refc;nimble check_bindingspasses for rust, cpp, c and c_abi against the regenerated example bindings.References