Summary
The Kotlin adapter emits no declaration for a property. map on a Kotlin class lists its methods and nothing else, so a reader concludes the type holds no state. The file parses cleanly — error_count: 0 — so nothing signals that anything was dropped.
Found while correcting a claim in #49 that --include-fields reveals a Kotlin val. It does not, and the flag is not the reason: the declarations are never created, so no projection flag can bring them back.
Measured on ast-bro 4.2.0.
Reproduce
$ cat W.kt
class Widget {
val computed: Int get() = 1
var count: Int = 0
fun go() {}
}
$ ast-bro map W.kt --include-fields
class Widget L1-5
fun go() L4
Same with val in the constructor, an explicit public val, @JvmField val, and a top-level val outside any class.
Kotlin is the only language that drops state entirely
Same shape in each language, map --include-fields:
| Language |
What comes back |
| C# |
Count [field], Name [property] |
| Java |
count [field] |
| Scala |
computed [field], count [field] |
| TypeScript |
count [field] |
| PHP |
$count [field] |
| Ruby |
:count [field] |
| Kotlin |
nothing |
Scala is the closest comparison — same val / var spelling, and its adapter maps both to Field.
The handling exists but is not reached
src/adapters/kotlin.rs already lists property_declaration among the node kinds it walks (line 65), branches on it (line 128), and can produce DeclarationKind::Property and Field (lines 454-456). So this is not a missing feature — the branch is written and does not fire on these inputs. A grammar node-name mismatch, or a class-body walk that never descends to properties, would both look like this. I did not chase it further.
No fixture covers a Kotlin property, which is presumably why it survived.
The design question this raises
Fixing the extraction needs a decision about which kind to emit, and that is not obvious:
- Kotlin has both, but the syntax does not separate them the way C# does.
val x = 1 is a property with a backing field; val x get() = 1 is a property with none; a true field needs @JvmField or is a private implementation detail. So "which of the two is this" is often not answerable from the declaration alone.
- Java has no properties at all, so a
--no-properties flag would be meaningless there — renaming --no-fields is not the answer.
- C# does separate them syntactically (
field_declaration vs property_declaration), and today the adapter reports them as distinct kinds.
The good news is that the projection does not currently ask the question. _member_visible treats Field | Property | Event | Indexer as one class, so --no-fields already means "hide the type's state" rather than "hide one syntactic form of it". Whichever kind Kotlin emits, the flag behaves correctly — the choice only affects how the member is labelled in output.
For map's purpose a public field and a public property are close to equivalent, with the property arguably the more interesting of the two, since it is the one a caller is meant to use. That argues for emitting Property for Kotlin's val / var and not attempting to distinguish a backing field. But Scala's adapter already chose Field for the same spelling, so consistency between the two is worth deciding deliberately rather than per adapter.
Suggestion
Two separable pieces:
- Emit something for a Kotlin property — the current silent drop is the actual bug, and either kind is better than none.
- Decide the Kotlin/Scala labelling together, since both languages spell state as
val / var and currently disagree by accident rather than by choice.
Either way, a fixture asserting that a Kotlin val reaches map would keep this from returning.
Summary
The Kotlin adapter emits no declaration for a property.
mapon a Kotlin class lists its methods and nothing else, so a reader concludes the type holds no state. The file parses cleanly —error_count: 0— so nothing signals that anything was dropped.Found while correcting a claim in #49 that
--include-fieldsreveals a Kotlinval. It does not, and the flag is not the reason: the declarations are never created, so no projection flag can bring them back.Measured on
ast-bro 4.2.0.Reproduce
Same with
valin the constructor, an explicitpublic val,@JvmField val, and a top-levelvaloutside any class.Kotlin is the only language that drops state entirely
Same shape in each language,
map --include-fields:Count [field],Name [property]count [field]computed [field],count [field]count [field]$count [field]:count [field]Scala is the closest comparison — same
val/varspelling, and its adapter maps both toField.The handling exists but is not reached
src/adapters/kotlin.rsalready listsproperty_declarationamong the node kinds it walks (line 65), branches on it (line 128), and can produceDeclarationKind::PropertyandField(lines 454-456). So this is not a missing feature — the branch is written and does not fire on these inputs. A grammar node-name mismatch, or a class-body walk that never descends to properties, would both look like this. I did not chase it further.No fixture covers a Kotlin property, which is presumably why it survived.
The design question this raises
Fixing the extraction needs a decision about which kind to emit, and that is not obvious:
val x = 1is a property with a backing field;val x get() = 1is a property with none; a true field needs@JvmFieldor is a private implementation detail. So "which of the two is this" is often not answerable from the declaration alone.--no-propertiesflag would be meaningless there — renaming--no-fieldsis not the answer.field_declarationvsproperty_declaration), and today the adapter reports them as distinct kinds.The good news is that the projection does not currently ask the question.
_member_visibletreatsField | Property | Event | Indexeras one class, so--no-fieldsalready means "hide the type's state" rather than "hide one syntactic form of it". Whichever kind Kotlin emits, the flag behaves correctly — the choice only affects how the member is labelled in output.For
map's purpose a public field and a public property are close to equivalent, with the property arguably the more interesting of the two, since it is the one a caller is meant to use. That argues for emittingPropertyfor Kotlin'sval/varand not attempting to distinguish a backing field. But Scala's adapter already choseFieldfor the same spelling, so consistency between the two is worth deciding deliberately rather than per adapter.Suggestion
Two separable pieces:
val/varand currently disagree by accident rather than by choice.Either way, a fixture asserting that a Kotlin
valreachesmapwould keep this from returning.