diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 65c76aee3b..60e9dc3902 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -578,17 +578,23 @@ class JavaParserImpl { prev.setLastToken(top.getLastToken()); } - /** + /** * Keeps track during tree construction, whether we are currently building an * explicit constructor invocation. Then the PrimaryExpression that may prefix * a qualified super constructor call may not consume "super" tokens. */ private boolean inExplicitConstructorInvoc = false; + /** + * Keeps track of the current token context. This is needed to resolve the ambiguities around String Templates: + * A closing bracket (RBRACE) might close a block or might be the start of a STRING_TEMPLATE_MID/END token. + * + * @see JEP 430: String Templates (Preview) + */ private Deque tokenContexts = new ArrayDeque(); TokenContext determineTokenContext() { if (tokenContexts.isEmpty()) { - return null; + return TokenContext.BLOCK; } return tokenContexts.peek(); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java21TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java21TreeDumpTest.java index 162bcfd770..0dbcaf725a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java21TreeDumpTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java21TreeDumpTest.java @@ -122,4 +122,9 @@ class Java21TreeDumpTest extends BaseTreeDumpTest { void recordPatternsExhaustiveSwitch() { doTest("RecordPatternsExhaustiveSwitch"); } + + @Test + void canParseAnnotationValueInitializers() { + doTest("AnnotationValueInitializers"); + } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/AnnotationValueInitializers.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/AnnotationValueInitializers.java new file mode 100644 index 0000000000..11bf9cc322 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/AnnotationValueInitializers.java @@ -0,0 +1,13 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/* + * Tests parsing after supporting String Templates. "}" is ambiguous. + */ + +@MyAnnotation(a = { "a" }, b = "b") // "}" might be recognized as STRING_TEMPLATE_END, but it is not +class AnnotationValueInitializers { } + +@MyAnnotation(a = { "a" }, b = "#b") // "}" might be recognized as STRING_TEMPLATE_END, but it is not +class AnnotationValueInitializers2 { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/AnnotationValueInitializers.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/AnnotationValueInitializers.txt new file mode 100644 index 0000000000..93deaccbde --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/AnnotationValueInitializers.txt @@ -0,0 +1,23 @@ ++- CompilationUnit[@PackageName = ""] + +- ClassOrInterfaceDeclaration[@Abstract = false, @Annotation = false, @Anonymous = false, @BinaryName = "AnnotationValueInitializers", @CanonicalName = "AnnotationValueInitializers", @EffectiveVisibility = Visibility.V_PACKAGE, @Enum = false, @Final = false, @Image = "AnnotationValueInitializers", @Interface = false, @Local = false, @Native = false, @Nested = false, @PackageName = "", @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Record = false, @RegularClass = true, @RegularInterface = false, @SimpleName = "AnnotationValueInitializers", @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @TopLevel = true, @Transient = false, @Visibility = Visibility.V_PACKAGE, @Volatile = false] + | +- ModifierList[] + | | +- Annotation[@AnnotationName = "MyAnnotation", @SimpleName = "MyAnnotation"] + | | +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "MyAnnotation", @TypeImage = "MyAnnotation"] + | | +- AnnotationMemberList[@Empty = false, @Size = 2] + | | +- MemberValuePair[@Image = "a", @Name = "a", @Shorthand = false] + | | | +- MemberValueArrayInitializer[] + | | | +- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "a", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"a\"", @IntLiteral = false, @Length = 1, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false] + | | +- MemberValuePair[@Image = "b", @Name = "b", @Shorthand = false] + | | +- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "b", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"b\"", @IntLiteral = false, @Length = 1, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false] + | +- ClassOrInterfaceBody[@Empty = true, @Size = 0] + +- ClassOrInterfaceDeclaration[@Abstract = false, @Annotation = false, @Anonymous = false, @BinaryName = "AnnotationValueInitializers2", @CanonicalName = "AnnotationValueInitializers2", @EffectiveVisibility = Visibility.V_PACKAGE, @Enum = false, @Final = false, @Image = "AnnotationValueInitializers2", @Interface = false, @Local = false, @Native = false, @Nested = false, @PackageName = "", @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Record = false, @RegularClass = true, @RegularInterface = false, @SimpleName = "AnnotationValueInitializers2", @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @TopLevel = true, @Transient = false, @Visibility = Visibility.V_PACKAGE, @Volatile = false] + +- ModifierList[] + | +- Annotation[@AnnotationName = "MyAnnotation", @SimpleName = "MyAnnotation"] + | +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "MyAnnotation", @TypeImage = "MyAnnotation"] + | +- AnnotationMemberList[@Empty = false, @Size = 2] + | +- MemberValuePair[@Image = "a", @Name = "a", @Shorthand = false] + | | +- MemberValueArrayInitializer[] + | | +- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "a", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"a\"", @IntLiteral = false, @Length = 1, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false] + | +- MemberValuePair[@Image = "b", @Name = "b", @Shorthand = false] + | +- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "#b", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"#b\"", @IntLiteral = false, @Length = 2, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false] + +- ClassOrInterfaceBody[@Empty = true, @Size = 0]