[Bug]: Exact import is ignored: simple name in `implements` binds to same-package class
- Dominant language
- Java
- Stars
- 2k
- Forks
- 392
- Avg merge
- 11h 24m
- Merged PRs (30d)
- 36
Description
### 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: #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
```Java
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
```Java
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
```Java
iface.getQualifiedName() is demo.Foo
getTypeDeclaration() returns the same-package class demo.Foo
```
### Expected output
```Java
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
Contributor guide
Research direction
Start by running the provided Launcher reproduction with noClasspath enabled and inspect the reference returned by getSuperInterfaces(). Check how getTypeDeclaration() resolves the simple name in this import and same-package scenario. Done means the reference resolves to other.Foo when available, or null when it is absent, and never to demo.Foo.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100