From fceb474df00cbdf456b488188076d73b2d4c37b7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 16 Aug 2020 18:22:45 +0200 Subject: [PATCH] [java] Remove "non-sealed" token and use semantic lookahead instead --- pmd-java/etc/grammar/Java.jjt | 43 +++++++++++++------ .../pmd/lang/java/ast/Java15Test.java | 5 +++ .../java15/NonSealedIdentifier.java | 15 +++++++ .../jdkversiontests/java15p/LocalRecords.java | 8 ++++ 4 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15/NonSealedIdentifier.java diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 1ba6dd9fd3..01a4e86375 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -537,13 +537,40 @@ public class JavaParser { * specific restricted keyword. * *

Restricted keywords are: - * var, yield, record, sealed, permits + * var, yield, record, sealed, permits, "non" + "-" + "sealed" * *

enum and assert is used like restricted keywords, as they were not keywords * in the early java versions. */ - private boolean isKeyword(String keyword) { - return getToken(1).kind == IDENTIFIER && getToken(1).image.equals(keyword); + private boolean isKeyword(String image) { + return isKeyword(1, image); + } + + private boolean isKeyword(int index, String image) { + Token token = getToken(index); + return token.kind == IDENTIFIER && token.image.equals(image); + } + + private boolean isToken(int index, int kind) { + return getToken(index).kind == kind; + } + + /** + * Semantic lookahead which matches "non-sealed". + * + *

"non-sealed" cannot be a token, for several reasons: + * It is only a keyword with java15 preview+, it consists actually + * of several separate tokens, which are valid on their own. + */ + private boolean isNonSealedModifier() { + if (isKeyword(1, "non") && isToken(2, MINUS) && isKeyword(3, "sealed")) { + Token nonToken = getToken(1); + Token minusToken = getToken(2); + Token sealedToken = getToken(3); + return nonToken.endColumn + 1 == minusToken.beginColumn + && minusToken.endColumn + 1 == sealedToken.beginColumn; + } + return false; } /** @@ -1024,14 +1051,6 @@ TOKEN : | < GT: ">" > } -// Note: New token need to be added at the very end in order to -// keep (binary) compatibility with the generated token ids -// see JavaParserConstants -TOKEN : -{ - < NON_SEALED: "non-sealed" > -} - /***************************************** * THE JAVA LANGUAGE GRAMMAR STARTS HERE * *****************************************/ @@ -1104,7 +1123,7 @@ int Modifiers() #void: | "strictfp" { modifiers |= AccessNode.STRICTFP; } | "default" { modifiers |= AccessNode.DEFAULT; checkForBadDefaultImplementationUsage(); } | LOOKAHEAD({isKeyword("sealed")}) { modifiers |= AccessNode.SEALED; checkForSealedClassUsage(); } - | "non-sealed" { modifiers |= AccessNode.NON_SEALED; checkForSealedClassUsage(); } + | LOOKAHEAD({isNonSealedModifier()}) { modifiers |= AccessNode.NON_SEALED; checkForSealedClassUsage(); } | Annotation() ) )* diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15Test.java index 003c5072cc..867504ebd8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15Test.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15Test.java @@ -71,4 +71,9 @@ public class Java15Test { java14.parse("class Foo { String s =\"a\\sb\"; }"); } + @Test + public void sealedAndNonSealedIdentifiers() { + java15.parseResource("NonSealedIdentifier.java"); + java15p.parseResource("NonSealedIdentifier.java"); + } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15/NonSealedIdentifier.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15/NonSealedIdentifier.java new file mode 100644 index 0000000000..dbeebe9403 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15/NonSealedIdentifier.java @@ -0,0 +1,15 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +public class NonSealedIdentifier { + public static void main(String[] args) { + int result = 0; + int non = 1; + // sealed is a valid identifier name in both Java15 and Java15 Preview + int sealed = 2; + // non-sealed is a valid subtraction expression in both Java15 and Java15 Preview + result = non-sealed; + System.out.println(result); + } +} 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 dd273baf5a..c359b99419 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 @@ -34,4 +34,12 @@ public class LocalRecords { void methodWithLocalClass() { class MyLocalClass {} } + + void methodWithLocalVarsNamedSealed() { + int result = 0; + int non = 1; + int sealed = 2; + result = non-sealed; + System.out.println(result); + } }