Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/java/spoon/reflect/declaration/CtImportKind.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum CtImportKind {
ALL_STATIC_MEMBERS, // import static my.package.Type.*;
FIELD, // import static my.package.Type.f;
METHOD, // import static my.package.Type.m;
UNRESOLVED // Any of the above when in mode no classpath and the reference cannot be resolved.
UNRESOLVED, // Any of the above when in mode no classpath and the reference cannot be resolved.
// It is then stored as a pure String that will be printed as is when pretty printed.
MODULE
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import spoon.experimental.CtUnresolvedImport;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtModuleReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtTypeMemberWildcardImportReference;
Expand Down Expand Up @@ -41,4 +42,8 @@ public <T> void visitAllStaticMembersImport(CtTypeMemberWildcardImportReference
@Override
public <T> void visitUnresolvedImport(CtUnresolvedImport unresolvedImport) {
}

@Override
public void visitModuleImport(CtModuleReference moduleReference) {
}
}
8 changes: 8 additions & 0 deletions src/main/java/spoon/reflect/visitor/CtImportVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import spoon.experimental.CtUnresolvedImport;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtModuleReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtTypeMemberWildcardImportReference;
Expand Down Expand Up @@ -48,4 +49,11 @@ public interface CtImportVisitor {
* Called for unresolved import
*/
<T> void visitUnresolvedImport(CtUnresolvedImport ctUnresolvedImport);

/**
* Called for import like:
* <code>import module my.module.name;</code>
* @param moduleReference the module reference
*/
void visitModuleImport(CtModuleReference moduleReference);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,13 @@ public <T> void visitUnresolvedImport(CtUnresolvedImport ctUnresolvedImport) {
}
printer.writeCodeSnippet(ctUnresolvedImport.getUnresolvedReference());
}

@Override
public void visitModuleImport(CtModuleReference moduleReference) {
printer.writeKeyword("module");
printer.writeSpace();
printer.writeIdentifier(moduleReference.getSimpleName());
}
});
printer.writeSeparator(";");
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/spoon/reflect/visitor/ImportCleaner.java
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,11 @@ void onCompilationUnitProcessed(CtCompilationUnit compilationUnit) {
ModelList<CtImport> existingImports = compilationUnit.getImports();
Set<CtImport> computedImports = new HashSet<>(this.computedImports.values());
topfor: for (CtImport oldImport : new ArrayList<>(existingImports)) {
if (oldImport.getImportKind() == CtImportKind.MODULE) {
//never remove module imports
continue;
}
Comment on lines 267 to 270
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is a conservative solution, i.e., removing such an import would be legal/might make sense in some cases (e.g., if we're in the same module?).

In that case, I think the comment should reflect that a bit better, e.g., "be conservative: do not remove any module imports". This should help if anyone ever tries to do more here.

if (!removeImport(oldImport, computedImports)) {

// case: import is required in Javadoc
for (CtType type: compilationUnit.getDeclaredTypes()) {
for (CtJavaDoc element: type.getElements(new TypeFilter<>(CtJavaDoc.class))) {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/spoon/reflect/visitor/TypeNameScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtModuleReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtTypeMemberWildcardImportReference;
import spoon.reflect.reference.CtTypeReference;
Expand Down Expand Up @@ -164,6 +165,11 @@ public <T> void visitAllStaticMembersImport(CtTypeMemberWildcardImportReference
public <T> void visitUnresolvedImport(CtUnresolvedImport ctUnresolvedImport) {
//there is no usable type member under unresolved import
}

@Override
public void visitModuleImport(CtModuleReference moduleReference) {
//modules are not relevant for type members
}
});
});
//names of all types of same package are visible too, but with lower priority then explicitly imported elements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@

import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import for StringUtils is added but not used anywhere in the file. This unused import should be removed.

Copilot uses AI. Check for mistakes.
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;

import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.declaration.CtImport;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtModule;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtType;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtModuleReference;
import spoon.reflect.reference.CtReference;

import java.util.Arrays;
Expand Down Expand Up @@ -73,6 +76,12 @@ void build() {
}
}

} else if ((importRef.modifiers & ClassFileConstants.AccModule) != 0) { // this check is copied from JDT's ImportReference
// import of a module
String moduleName = JDTTreeBuilderHelper.createQualifiedTypeName(importRef.getImportName());
CtModule ctModule = factory.Module().getOrCreate(moduleName);
CtModuleReference moduleRef = factory.Module().createReference(ctModule).setSimpleName(moduleName);
this.imports.add(createImportWithPosition(moduleRef, importRef));
} else {
CtType klass = this.getOrLoadClass(importName);
if (klass != null) {
Expand Down
37 changes: 12 additions & 25 deletions src/main/java/spoon/support/reflect/declaration/CtImportImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.declaration.CtImport;
import spoon.reflect.reference.CtModuleReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtReference;
import spoon.reflect.declaration.CtImportKind;
Expand Down Expand Up @@ -50,8 +51,10 @@ private CtImportKind getImportKindFor(CtReference ref) {
return CtImportKind.ALL_STATIC_MEMBERS;
} else if (ref instanceof CtTypeReference) {
return CtImportKind.TYPE;
} else if (ref instanceof CtModuleReference) {
return CtImportKind.MODULE;
} else {
throw new SpoonException("Only CtFieldReference, CtExecutableReference, CtPackageReference and CtTypeReference are accepted reference types. Given " + ref.getClass());
throw new SpoonException("Only CtFieldReference, CtExecutableReference, CtPackageReference, CtTypeReference and CtModuleReference are accepted reference types. Given " + ref.getClass());
}
}

Expand Down Expand Up @@ -82,30 +85,14 @@ public void accept(CtVisitor visitor) {
@Override
public void accept(CtImportVisitor visitor) {
switch (getImportKind()) {
case TYPE:
visitor.visitTypeImport((CtTypeReference<?>) localReference);
break;

case METHOD:
visitor.visitMethodImport((CtExecutableReference<?>) localReference);
break;

case FIELD:
visitor.visitFieldImport((CtFieldReference<?>) localReference);
break;

case ALL_TYPES:
visitor.visitAllTypesImport((CtPackageReference) localReference);
break;

case ALL_STATIC_MEMBERS:
visitor.visitAllStaticMembersImport((CtTypeMemberWildcardImportReference) localReference);
break;
case UNRESOLVED:
visitor.visitUnresolvedImport((CtUnresolvedImport) localReference);
break;
default:
throw new SpoonException("Unexpected import kind: " + getImportKind());
case TYPE -> visitor.visitTypeImport((CtTypeReference<?>) localReference);
case METHOD -> visitor.visitMethodImport((CtExecutableReference<?>) localReference);
case FIELD -> visitor.visitFieldImport((CtFieldReference<?>) localReference);
case ALL_TYPES -> visitor.visitAllTypesImport((CtPackageReference) localReference);
case ALL_STATIC_MEMBERS -> visitor.visitAllStaticMembersImport((CtTypeMemberWildcardImportReference) localReference);
case MODULE -> visitor.visitModuleImport((CtModuleReference) localReference);
case UNRESOLVED -> visitor.visitUnresolvedImport((CtUnresolvedImport) localReference);
default -> throw new SpoonException("Unexpected import kind: " + getImportKind());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
Expand Down Expand Up @@ -39,6 +41,7 @@
import spoon.support.reflect.reference.CtArrayTypeReferenceImpl;
import spoon.test.SpoonTestHelpers;
import spoon.testing.assertions.SpoonAssertions;
import spoon.testing.utils.BySimpleName;
import spoon.testing.utils.GitHubIssue;
import spoon.testing.utils.ModelTest;

Expand Down Expand Up @@ -474,4 +477,12 @@ void printAnnotationsInOrphanTypeReference(Factory factory) {
private @interface TypeUseAnnotation {

}

@ModelTest(value = {"src/test/resources/imports/ModuleImport.java"}, autoImport = true, complianceLevel = 25)
void moduleImportsArePrinted(@BySimpleName("ModuleImport") CtType<?> type, Launcher launcher) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already forgot about the @BySimpleName annotation again, but it definitely makes the test cleaner. Can you add a // contract: comment though?

PrettyPrinter prettyPrinter = launcher.createPrettyPrinter();
String output = prettyPrinter.prettyprint(type.getPosition().getCompilationUnit());
Assertions.assertThat(output).contains("import module java.base");
}

}
Loading
Loading