Skip to content

Kotlin: enum class entries (NORTH, OK(200), RED(0xFF0000) { override fun display() = ... }) are never captured as symbols #320

Description

@Widthdom

Summary

Kotlin's enum class entries — the individual values declared inside the enum body — are never captured by the symbol extractor. The enum type is captured correctly (enum class Directionenum Direction), but each entry (NORTH,, SOUTH,, …) and each parameterized entry (OK(200),, NOT_FOUND(404),, …) falls through every row in the Kotlin pattern set and is silently dropped. Bodied entries with per-entry class-body overrides (RED(0xFF0000) { override fun display() = "red" }) are also dropped in their entirety, including the overridden member.

This is the third language this session where enum values are invisible — siblings:

Java has a dedicated enum-member row (SymbolExtractor.cs:183) that captures entries as function kind, so Java is the one supported language of the four. Kotlin/Swift/PHP/Rust all need parallel treatment.

Repro

CDIDX=/root/.local/bin/cdidx
mkdir -p /tmp/dogfood/kt-enum && cat > /tmp/dogfood/kt-enum/E.kt <<'EOF'
enum class Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST
}

enum class HttpStatus(val code: Int) {
    OK(200),
    NOT_FOUND(404),
    SERVER_ERROR(500);

    fun isError(): Boolean = code >= 400
}

enum class Color(val rgb: Int) {
    RED(0xFF0000) {
        override fun display() = "red"
    },
    GREEN(0x00FF00) {
        override fun display() = "green"
    };

    abstract fun display(): String
}
EOF
"$CDIDX" index /tmp/dogfood/kt-enum --rebuild
"$CDIDX" symbols --db /tmp/dogfood/kt-enum/.cdidx/codeindex.db

Observed:

enum       Color                                    E.kt:16-25
enum       Direction                                E.kt:1-6
enum       HttpStatus                               E.kt:8-14
function   isError                                  E.kt:13-25
(4 symbols in 1 files)

Missing:

  • 4 plain entries in Direction (NORTH, SOUTH, EAST, WEST).
  • 3 parameterized entries in HttpStatus (OK(200), NOT_FOUND(404), SERVER_ERROR(500)).
  • 2 bodied entries in Color (RED(...), GREEN(...)) and the two per-entry display() overrides inside them.
  • abstract fun display(): String at the enum's tail in Color — dropped because isError took the BodyStyle.Brace range and the extractor probably moved past line 24. The isError range E.kt:13-25 in the output (spanning into Color's body) is a separate body-range leakage issue worth checking but likely the same underlying cause.

Expected: 4 + 3 + 2 (or 4 if per-entry overrides are counted) + 1 (abstract display) = at least 9 additional rows. Today: 0.

Suspected root cause

src/CodeIndex/Indexer/SymbolExtractor.cs:186-202 — the Kotlin row list contains:

  • companion object row (line 189)
  • interface row (line 191)
  • enum class row (line 193)
  • class/object row (line 196)
  • fun method/extension row (line 198)
  • Top-level val/var row (line 200)
  • import row (line 201)

No row starts with an uppercase identifier followed by optional parens and a comma / semicolon / open-brace terminator — which is exactly the shape of a Kotlin enum entry. For comparison, Java captures this via a dedicated row:

// Line 183 — Java enum member
new("function", new Regex(@"^\s{2,}(?<name>[A-Z]\w*)\s*(?:\([^)]*\))?\s*(?:,|\{|;)\s*$", RegexOptions.Compiled), BodyStyle.None),

The Java row relies on ^\s{2,} (2+ leading spaces, to avoid matching top-level identifiers) and a closing-token terminator (,, {, ;). Kotlin entries have the same structural shape and the same row shape would work, possibly with minor adjustments:

  1. Kotlin allows single-tab or 4-space indent conventions — the Java \s{2,} lookahead is permissive enough to cover both.
  2. Kotlin bodied entries have a { terminator where the body spans lines, and the closing }, (or };) is on a later line — BodyStyle.Brace would be appropriate so the entry's range covers the override body.
  3. Kotlin's abstract enum trailing member syntax places an unterminated enum list (};) before the abstract method block — verify that the new row and the existing fun row coexist without fighting for the same lines.

Suggested direction

Add one row to the Kotlin pattern set, placed after the fun row (more specific patterns before catch-alls):

