Describe the bug
When the sniper printer reprints a type declaration that is annotated with an annotation whose argument contains a class literal (e.g. @Foo(exclude = { String.class })), and any part of the type is modified so the
sniper has to reprint it, the printer silently drops the class keyword of the type declaration:
@Foo(exclude = { String.class })
class Bar { ... }
is reprinted as
@Foo(exclude = { String.class })
Bar { ... }
which is not compilable Java.
Root cause
AbstractSourceFragmentContextCollection.knowsHowToPrint decides whether the current (possibly muted) fragment-collection context owns the next token. For a KEYWORD token it matches the keyword against its child
fragments with a loose substring check:
if (TokenType.KEYWORD.equals(tpe.getTokenType())) {
for (SourceFragment f : childFragments) {
if (f.getSourceCode().contains(tpe.getToken())) { // "String.class".contains("class") == true
return true;
}
}
return false;
}
The source of the annotation-argument fragment String.class contains the substring class. So when the printer reaches the class keyword of the following type declaration, the (muted) annotation collection context
wrongly claims it and, being muted, prints nothing — swallowing the keyword.
Source code you are trying to analyze/transform
@EnableAutoConfiguration(exclude = {
java.lang.String.class,
java.lang.Integer.class
})
class ClassKeywordAnnotation {
static class Nested {
void notFound() {
throw new RuntimeException("boom");
}
}
}
@interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
}
Source code for your Spoon processing
Actual output
@EnableAutoConfiguration(exclude = {
java.lang.String.class,
java.lang.Integer.class
})
ClassKeywordAnnotation {
static class Nested {
void notFound() {
throw new RuntimeException("boom");
}
}
}
@interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
}
Expected output
@EnableAutoConfiguration(exclude = {
java.lang.String.class,
java.lang.Integer.class
})
class ClassKeywordAnnotation {
static class Nested {
void notFound() {
throw new RuntimeException("boom");
}
}
}
@interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
}
Spoon Version
v11.5.0 also in v11.5.1-beta-4
JVM Version
21
What operating system are you using?
Windows 11
Describe the bug
When the sniper printer reprints a type declaration that is annotated with an annotation whose argument contains a class literal (e.g.
@Foo(exclude = { String.class })), and any part of the type is modified so thesniper has to reprint it, the printer silently drops the
classkeyword of the type declaration:is reprinted as
which is not compilable Java.
Root cause
AbstractSourceFragmentContextCollection.knowsHowToPrintdecides whether the current (possibly muted) fragment-collection context owns the next token. For aKEYWORDtoken it matches the keyword against its childfragments with a loose substring check:
The source of the annotation-argument fragment
String.classcontains the substringclass. So when the printer reaches theclasskeyword of the following type declaration, the (muted) annotation collection contextwrongly claims it and, being muted, prints nothing — swallowing the keyword.
Source code you are trying to analyze/transform
Source code for your Spoon processing
Actual output
Expected output
Spoon Version
v11.5.0 also in v11.5.1-beta-4
JVM Version
21
What operating system are you using?
Windows 11