Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Fix sniper printer failing to prepend whitespace to top-added Ct…
Browse files Browse the repository at this point in the history
…Modifiable (INRIA#3856)
slarse authored and woutersmeenk committed Aug 29, 2021
1 parent e40ea24 commit fc49afa
Showing 2 changed files with 71 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -277,8 +277,9 @@ protected int findIndexOfNextChildTokenOfElement(SourcePositionHolder element) {
protected int findIFragmentIndexCorrespondingToEvent(PrinterEvent event) {
CtRole role = event.getRole();
if (role != null) {
if (event.getElement() instanceof CtModifiable || role == CtRole.MODIFIER) {
// using only roles for handling modifiers correctly
if ((event.getElement() instanceof CtModifiable && event.getElement().getPosition().isValidPosition())
|| role == CtRole.MODIFIER) {
// using only roles for handling modifiers and preexisting modifiables correctly
return findIndexOfNextChildTokenOfRole(childFragmentIdx + 1, role);
}
return findIndexOfNextChildTokenOfElement(event.getElement());
68 changes: 68 additions & 0 deletions src/test/java/spoon/test/prettyprinter/TestSniperPrinter.java
Original file line number Diff line number Diff line change
@@ -414,6 +414,74 @@ public void testCalculateCrashesWithInformativeMessageWhenSniperPrinterSetAfterM
}
}

@Test
public void testWhitespacePrependedToFieldAddedAtTop() {
assumeNotWindows(); // FIXME Make test case pass on Windows
// contract: newline and indentation must be inserted before a field that's added to the top
// of a class body when the class already has other type members.

Consumer<CtType<?>> addFieldAtTop = type -> {
Factory fact = type.getFactory();
CtField<?> field = fact.createCtField(
"newFieldAtTop", fact.Type().INTEGER_PRIMITIVE, "2");
type.addFieldAtTop(field);
};

final String expectedFieldSource = "int newFieldAtTop = 2;";
BiConsumer<CtType<?>, String> assertTopAddedFieldOnSeparateLine = (type, result) ->
assertThat(result, containsString("{\n " + expectedFieldSource));

// it doesn't matter which test resource is used, as long as it has a non-empty class
String nonEmptyClass = "TypeMemberComments";
testSniper(nonEmptyClass, addFieldAtTop, assertTopAddedFieldOnSeparateLine);
}

@Test
public void testWhitespacePrependedToNestedClassAddedAtTop() {
assumeNotWindows(); // FIXME Make test case pass on Windows
// contract: newline and indentation must be inserted before a nested class that's added to
// the top of a class body when the class already has other type members.

Consumer<CtType<?>> addNestedClassAtTop = type -> {
CtClass<?> nestedClass = type.getFactory().createClass("Nested");
type.addTypeMemberAt(0, nestedClass);
};

final String expectedClassSource = "class Nested {}";
BiConsumer<CtType<?>, String> assertTopAddedClassOnSeparateLine = (type, result) ->
assertThat(result, containsString("{\n " + expectedClassSource));

// it doesn't matter which test resource is used, as long as it has a non-empty class
String nonEmptyClass = "TypeMemberComments";
testSniper(nonEmptyClass, addNestedClassAtTop, assertTopAddedClassOnSeparateLine);
}

@Test
public void testWhitespacePrependedToLocalVariableAddAtTopOfNonEmptyMethod() {
assumeNotWindows(); // FIXME Make test case pass on Windows
// contract: newline and indentation must be inserted before a local variable that's added
// to the top of a non-empty statement list.

Consumer<CtType<?>> addLocalVariableAtTopOfMethod = type -> {
Factory factory = type.getFactory();
CtMethod<?> method = type.getMethods().stream()
.filter(m -> !m.getBody().getStatements().isEmpty())
.findFirst()
.get();
CtLocalVariable<?> localVar = factory.createLocalVariable(
factory.Type().INTEGER_PRIMITIVE, "localVar", factory.createCodeSnippetExpression("2"));
method.getBody().addStatement(0, localVar);
};

final String expectedVariableSource = "int localVar = 2;";
BiConsumer<CtType<?>, String> assertTopAddedVariableOnSeparateLine = (type, result) ->
assertThat(result, containsString("{\n " + expectedVariableSource));

// the test resource must have a class with a non-empty method
String classWithNonEmptyMethod = "methodimport.ClassWithStaticMethod";
testSniper(classWithNonEmptyMethod, addLocalVariableAtTopOfMethod, assertTopAddedVariableOnSeparateLine);
}

@Test
public void testNewlineInsertedBetweenCommentAndTypeMemberWithAddedModifier() {
assumeNotWindows(); // FIXME Make test case pass on Windows

0 comments on commit fc49afa

Please sign in to comment.