// Kotlin enum entry — plain (NAME,), parameterized (NAME(args),), or bodied (NAME(args) { ... }).
// Kotlin enum エントリ — bare / パラメータ付き / body 付きの各形に対応。
new("property",
    new Regex(
        @"^\s{2,}(?<name>[A-Z][A-Z0-9_]*)"                   // uppercase-only enum entry name
      + @"(?:\s*\((?<returnType>[^)]*)\))?"                  // optional argument list captured as return_type metadata
      + @"\s*(?:,|\{|;)\s*$",
        RegexOptions.Compiled),
    BodyStyle.Brace,                                         // Brace to capture per-entry class-body overrides
    ReturnTypeGroup: "returnType"),

Kind choice notes:

False-positive containment:

  • Uppercase-only entry names ([A-Z][A-Z0-9_]*) reject ordinary identifiers like Person (mixed-case) and foo (lowercase). Idiomatic Kotlin enum entries are uppercase-with-underscores (NORTH, NOT_FOUND), so this is a tight fit.
  • The ^\s{2,} anchor rejects top-level UPPERCASE_CONST, lines outside class bodies (those are rare in Kotlin anyway — const val is the idiomatic form and is already covered by the val/var row).
  • The closing-token terminator ,|\{|; keeps false positives out of method bodies that might contain uppercase identifier references as standalone statements (which is unusual Kotlin anyway).

Body-range leakage:

The observation that isError's range shows E.kt:13-25 (spanning into Color's body) suggests a separate BodyStyle.Brace scan-past-end bug, visible as a side-effect of the }; trailing an enum entry list followed by abstract fun display(): String. Worth verifying as part of the same fixture; if it persists after adding the enum-entry row, it's a body-range-scanning bug in the extractor that's not Kotlin-specific and deserves its own ticket.

Why it matters

  • symbols NORTH --lang kotlin — zero results today.
  • definition OK --lang kotlin — returns nothing on an HttpStatus.OK lookup.
  • outline on the enum file shows the type range but no entries — same symptoms as the PHP/Swift siblings.
  • Unused / callers / impact all blind to enum-entry references because the entries aren't definitions in the graph to begin with (Kotlin graph is yes per cdidx languages, so the reference-side gap matters here more than for Swift/PHP).
  • Idiomatic Kotlin is enum-heavy. Android Lifecycle.State, Compose RecompositionMode, Kotlin stdlib's own DayOfWeek, Month, Byte.SIZE_BYTES (enum-like constants). Every Kotlin domain model uses enums for state. Missing all of them is a huge navigation gap.

Cross-language note

Enum-entry coverage after this ticket lands:

Language Current After planned fixes
Java ✅ (as function) ✅ (unchanged)
PHP ✅ (via #318)
Swift ✅ (via #319)
Kotlin ✅ (this ticket)
Rust parallel ticket
C#
TypeScript ✅ (via enum row)

Rust enum variants (Ok(T), Err(E), Circle { radius: f64 }, Rectangle(f64, f64)) are a separate novel gap being filed in parallel.

Scope

  • src/CodeIndex/Indexer/SymbolExtractor.cs:186-202 — add one new row for enum entries between the fun row and the val/var row.
  • tests/CodeIndex.Tests/SymbolExtractorTests.cs — fixtures for: (a) plain entry NORTH, captured as property NORTH, (b) parameterized entry OK(200), captured with arg list in return_type, (c) bodied entry RED(0xFF0000) { override fun display() = ... } captured with range spanning the body, (d) the final entry with ; terminator captured, (e) the subsequent fun/abstract fun members still captured correctly (regression for body-range leakage), (f) non-entry uppercase identifiers inside method bodies are NOT captured.
  • DEVELOPER_GUIDE.md — Kotlin language pattern row should list enum entry support.

Related

Environment

  • cdidx v1.10.0 at /root/.local/bin/cdidx (trimmed release build installed via install.sh).
  • Fixture /tmp/dogfood/kt-enum/E.kt as above.
  • cdidx languages shows kotlin with yes / yes in Symbols/Graph — so both symbol-side and reference-side effects of this bug are in scope.
  • Platform: linux-x64 container.
  • Filed from a Claude Code cloud session per CLOUD_BOOTSTRAP_PROMPT.md.

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