Documentation comments
KDoc on an exported Kotlin declaration becomes an XML doc comment (///) on the matching generated C# declaration, so a bound member's <summary>, <remarks>, <param>, <returns>, and <exception> show up in the consumer's own IDE. Nothing needs to be written differently in Kotlin to get this.
The second overload above is the widened signature suite's default produces; it keeps the doc but drops the <param> it has no parameter for.
What carries a doc comment
Every generated declaration that mirrors a documented Kotlin one: a class, object, interface (and its members), enum (and a documented entry), sealed base and arm, value class, constructor (including a secondary one), method, property, extension function, top-level function, and the Async projection of a suspend function (see Coroutines and Flow).
Tag mapping
KDoc | C# |
|---|---|
body, first paragraph |
|
a later paragraph | a |
|
|
a fenced code block | a |
|
|
| as above, with the C# |
|
|
|
|
|
|
| the |
| the |
|
|
| no doc comment on that declaration at all |
|
|
| dropped |
A tag section (@param, @throws, …) runs through any blank lines up to the next tag; the whole section is one string, joined with spaces.
<remarks>: everything after the summary, in order
Every later paragraph, every fenced block outside a tag, and an unresolved @see all land in one <remarks> element, never more than one. Rehome below shows three behaviors at once: a @param section that runs through a blank line, an unmapped @throws, and both halves of @see (one resolves, one doesn't):
home's text includes "once the sunbeam moves" from after the blank line, because a blank line inside a tag section continues that section rather than starting a body paragraph; the section ends only at the next tag. RuntimeException isn't in the exception table, so it crefs KotlinException and keeps its own name as a plain-text prefix. @see SunSpot resolves to a type this file declares, so it becomes a navigable <seealso cref>; @see book names a member, which has no cref spelling the bridge can produce, so it closes <remarks> as prose instead.
Every parameter gets a tag, or none do
C# requires either every parameter of a member to carry a <param> tag or none of them to (otherwise an IDE, and this project's own build, sees a partial set as a defect, CS1573). A partially-documented member still gets a tag for the parameter it didn't document, just an empty one:
The same rule covers the cancellationToken parameter the generator adds to a suspend function's Async projection: there's no Kotlin @param for it, so it gets an empty tag too, next to the Kotlin parameters that do have one:
@property and @constructor document a different member than the one they sit on
A primary-constructor property (class Bowl(val flavour: String)) and the primary constructor itself carry no KDoc of their own in Kotlin: @property and @constructor on the class comment are the only way to document them.
Flavour takes its <summary> from @property flavour, and the same text becomes the constructor's <param name="flavour">, since the class has no @param flavour of its own. scoops has a class-level @param instead of a @property, so it documents the constructor parameter only: Scoops the property gets no doc comment at all. A property with its own KDoc (rinsed) always keeps it, even when the class also has a @property rinsed tag for it.
@suppress opts a declaration out
SecretTreat() still generates and works exactly as before; it just has no /// block at all. @suppress on its own line anywhere in the comment drops the whole thing, including any @param/@return on the same declaration.
What doesn't carry a doc comment, or only partly does
A dependency (klib) declaration renders undocumented. KSP reads no KDoc from compiled metadata, so a type reached only through a dependency module carries no comment in the generated C#, however it was documented at its own source.
A member link, a parameter link, and a link to a generic type stay
<c>with the Kotlin spelling.[book],[nights], and[Crate](a generic class) never become acref: a member needs an overload-qualified spelling the bridge doesn't produce yet, and a bare cref to a generic type doesn't resolve.A link Kotlin would resolve through an
importcan pick the wrong type. Withimport other.Oddbeside a same-packageOdd,[Odd]links the same-package type, because the processor cannot see file imports. Rename one of the types to avoid the clash.[label](url)is left as literal prose, not a<see href>.
expect/actual
KDoc written on an expect declaration reaches the generated C#, for every family the list above covers: not only a class, as below, but also a member of an expect class, a top-level expect val, an expect object/interface/sealed class/value class, and an enum entry. If the actual itself carries its own KDoc, that wins instead; most authors write the one shared doc on the expect and leave the actual bare, which is why the example below documents only the expect side.
See expect/actual declarations for how the two sides otherwise relate.
Dropped declarations
A property, method, or top-level function the forward bridge cannot express (see Publishing Kotlin to C#: Skipped declarations) still shows up in Interop.cs: as a <remarks> paragraph on the type that would have declared it, naming the Kotlin member, the diagnostic kind, and why. There is no CirProperty/CirMethod for a dropped member to attach text to, so the paragraph lands on the owner instead, alongside any author KDoc (author paragraphs always come first):
A file whose every top-level declaration was dropped still gets its ADR-007 static holder class, carrying nothing but the remark, rather than disappearing along with its members:
When only a property's setter is dropped, the property survives read-only, so the remark goes on the C# property itself ("Kotlin lastTumble: its setter is not generated because …") instead of on the class, since the member is not actually absent. The remark never repeats the build-log message verbatim: it never includes the author-facing hint or a source file path, so nothing about where the Kotlin was declared ships to a consumer. See ADR-064's 2026-09-20 amendment for the full mechanism.
No separate documentation file to pack
The generated shim ships as source (see Publishing Kotlin to C#), so there is no lib/<tfm>/*.xml documentation file in the package: turn on <GenerateDocumentationFile>true</GenerateDocumentationFile> in the consuming project and these entries show up in its own IntelliSense. A malformed comment (an unresolvable cref, a partial <param> set, mismatched tags) would already have failed the library's own build before the package was published, so nothing here can reach a consumer broken. This mapping mints no handle and changes no ABI: it costs nothing at the crossing, only at compile time. Keep the first paragraph self-contained: it is the whole <summary>, and everything after it lands in <remarks> instead.