diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c2ba2e8b10..87cb787e9a 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,7 @@ This is a {{ site.pmd.release_type }} release. * java * [#3117](https://github.com/pmd/pmd/issues/3117): \[java] Infinite loop when parsing invalid code nested in lambdas + * [#3145](https://github.com/pmd/pmd/issues/3145): \[java] Parse exception when using "record" as variable name ### API Changes diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index be6a803151..4e17336cba 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -1,4 +1,7 @@ /** + * Fix #3145 - parse exception with local records + * Clément Fournier 03/2021 + *==================================================================== * Remove support for Java 14 preview language features * JEP 397: Sealed Classes (Second Preview) for Java16 Preview * JEP 395: Records for Java16 @@ -599,7 +602,7 @@ public class JavaParser { return next.kind == CLASS || isRecordTypeSupported() && next.kind == INTERFACE || isRecordTypeSupported() && next.kind == IDENTIFIER && next.image.equals("enum") - || isRecordTypeSupported() && next.kind == IDENTIFIER && next.image.equals("record"); + || isRecordTypeSupported() && next.kind == IDENTIFIER && next.image.equals("record") && isToken(2, IDENTIFIER); } /** 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 c359b99419..b860e99136 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 @@ -31,6 +31,12 @@ public class LocalRecords { final @Deprecated static record MyRecord4(String a) {} } + void statementThatStartsWithRecordAsRegularIdent() { + // https://github.com/pmd/pmd/issues/3145 + final Map record = new HashMap<>(); + record.put("key", "value"); + } + void methodWithLocalClass() { class MyLocalClass {} } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt index c274834840..afcb2d7def 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt @@ -215,6 +215,51 @@ | | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = true, @FormalParameter = false, @Image = "a", @LambdaParameter = false, @LocalVariable = false, @Name = "a", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "a"] | +- RecordBody[] +- ClassOrInterfaceBodyDeclaration[@AnonymousInnerClass = false, @EnumChild = false, @Kind = DeclarationKind.METHOD] + | +- MethodDeclaration[@Abstract = false, @Arity = 0, @Default = false, @Final = false, @InterfaceMember = false, @Kind = MethodLikeKind.METHOD, @MethodName = "statementThatStartsWithRecordAsRegularIdent", @Modifiers = 0, @Name = "statementThatStartsWithRecordAsRegularIdent", @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyPublic = false, @Transient = false, @Void = true, @Volatile = false] + | +- ResultType[@Void = true, @returnsArray = false] + | +- MethodDeclarator[@Image = "statementThatStartsWithRecordAsRegularIdent", @ParameterCount = 0] + | | +- FormalParameters[@ParameterCount = 0, @Size = 0] + | +- Block[@containsComment = false] + | +- BlockStatement[@Allocation = true] + | | +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = true, @Modifiers = 32, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "record", @Volatile = false] + | | +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "Map"] + | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "Map", @ReferenceToClassSameCompilationUnit = false] + | | | +- TypeArguments[@Diamond = false] + | | | +- TypeArgument[@Wildcard = false] + | | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "String", @ReferenceToClassSameCompilationUnit = false] + | | | +- TypeArgument[@Wildcard = false] + | | | +- ReferenceType[@Array = false, @ArrayDepth = 0] + | | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "String", @ReferenceToClassSameCompilationUnit = false] + | | +- VariableDeclarator[@Initializer = true, @Name = "record"] + | | +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = true, @FormalParameter = false, @Image = "record", @LambdaParameter = false, @LocalVariable = true, @Name = "record", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "record"] + | | +- VariableInitializer[] + | | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- AllocationExpression[@AnonymousClass = false] + | | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = false, @ArrayDepth = 0, @Image = "HashMap", @ReferenceToClassSameCompilationUnit = false] + | | | +- TypeArguments[@Diamond = true] + | | +- Arguments[@ArgumentCount = 0, @Size = 0] + | +- BlockStatement[@Allocation = false] + | +- Statement[] + | +- StatementExpression[] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Name[@Image = "record.put"] + | +- PrimarySuffix[@ArgumentCount = 2, @Arguments = true, @ArrayDereference = false] + | +- Arguments[@ArgumentCount = 2, @Size = 2] + | +- ArgumentList[@Size = 2] + | +- Expression[@StandAlonePrimitive = false] + | | +- PrimaryExpression[] + | | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""key"", @FloatLiteral = false, @Image = ""key"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""key"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + | +- Expression[@StandAlonePrimitive = false] + | +- PrimaryExpression[] + | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false] + | +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = ""value"", @FloatLiteral = false, @Image = ""value"", @IntLiteral = false, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = true, @TextBlock = false, @TextBlockContent = ""value"", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0] + +- ClassOrInterfaceBodyDeclaration[@AnonymousInnerClass = false, @EnumChild = false, @Kind = DeclarationKind.METHOD] | +- MethodDeclaration[@Abstract = false, @Arity = 0, @Default = false, @Final = false, @InterfaceMember = false, @Kind = MethodLikeKind.METHOD, @MethodName = "methodWithLocalClass", @Modifiers = 0, @Name = "methodWithLocalClass", @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyPublic = false, @Transient = false, @Void = true, @Volatile = false] | +- ResultType[@Void = true, @returnsArray = false] | +- MethodDeclarator[@Image = "methodWithLocalClass", @ParameterCount = 0]