diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index c9dcdd7fa7..93428c162a 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -478,6 +478,21 @@ public class JavaParser { return (jdkVersion == 14 || jdkVersion == 15) && preview; } + private boolean isRecordDeclarationAhead() { + int amount = 1; + while (amount < 10) { + Token next = getToken(amount); + if (next.kind == IDENTIFIER && "record".equals(next.image)) { + return true; + } + // stop looking ahead at "=" or ";" or "{" + if (next.kind == ASSIGN || next.kind == LBRACE || next.kind == SEMICOLON) { + return false; + } + amount++; + } + return false; + } // This is a semantic LOOKAHEAD to determine if we're dealing with an assert // Note that this can't be replaced with a syntactic lookahead @@ -1886,10 +1901,10 @@ void BlockStatement(): LOOKAHEAD( { isNextTokenAnAssert() } ) AssertStatement() | LOOKAHEAD( { isYieldStart() } ) YieldStatement() | - LOOKAHEAD(2147483647, ( "final" | Annotation() )* Type() , {isRecordTypeSupported() && !isKeyword("record") || !isRecordTypeSupported()}) + LOOKAHEAD(2147483647, ( "final" | Annotation() )* Type() , {isRecordTypeSupported() && !isRecordDeclarationAhead() || !isRecordTypeSupported()}) LocalVariableDeclaration() ";" | - LOOKAHEAD({isRecordTypeSupported() && !isKeyword("record") || !isRecordTypeSupported()}) + LOOKAHEAD(1, {isRecordTypeSupported() && !isRecordDeclarationAhead() || !isRecordTypeSupported()}) Statement() | // we don't need to lookahead further here diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTest.java index 5874a0ae86..ac324726f2 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTest.java @@ -160,7 +160,7 @@ public class Java15PreviewTest { public void localRecords() { ASTCompilationUnit compilationUnit = java15p.parseResource("LocalRecords.java"); List records = compilationUnit.findDescendantsOfType(ASTRecordDeclaration.class); - Assert.assertEquals(1, records.size()); + Assert.assertEquals(5, records.size()); Assert.assertEquals("MerchantSales", records.get(0).getSimpleName()); Assert.assertTrue(records.get(0).isLocal()); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java index d958e2b84a..dd273baf5a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java @@ -23,4 +23,15 @@ public class LocalRecords { .map(MerchantSales::merchant) .collect(Collectors.toList()); } + + void methodWithLocalRecordAndModifiers() { + final record MyRecord1(String a) {} + final static record MyRecord2(String a) {} + @Deprecated record MyRecord3(String a) {} + final @Deprecated static record MyRecord4(String a) {} + } + + void methodWithLocalClass() { + class MyLocalClass {} + } }