Skip to content

Refine the hint at uninitialized properties of a failed snippet #165

Open

Description

@morisil

Follow-up of #144, found by the review of its fix (none of it blocks the merge, all of it caused by the change).

  1. A false hint for a property null on purpose: UninitializedProperties.keep records every null static field of a failed snippet,
    so val p = System.getProperty("no.such"); error("boom"), then p.length, says p "might be left uninitialized … declare it again" — which gives null again.
    Record only the properties whose getter carries @org.jetbrains.annotations.NotNull (Kotlin puts it on non-null types, not on platform or nullable ones).
  2. A lambda or anonymous object reading the property is not named: the hint uses only the first snippet frame, which for fun greet() = lazy { name.uppercase() }.value is <lambda>. Use the first frame with a declaration name.
  3. A transitive caller gets the same hint every time: with fun wrapper() = greet() in snippet2, redeclaring both name and greet still leaves wrapper() failing with the hint naming 'greet' of snippet1.kts — a possible loop. Mention the calling snippet frames too.
  4. A custom getter is named by its JVM name: val t: Int get() = name.length is named 'getT'; map get… / is… / set… frames back to the property name.
  5. A fatal error skips the recording: keep() runs after scriptResult, which rethrows a fatal VirtualMachineError, so the declarations stay in the history unrecorded. Record first.
  6. Smaller gaps:
    • @get:JvmName("text") gets no hint (the NPE names Snippet1.text());
    • passing the property to a function fails Kotlin's parameter check (Parameter specified as non-null is null) without a hint;
    • for a var, the hint says to declare it again, while assigning it is enough.
  7. Docs: the README and the KDoc say there is no hint without helpful NPE messages, but a delegated property still gets one (found by its stack frame); the cases of 6 are not listed.
  8. Possible simplifications:
    • getterName copies the compiler's naming rules — org.jetbrains.kotlin.load.java.JvmAbi.getterName might be on the classpath (not verified);
    • a different design: name the uninitialized properties in the failing snippet's own EVALUATION error, when keep() collects them — covering also primitives and values copied before the read, without parsing NPE messages.

Not caused by the change: the second access to an object whose initializer failed throws NoClassDefFoundError: Could not initialize class Config, without a hint, and only redeclaring the object helps.

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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions