Skip to content

Commit

Permalink
fix: Fix sniper-printing of method imports (#3744)
Browse files Browse the repository at this point in the history
  • Loading branch information
slarse authored Jan 12, 2021
1 parent 3435fac commit 0d3d34b
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,9 @@ protected void exit(CtElement e) {
*/
private ElementSourceFragment addChild(CtRole roleInParent, SourcePositionHolder otherElement) {
SourcePosition otherSourcePosition = otherElement.getPosition();
if (otherSourcePosition instanceof SourcePositionImpl && !(otherSourcePosition.getCompilationUnit() instanceof NoSourcePosition.NullCompilationUnit)) {
if (otherSourcePosition instanceof SourcePositionImpl && !(otherSourcePosition.getCompilationUnit() instanceof NoSourcePosition.NullCompilationUnit)
// method imports have child type references from other files, see https://github.com/INRIA/spoon/issues/3743
&& fromSameFile(element, otherElement)) {
ElementSourceFragment otherFragment = new ElementSourceFragment(otherElement, this.getRoleHandler(roleInParent, otherElement));
this.addChild(otherFragment);
return otherFragment;
Expand All @@ -245,6 +247,16 @@ private ElementSourceFragment addChild(CtRole roleInParent, SourcePositionHolder
return null;
}

private static boolean fromSameFile(SourcePositionHolder parent, SourcePositionHolder child) {
SourcePosition parentPos = parent.getPosition();
SourcePosition childPos = child.getPosition();

return parentPos.getFile().equals(childPos.getFile())
// we always consider the children of a compilation unit to be from the same file,
// as this is required e.g. to support sniper printing of renamed classes
|| parent instanceof CtCompilationUnit;
}

private RoleHandler getRoleHandler(CtRole roleInParent, SourcePositionHolder otherElement) {
SourcePositionHolder parent = element;
if (parent == null) {
Expand Down
53 changes: 53 additions & 0 deletions src/test/java/spoon/test/prettyprinter/TestSniperPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import spoon.reflect.declaration.CtCompilationUnit;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtImport;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtType;
Expand Down Expand Up @@ -540,6 +541,58 @@ public void testDefaultsToSingleTabIndentationWhenThereAreNoTypeMembers() {
});
}

@Test
public void testPrintTypeWithMethodImportAboveMethodDefinition() {
// contract: The type references of a method import (e.g. its return type) has source
// positions in the file the method was imported from. The resolved source end position
// of the import should not be affected by the placement of the imported method. This
// test ensures this is the case even when the end position of the imported method is
// greater than the end position of the import statement.

Launcher launcher = createLauncherWithSniperPrinter();
launcher.addInputResource(getResourcePath("methodimport.ClassWithStaticMethod"));
launcher.addInputResource(getResourcePath("methodimport.MethodImportAboveImportedMethod"));

CtModel model = launcher.buildModel();
CtType<?> classWithStaticMethodImport = model.getAllTypes().stream()
.filter(type -> type.getSimpleName().endsWith("AboveImportedMethod"))
.findFirst()
.get();

List<CtImport> imports = classWithStaticMethodImport.getFactory().CompilationUnit().getOrCreate(classWithStaticMethodImport).getImports();

String output = launcher
.getEnvironment()
.createPrettyPrinter().printTypes(classWithStaticMethodImport);

assertThat(output, containsString("import static methodimport.ClassWithStaticMethod.staticMethod;"));
}

@Test
public void testPrintTypeWithMethodImportBelowMethodDefinition() {
// contract: The type references of a method import (e.g. its return type) has source
// positions in the file the method was imported from. The resolved source start position
// of the import should not be affected by the placement of the imported method. This
// test ensures this is the case even when the start position of the imported method is
// less than the start position of the import statement.

Launcher launcher = createLauncherWithSniperPrinter();
launcher.addInputResource(getResourcePath("methodimport.ClassWithStaticMethod"));
launcher.addInputResource(getResourcePath("methodimport.MethodImportBelowImportedMethod"));

CtModel model = launcher.buildModel();
CtType<?> classWithStaticMethodImport = model.getAllTypes().stream()
.filter(type -> type.getSimpleName().endsWith("BelowImportedMethod"))
.findFirst()
.get();

String output = launcher
.getEnvironment()
.createPrettyPrinter().printTypes(classWithStaticMethodImport);

assertThat(output, containsString("import static methodimport.ClassWithStaticMethod.staticMethod;"));
}

/**
* 1) Runs spoon using sniper mode,
* 2) runs `typeChanger` to modify the code,
Expand Down
10 changes: 10 additions & 0 deletions src/test/resources/methodimport/ClassWithStaticMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package methodimport;

public class ClassWithStaticMethod {
public static void main(String[] args) {
}

public static void staticMethod() {
System.out.println(42);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package methodimport;
import static methodimport.ClassWithStaticMethod.staticMethod;

/**
* This compilation unit has a method import of the method staticMethod, and the import has a
* lesser source position than the definition of staticMethod has in its respective file.
*/
public class MethodImportAboveImportedMethod {
public static void main(String[] args) {
staticMethod();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package methodimport;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;
import java.util.Set;

import static methodimport.ClassWithStaticMethod.staticMethod;

/**
* This compilation unit has a method import of the method staticMethod, and the import has a
* greater source position than the definition of staticMethod has in its respective file.
*/
public class MethodImportBelowImportedMethod {
public static void main(String[] args) {
staticMethod();
}
}

0 comments on commit 0d3d34b

Please sign in to comment.