diff --git a/src/main/java/se/kth/spork/cli/Cli.java b/src/main/java/se/kth/spork/cli/Cli.java index 7a7fcc01..a528eb88 100644 --- a/src/main/java/se/kth/spork/cli/Cli.java +++ b/src/main/java/se/kth/spork/cli/Cli.java @@ -23,6 +23,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; +import java.util.stream.Collectors; /** * Command line interface for Spork. @@ -45,8 +46,6 @@ public static void main(String[] args) { */ public static String prettyPrint(CtModule spoonRoot) { LOGGER.info("Pre-processing tree for pretty-printing"); - new PrinterPreprocessor().scan(spoonRoot); - CtPackage activePackage = spoonRoot.getRootPackage(); while (!activePackage.getPackages().isEmpty()) { Set subPkgs = activePackage.getPackages(); @@ -60,12 +59,19 @@ public static String prettyPrint(CtModule spoonRoot) { activePackage = pkgOpt.get(); } + Collection imports = (Collection) spoonRoot.getMetadata(Parser.IMPORT_STATEMENTS); + List importNames = imports.stream() + .map(Object::toString) + .map(impStmt -> impStmt.substring("import ".length(), impStmt.length() - 1)) + .collect(Collectors.toList()); + new PrinterPreprocessor(importNames, activePackage.getQualifiedName()).scan(spoonRoot); + + StringBuilder sb = new StringBuilder(); if (!activePackage.isUnnamedPackage()) { sb.append("package ").append(activePackage.getQualifiedName()).append(";").append("\n\n"); } - Collection imports = (Collection) spoonRoot.getMetadata(Parser.IMPORT_STATEMENTS); for (Object imp : imports) { sb.append(imp).append("\n"); } diff --git a/src/main/java/se/kth/spork/cli/PrinterPreprocessor.java b/src/main/java/se/kth/spork/cli/PrinterPreprocessor.java index 3d604099..284b2669 100644 --- a/src/main/java/se/kth/spork/cli/PrinterPreprocessor.java +++ b/src/main/java/se/kth/spork/cli/PrinterPreprocessor.java @@ -6,13 +6,13 @@ import se.kth.spork.spoon.RoledValue; import se.kth.spork.util.LineBasedMerge; import se.kth.spork.util.Pair; -import spoon.reflect.code.CtComment; import spoon.reflect.code.CtOperatorAssignment; -import spoon.reflect.code.UnaryOperatorKind; import spoon.reflect.cu.SourcePosition; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.ModifierKind; import spoon.reflect.path.CtRole; +import spoon.reflect.reference.CtPackageReference; +import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.CtScanner; import spoon.reflect.visitor.printer.CommentOffset; @@ -33,11 +33,22 @@ public class PrinterPreprocessor extends CtScanner { // in a merged tree public static final String POSITION_KEY = "spork_position"; + private final List importStatements; + private final String activePackage; + + public PrinterPreprocessor(List importStatements, String activePackage) { + this.importStatements = importStatements; + this.activePackage = activePackage; + } + @Override public void scan(CtElement element) { if (element == null) return; + // FIXME Temporary fix for bug in Spoon. See method javadoc. Remove once fixed in Spoon. + handleIncorrectExplicitPackages(element); + @SuppressWarnings("unchecked") List conflicts = (List) element.getMetadata(ContentConflict.METADATA_KEY); @@ -50,15 +61,51 @@ public void scan(CtElement element) { super.scan(element); } + /** + * There's a bug in Spoon that causes packages that should be implicit to be explicit. There's another bug + * that sometimes attaches the wrong package to references that don't have an explicit package in the source + * code. This method attempts to mark all such occasions of packages implicit, so they are not reflected in the + * final output. + * + * See https://github.com/kth/spork/issues/94 + * + * For each package reference attached to a type reference, mark as implicit if: + * + * 1. The package reference refers to the package of the current compilation unit. + * 2. The type has been explicitly imported. + * 3. All types in the package have been imported with a * + * + * @param element An element. + */ + private void handleIncorrectExplicitPackages(CtElement element) { + if (element instanceof CtPackageReference) { + String pkgName = ((CtPackageReference) element).getQualifiedName(); + CtElement parent = element.getParent(); + if (pkgName.equals(activePackage)) { + element.setImplicit(true); + } else if (parent instanceof CtTypeReference) { + String parentQualName = ((CtTypeReference) parent).getQualifiedName(); + + for (String imp : importStatements) { + if (imp.equals(parentQualName) || imp.endsWith("*") + && pkgName.equals(imp.substring(0, imp.length() - 2))) { + element.setImplicit(true); + break; + } + } + } + } + } + /** * Comments that come from a different source file than the node they are attached to are unlikely to actually * get printed, as the position relative to the associated node is taken into account by the pretty-printer. * Setting the position to {@link SourcePosition#NOPOSITION} causes all comments to be printed before the * associated node, but at least they get printed! - * + *

* The reason for this can be found in * {@link spoon.reflect.visitor.ElementPrinterHelper#getComments(CtElement, CommentOffset)}. - * + *

* If the position is all ready {@link SourcePosition#NOPOSITION}, then do nothing. */ private static void unsetSourcePosition(CtElement element) { @@ -74,7 +121,7 @@ private static void unsetSourcePosition(CtElement element) { * as strings may have the conflict embedded directly into the literal. * * @param conflict A content conflict. - * @param element The element associated with the conflict. + * @param element The element associated with the conflict. */ @SuppressWarnings("unchecked") private void processConflict(ContentConflict conflict, CtElement element) {