-
-
Notifications
You must be signed in to change notification settings - Fork 352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Fix Sniper printer failing to put added import statement on separate line #3702
fix: Fix Sniper printer failing to put added import statement on separate line #3702
Conversation
SniperJavaPrettyPrinter printer = new SniperJavaPrettyPrinter(launcher.getEnvironment()); | ||
printer.setPreprocessors(Collections.unmodifiableList(Arrays.<Processor<CtElement>>asList( | ||
//remove unused imports first. Do not add new imports at time when conflicts are not resolved | ||
new ImportCleaner().setCanAddImports(false), | ||
//solve conflicts, the current imports are relevant too | ||
new ImportConflictDetector(), | ||
//compute final imports | ||
new ImportCleaner() | ||
))); | ||
return printer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed as it interfered with my test case (removed unused imports), and no other test cases actually rely on this functionality. Very unclear why it's here.
I'm not satisfied with this solution, there should be a more general solution
LGTM, unwip? |
I'm not quite satisfied with the solution, it's a corner case solution for import statements, but the problem is essentially an off-by-one error that might have a more general solution. The ones I've tried have unfortunately yielded unwanted whitespace in other places. It may be that this problem is specific to import statements, but I'm not sure. I'm hoping to find a better solution as side effect of working on #3701, in which I'm poking about the exact same parts of the sniper printer. If I don't have a better solution in a couple of days, I'll just make a note of it in the source code and unwip and ping you :) |
@monperrus I thought this was not good enough a fix, and I was right: it doesn't fix the problem if you add an import statement to a project that has a package statement. The problem is an off-by-one error that seemingly only comes into play when adding stuff (any stuff) at the very top of the file. Back to the drawing board, I need to actually fix the off-by-one error :) |
New test case shows the newline fail here |
@monperrus I've finally figured this out. The missing newline for an import following a package statement, and the missing newline between import statements, are really two separate problems. Anyway, here's the rundown. Two import statements ending up on the same lineThis problem is occurs when adding a new import statement, which is printed first in the file, and being immediately followed by a pre-existing import statement. As the pre-existing import statement has index 0 in the fragment child list, any newline events created by the default printer will never be printed. The fix here is to always print newline events when printing the first pre-existing import statement. I think this might affect other child lists than import statements, but we can't unconditionally print newline events at the start of every child list as the default printer sometimes generates unwanted events (e.g. in if/else statements) that we definitely don't want. I think we will simply have to keep adding exceptions to this particular case as they pop up. A newly added import statement ending up on the same line as the package declarationThis occurs when adding a new import statement that is printed after the package declaration. Previously, the The fix was simply to not let Also see some additional comments in the code. Let me know if you want me to split this PR in two, it's technically two independent fixes with similar symptoms. |
CtPackage pkg = compilationUnit.getDeclaredPackage(); | ||
if (pkg != null && !pkg.isUnnamedPackage()) { | ||
printer.writeln(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only difference here, the rest is just indentation. In combination with the fix in visitCtPackageDeclaration
, this fixes newlines after package declarations in the sniper printer.
@@ -1111,7 +1117,7 @@ public void visitCtPackageDeclaration(CtPackageDeclaration packageDeclaration) { | |||
CtPackageReference ctPackage = packageDeclaration.getReference(); | |||
elementPrinterHelper.writeComment(ctPackage, CommentOffset.BEFORE); | |||
if (!ctPackage.isUnnamedPackage()) { | |||
elementPrinterHelper.writePackageLine(ctPackage.getQualifiedName()); | |||
elementPrinterHelper.writePackageStatement(ctPackage.getQualifiedName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the "fix in visitCtPackageDeclaration" I mention.
} else if (event.getRole() == CtRole.DECLARED_IMPORT && fragmentIndex == 0) { | ||
// this is the first pre-existing import statement, and so we must print all newline | ||
// events unconditionally to avoid placing it on the same line as a newly added import | ||
// statement. See PR #3702 for details | ||
printStandardSpaces(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the special handling of the first import statement. We may need to add more roles here as more problems crop up. Using only the condition fragmentIndex == 0
causes unwanted newlines in if/else statements, and perhaps elsewhere as well.
/** | ||
* Write a package statement and a newline. | ||
*/ | ||
public void writePackageLine(String packageQualifiedName) { | ||
writePackageStatement(packageQualifiedName); | ||
printer.writeln(); | ||
} | ||
|
||
/** | ||
* Write a package statement. | ||
*/ | ||
public void writePackageStatement(String packageQualifiedName) { | ||
printer.writeKeyword("package").writeSpace(); | ||
writeQualifiedName(packageQualifiedName).writeSeparator(";").writeln(); | ||
writeQualifiedName(packageQualifiedName).writeSeparator(";"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, I've added the method writePackageStatement
, as I did not want to break the previous API by modifying writePackageLine
.
Excellent work and explanations, thanks a lot @slarse |
You're welcome :). I learned a ton about the sniper printer doing this, so I think I'm ready to get back to tackling #3697 now. Ps. it looks like merging my PR broke the build on master, but I'm pretty sure that wasn't my fault. |
it looks like merging my PR broke the build on master, but I'm pretty sure that wasn't my fault.
yes, it's not your fault, it's a flaky check due to Github API Rate
Limiting, we may remove this low value check.
|
Fix #3698
This is ready for review, see comments further down :)