Skip to content

Kotlin: val and var never appear in map/digest, so a type reads as having no state #66

Description

@vlsi

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:

  1. Emit something for a Kotlin property — the current silent drop is the actual bug, and either kind is better than none.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions