Skip to content

[Bug]: Exact import is ignored: simple name in implements binds to same-package class #6867

Description

@wang422403

Describe the bug

When a compilation unit has:

  1. a same-package type with simple name Foo, and
  2. a single-type import of a different Foo (other.Foo),
    Spoon resolves the simple name Foo in implements Foo to the same-package class, not to the imported type.
    This contradicts JLS 6.4.1: a single-type-import of n shadows a top-level type named n declared in another compilation unit of the same package.
    We hit this in noclasspath mode (Spoon 11.5.1-beta-5). The imported interface is often not in the model (external jar). Spoon still binds implements Foo to the same-package class, so getTypeDeclaration() is non-null and looks like a successful resolve.
    Related: Problem with qualified names of CtTypeReference when using setNoClasspath(true) #465 (noclasspath implements / parameters not tied to imports). This case is sharper: a same-package class steals the binding, so callers cannot tell the resolve failed.

Source code you are trying to analyze/transform

package demo;
public class Foo {
    public void control(String a) {}
}

package demo;
import other.Foo;
public class FooImpl implements Foo {
    public void control(String a) {}
    public void extra() {}
}

package other;
public interface Foo {
    void control(String a);
    void extra();
}

Source code for your Spoon processing

Launcher launcher = new Launcher();
launcher.getEnvironment().setNoClasspath(true);
launcher.getEnvironment().setShouldCompile(false);
launcher.addInputResource("src"); // both demo/ and other/ (or omit other/ to mimic missing jar)
launcher.buildModel();
CtType<?> impl = launcher.getFactory().Type().get("demo.FooImpl");
CtTypeReference<?> iface = impl.getSuperInterfaces().iterator().next();
System.out.println(iface.getQualifiedName());
System.out.println(iface.getTypeDeclaration() == null
        ? "null"
        : iface.getTypeDeclaration().getQualifiedName()
            + " isInterface=" + iface.getTypeDeclaration().isInterface());

Actual output

iface.getQualifiedName() is demo.Foo
getTypeDeclaration() returns the same-package class demo.Foo

Expected output

iface.getQualifiedName() is other.Foo
declaration is the imported interface, or null if other.Foo is not in the model
it must not be demo.Foo
javac already treats implements Foo as other.Foo because of the import.

Spoon Version

Spoon: 11.5.1-beta-5 (fr.inria.gforge.spoon:spoon-core)
Mode: setNoClasspath(true), empty sourceClasspath, setShouldCompile(false)
JDK used to run Spoon: 17+ (sources under analysis are Java 8)

JVM Version

17

What operating system are you using?

macOS

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions