Skip to content

[Roadmap] Enhancing KMP Tooling, Semantic Diagnostics, and Smart Type Systems #138

Description

@ulite-Amr

Context & Goals

The current Indexer architecture in kotlin-lsp-rs is incredibly fast, memory-safe, and highly concurrent. However, to elevate this LSP into a robust IDE-grade developer experience, we need to bridge the gap between fast autocomplete and true Semantic Analysis & Static Type Safety.

I am planning to implement a comprehensive suite of features over the next few days to introduce full diagnostic reporting (red squiggly lines), advanced cross-platform KMP navigation, and smart casting.

Below is the detailed architectural roadmap of what I am currently working on.


🗺️ Engineering Roadmap & Feature Specs

1. Cross-Platform KMP Navigation (expect / actual CodeLens) ✅ COMPLETED

  • The Problem: Navigating Kotlin Multiplatform (KMP) codebases between commonMain definitions and native target implementations (androidMain, iosMain) is friction-heavy.
  • The Solution: Implement an LSP textDocument/codeLens provider.
  • Rust Implementation: Our Indexer already parses fully-qualified names globally. We will traverse the file's AST looking for symbols marked with expect or actual. The CodeLens provider will query the qualified map in the Indexer to resolve cross-module links and overlay virtual clickable text (└── ◆ iOS Actual | ▣ Android Actual). Clicking it invokes textDocument/definition via a resolved Location link.
  • Extras:
    • Neovim/Helix support via window/showDocument fallback in execute_command
    • Dynamic modifier verification — live-tree parse confirms counterpart carries the expected modifier
    • Missing-actual diagnostics — warns when a platform source set lacks an actual implementation for a declared expect
    • One-click code action — creates missing actual files with TODO body and post-creation navigation
    • Platform groups simplified to match real KMP default hierarchy (10 groups: common, android, apple, ios, jvm, js, native, macos, linux, wasm)

2. Dead Code Elimination (Unused Symbols Diagnostic)

  • The Problem: Dead code and stale imports reduce project maintainability and performance.
  • The Solution: Emit DiagnosticSeverity::HINT on unused variables, functions, and imports so editors can grey them out, coupled with a CodeAction to remove them.
  • Rust Implementation:
    During the AST traversal pass of a single file, we will collect all internal declarations into a HashSet. As the file walker parses the expression blocks, any identifier hit will be removed from the HashSet. At the end of the document pass, the remaining elements in the set represent dead code, triggering a hint diagnostic.

3. Smart Casts & Scope-Aware Type Overrides

  • The Problem: Kotlin relies heavily on smart casts (e.g., if (obj is String) { ... }). Outside that block, using it as a subclass should throw a type error, while inside, its type must be dynamically upgraded.
  • The Solution: Introduce a lightweight scope-tracking mechanism or a micro-Control Flow Graph (CFG) inside our diagnostic pass.
  • Rust Implementation:
    When evaluating an if_expression containing an is_expression condition, the local semantic resolver will inject a temporary type override inside that specific block scope. Upon reaching the end node (}) of the block, the type variable falls back to its original supertype (Any). Accessing subclass methods outside the scope will trigger an unresolved method diagnostic.

4. Unresolved Supertypes & Inheritance Restrictions

  • The Problem: In Kotlin, classes are final by default. Inheriting from a closed class without an open, abstract, or interface modifier is a compilation error.
  • The Solution: Emit a compiler error diagnostic when a class attempts to extend a non-open parent.
  • Rust Implementation:
    When the parser encounters a class inheritance node (e.g., class MyIntent : Intent()), the engine will query the Indexer for the Intent superclass declaration. We will inspect its cached modifiers. If it lacks open/abstract/interface, we will emit: This type is final, so it cannot be inherited from.

📈 Current Progress & PR Tracking

To keep everything transparent and prevent any duplication of work, here is the current status of my open pull requests and active contributions:

  • [OPEN PR] Mutability Enforcement: * PR: #137 feat: add val-reassignment diagnostics via live-CST walk
    • Spec: Flags assignment_expression mutations where the left-hand side identifier resolves to a read-only val property (is_writable == false).
  • [OPEN PR] Code Generation & Refactoring: * PR: #134 feat:Generate code actions: getters, setters, and override methods
    • Spec: Introduces Quick Fix/Code Actions to automatically generate standard Boilerplate Getters, Setters, and unimplemented/override methods for class properties.
  • [OPEN PR] Supertype Fallbacks: * PR: #132 feat:(generate_constructor): add resolver fallback chain for unindexed supertypes
    • Spec: Resolves empty constructor/initialization chains safely even when dealing with unindexed external supertypes.
  • [COMPLETED] goToDefinition Scope Resolution Fix:
    • Status: Resolved in feat/kmp-codelens branch.
    • Spec: Fixed an edge case where invoking goToDefinition incorrectly jumped to a variable sharing the exact same name within the same file, even though it belonged to an entirely different, isolated scope.
  • [NEW PR] KMP CodeLens + Diagnostics + Code Action: * Branch: feat/kmp-codelens
    • Status: CodeLens navigation for expect/actual across all KMP source sets, missing-actual diagnostics, one-click file creation code action, Neovim support, platform groups matching real KMP hierarchy. All 1201 tests pass, clippy clean.

Next Steps & Feedback

I will be rolling out the implementations for Smart Casts and Unused Diagnostics incrementally over the next couple of days.

@Hessesian — Please let me know your thoughts on this roadmap or if you have specific architectural preferences before I submit the next batch of PRs. Let's make this LSP absolute lightning! ⚡

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