Skip to content

shortenFullyQualifiedTypes corrupts method references on fields (x.field::m -> bogus import x.field) #3133

Closed

Description

@maxandersen

Summary

shortenFullyQualifiedTypes() (added in #3005) corrupts a method reference on a field of a local variable. It treats localVar.field::method as if localVar.field were a fully-qualified type, shortens it to field::method, and injects a bogus import localVar.field; — which does not compile.

Because it only manifests on spotlessApply (not the read-only spotlessCheck), a developer runs spotlessApply, commits, and then the project no longer builds.

Version

  • com.diffplug.spotless 8.10.3 (Gradle plugin)
  • JDK 11 / 17 (reproduces on both)

Minimal reproducer

build.gradle:

plugins {
    id 'java'
    id 'com.diffplug.spotless' version '8.10.3'
}
repositories { mavenCentral() }
spotless { java { shortenFullyQualifiedTypes() } }

src/main/java/Example.java:

import java.util.Map;
import java.util.function.Function;

public class Example {
    static class Catalog {
        Map<String, String> aliases;
    }

    String demo(Catalog catalog) {
        return find(catalog.aliases::get);   // method ref on a field of a local var
    }

    String find(Function<String, String> f) {
        return f.apply("x");
    }
}

Steps

./gradlew spotlessApply
./gradlew compileJava

Actual

spotlessApply rewrites the file to:

import java.util.Map;
import java.util.function.Function;
import catalog.aliases;          // <-- bogus import of a non-existent package/type

public class Example {
    ...
    String demo(Catalog catalog) {
        return find(aliases::get);   // <-- qualifier dropped, now unresolved
    }
    ...
}

and compilation fails:

Example.java: error: package catalog does not exist

Expected

catalog.aliases::get is a method reference on a field access of a local variable, not a fully-qualified type reference. It must be left untouched (no shortening, no import added).

Root cause (guess)

The qualifier-resolution doesn't verify that the left-hand side of a qualified name / method-reference scope actually resolves to a type before shortening. Field accesses on locals/params (x.y, x.y::m, x.y.z) are being misclassified as fully-qualified type names. Checking that the qualifier is a type (not a local variable / field expression) before rewriting should fix it.

Found while enabling the step on a real codebase (jbangdev/jbang), where catalog.aliases::get (a Catalog local + Map field) broke the build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions