From 8949019da6f30da205dfefe346488dd7e1c347d7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Mar 2019 15:29:16 +0100 Subject: [PATCH] [java] Add Java12 support --- pmd-java/etc/grammar/Java.jjt | 74 +++++++++++++++++-- .../pmd/lang/java/JavaLanguageModule.java | 3 +- .../lang/java/ast/ASTSwitchExpression.java | 21 ++++++ .../lang/java/ast/ASTSwitchLabeledRule.java | 21 ++++++ .../java/ast/JavaParserDecoratedVisitor.java | 12 +++ .../java/ast/JavaParserVisitorAdapter.java | 10 +++ .../java/ast/JavaParserVisitorDecorator.java | 10 +++ .../pmd/lang/java/rule/AbstractJavaRule.java | 10 +++ .../pmd/lang/java/ast/Java12Test.java | 63 ++++++++++++++++ .../java12/MultipleCaseLabels.java | 21 ++++++ .../java12/SwitchExpressions.java | 31 ++++++++ .../jdkversiontests/java12/SwitchRules.java | 20 +++++ 12 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpression.java create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabeledRule.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java12Test.java create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/MultipleCaseLabels.java create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchExpressions.java create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchRules.java diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index b84165ca98..deb8d1b79a 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -378,6 +378,21 @@ public class JavaParser { throwParseException("With JDK 10, 'var' is a restricted local variable type and cannot be used for type declarations!"); } } + private void checkForMultipleCaseLabels() { + if (jdkVersion < 12) { + throwParseException("Multiple case labels in switch statements are only supported with Java 12"); + } + } + private void checkForSwitchRules() { + if (jdkVersion < 12) { + throwParseException("Switch rules in switch statements are only supported with Java 12"); + } + } + private void checkForSwitchExpression() { + if (jdkVersion < 12) { + throwParseException("Switch expressions are only supported with Java 12"); + } + } // 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 @@ -2006,10 +2021,14 @@ void Expression() : // https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.27 {} { + LOOKAHEAD((|LambdaParameters()) "->" ) LambdaExpression() + | + ( ConditionalExpression() [ LOOKAHEAD(2) AssignmentOperator() Expression() ] + ) } void AssignmentOperator() : @@ -2140,13 +2159,14 @@ void UnaryExpressionNotPlusMinus() #UnaryExpressionNotPlusMinus((jjtn000.getImag { ( "~" {jjtThis.setImage("~");} | "!" {jjtThis.setImage("!");} ) UnaryExpression() /* - * This is really ugly... we are repeting the CastExpression lookahead and full expression... + * This is really ugly... we are repeating the CastExpression lookahead and full expression... * If we don't the lookahead within CastExpression is ignored, and it simply looks for the expression, * meaning we can't be explicit as to what can be casted depending on the cast type (primitive or otherwhise) */ | LOOKAHEAD("(" (Annotation())* PrimitiveType() ")") CastExpression() | LOOKAHEAD("(" (Annotation())* Type() ( "&" ReferenceType() )* ")" UnaryExpressionNotPlusMinus()) CastExpression() | PostfixExpression() +| SwitchExpression() } void PostfixExpression() #PostfixExpression((jjtn000.getImage() != null)): @@ -2164,6 +2184,13 @@ void CastExpression() : | "(" (TypeAnnotation())* Type() ( "&" {checkForBadIntersectionTypesInCasts(); jjtThis.setIntersectionTypes(true);} ReferenceType() )* ")" UnaryExpressionNotPlusMinus() } +void SwitchExpression() : +{} +{ + {checkForSwitchExpression();} + "switch" "(" Expression() ")" "{" SwitchBlock() "}" +} + void PrimaryExpression() : {} { @@ -2191,9 +2218,9 @@ void PrimaryPrefix() : Literal() | LOOKAHEAD(2) "this" {jjtThis.setUsesThisModifier();} | "super" {jjtThis.setUsesSuperModifier();} -| LOOKAHEAD( "->" ) LambdaExpression() -| LOOKAHEAD( "(" VariableDeclaratorId() ( "," VariableDeclaratorId() )* ")" "->" ) LambdaExpression() -| LOOKAHEAD( FormalParameters() "->" ) LambdaExpression() +//| LOOKAHEAD( "->" ) LambdaExpression() +//| LOOKAHEAD( "(" VariableDeclaratorId() ( "," VariableDeclaratorId() )* ")" "->" ) LambdaExpression() +//| LOOKAHEAD( FormalParameters() "->" ) LambdaExpression() | LOOKAHEAD(3) "(" Expression() ")" | AllocationExpression() | LOOKAHEAD( ResultType() "." "class" ) ResultType() "." "class" @@ -2415,20 +2442,51 @@ void StatementExpression() : PostfixExpression() } -void SwitchStatement() : +void SwitchStatement(): {} { "switch" "(" Expression() ")" "{" - ( SwitchLabel() ( BlockStatement() )* )* + SwitchBlock() "}" } +void SwitchBlock() #void : +{} +{ +( + SwitchLabel() + ( + "->" ( Expression() ";" | Block() | ThrowStatement() ) + | + ":" (SwitchLabel() ":")* ( BlockStatement() )* + ) +)* +/* + LOOKAHEAD(SwitchLabel() "->") SwitchLabeledRule() ( SwitchLabeledRule() )* + | + ( SwitchLabeledStatementGroup() )* +*/ +} + +void SwitchLabeledStatementGroup() #void: +{} +{ + SwitchLabel() ":" (SwitchLabel() ":")* ( BlockStatement() )* +} + void SwitchLabel() : {} { - "case" Expression() ":" + "case" ConditionalExpression() ({checkForMultipleCaseLabels();} "," ConditionalExpression())* | - "default" {jjtThis.setDefault();} ":" + "default" {jjtThis.setDefault();} +} + +void SwitchLabeledRule(): +{} +{ + {checkForSwitchRules();} + SwitchLabel() "->" ( Expression() ";" | Block() | ThrowStatement() ) } void IfStatement() : diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java index f5fb17220a..a62593e92c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java @@ -25,7 +25,8 @@ public class JavaLanguageModule extends BaseLanguageModule { addVersion("1.8", new JavaLanguageHandler(8), false); addVersion("9", new JavaLanguageHandler(9), false); addVersion("10", new JavaLanguageHandler(10), false); - addVersion("11", new JavaLanguageHandler(11), true); + addVersion("11", new JavaLanguageHandler(11), false); + addVersion("12", new JavaLanguageHandler(12), true); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpression.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpression.java new file mode 100644 index 0000000000..ece1206cf6 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpression.java @@ -0,0 +1,21 @@ +/* Generated By:JJTree: Do not edit this line. ASTSwitchLabeledRule.java Version 4.3 */ +/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=true,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package net.sourceforge.pmd.lang.java.ast; + +public +class ASTSwitchExpression extends AbstractJavaNode { + ASTSwitchExpression(int id) { + super(id); + } + + ASTSwitchExpression(JavaParser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} +/* JavaCC - OriginalChecksum=8b1747ca53f66203ee212a3699a9a2f3 (do not edit this line) */ diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabeledRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabeledRule.java new file mode 100644 index 0000000000..b2058d2651 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabeledRule.java @@ -0,0 +1,21 @@ +/* Generated By:JJTree: Do not edit this line. ASTSwitchLabeledRule.java Version 4.3 */ +/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=true,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package net.sourceforge.pmd.lang.java.ast; + +public +class ASTSwitchLabeledRule extends AbstractJavaNode { + ASTSwitchLabeledRule(int id) { + super(id); + } + + ASTSwitchLabeledRule(JavaParser p, int id) { + super(p, id); + } + + + /** Accept the visitor. **/ + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} +/* JavaCC - OriginalChecksum=8b1747ca53f66203ee212a3699a9a2f3 (do not edit this line) */ diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java index d13a5e1ff8..1d76676124 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserDecoratedVisitor.java @@ -868,4 +868,16 @@ public class JavaParserDecoratedVisitor implements JavaParserVisitor { visitor.visit(node, data); return visit((JavaNode) node, data); } + + @Override + public Object visit(ASTSwitchLabeledRule node, Object data) { + visitor.visit(node, data); + return visit((JavaNode) node, data); + } + + @Override + public Object visit(ASTSwitchExpression node, Object data) { + visitor.visit(node, data); + return visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java index 210b09c0b4..721c0893c0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java @@ -600,4 +600,14 @@ public class JavaParserVisitorAdapter implements JavaParserVisitor { public Object visit(ASTModuleName node, Object data) { return visit((JavaNode) node, data); } + + @Override + public Object visit(ASTSwitchLabeledRule node, Object data) { + return visit((JavaNode) node, data); + } + + @Override + public Object visit(ASTSwitchExpression node, Object data) { + return visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java index d9968afdbf..b25a36ae35 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorDecorator.java @@ -730,4 +730,14 @@ public class JavaParserVisitorDecorator implements JavaParserControllessVisitor public Object visit(ASTModuleName node, Object data) { return visitor.visit(node, data); } + + @Override + public Object visit(ASTSwitchLabeledRule node, Object data) { + return visitor.visit(node, data); + } + + @Override + public Object visit(ASTSwitchExpression node, Object data) { + return visitor.visit(node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java index 0e17201d7a..f22a9c13b8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java @@ -674,4 +674,14 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse public Object visit(ASTModuleName node, Object data) { return visit((JavaNode) node, data); } + + @Override + public Object visit(ASTSwitchLabeledRule node, Object data) { + return visit((JavaNode) node, data); + } + + @Override + public Object visit(ASTSwitchExpression node, Object data) { + return visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java12Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java12Test.java new file mode 100644 index 0000000000..fbff30b786 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java12Test.java @@ -0,0 +1,63 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.ParseException; +import net.sourceforge.pmd.lang.java.ParserTstUtil; + +public class Java12Test { + private static String loadSource(String name) { + try { + return IOUtils.toString(Java10Test.class.getResourceAsStream("jdkversiontests/java12/" + name), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test(expected = ParseException.class) + public void testMultipleCaseLabelsJava11() { + ParserTstUtil.parseAndTypeResolveJava("11", loadSource("MultipleCaseLabels.java")); + } + + @Test + public void testMultipleCaseLabels() { + ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("12", + loadSource("MultipleCaseLabels.java")); + Assert.assertNotNull(compilationUnit); + } + + @Test(expected = ParseException.class) + public void testSwitchRulesJava11() { + ParserTstUtil.parseAndTypeResolveJava("11", loadSource("SwitchRules.java")); + } + + @Test + public void testSwitchRules() { + ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("12", + loadSource("SwitchRules.java")); + Assert.assertNotNull(compilationUnit); + } + + @Test(expected = ParseException.class) + public void testSwitchExpressionsJava11() { + ParserTstUtil.parseAndTypeResolveJava("11", loadSource("SwitchExpressions.java")); + } + + @Test + public void testSwitchExpressions() { + ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("12", + loadSource("SwitchExpressions.java")); + Assert.assertNotNull(compilationUnit); + } + +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/MultipleCaseLabels.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/MultipleCaseLabels.java new file mode 100644 index 0000000000..38b3b30a7c --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/MultipleCaseLabels.java @@ -0,0 +1,21 @@ +public class MultipleCaseLabels { + private static final int MONDAY = 1; + private static final int TUESDAY = 2; + private static final int WEDNESDAY = 3; + private static final int THURSDAY = 4; + private static final int FRIDAY = 5; + private static final int SATURDAY = 6; + private static final int SUNDAY = 7; + + + public static void main(String[] args) { + int day = THURSDAY; + + switch (day) { + case MONDAY, FRIDAY, SUNDAY: System.out.println(" 6"); break; + case TUESDAY : System.out.println(" 7"); break; + case THURSDAY, SATURDAY : System.out.println(" 8"); break; + case WEDNESDAY : System.out.println(" 9"); break; + } + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchExpressions.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchExpressions.java new file mode 100644 index 0000000000..ba66110c18 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchExpressions.java @@ -0,0 +1,31 @@ +public class SwitchExpressions { + private static final int MONDAY = 1; + private static final int TUESDAY = 2; + private static final int WEDNESDAY = 3; + private static final int THURSDAY = 4; + private static final int FRIDAY = 5; + private static final int SATURDAY = 6; + private static final int SUNDAY = 7; + + + public static void main(String[] args) { + int day = FRIDAY; + + int numLetters = switch (day) { + case MONDAY, FRIDAY, SUNDAY -> 6; + case TUESDAY -> 7; + case THURSDAY, SATURDAY -> 8; + case WEDNESDAY -> 9; + default -> { + int k = day * 2; + int result = f(k); + break result; + } + }; + System.out.printf("NumLetters: %d%n", numLetters); + } + + private static int f(int k) { + return k*3; + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchRules.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchRules.java new file mode 100644 index 0000000000..52bb00cb33 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java12/SwitchRules.java @@ -0,0 +1,20 @@ +public class SwitchRules { + private static final int MONDAY = 1; + private static final int TUESDAY = 2; + private static final int WEDNESDAY = 3; + private static final int THURSDAY = 4; + private static final int FRIDAY = 5; + private static final int SATURDAY = 6; + private static final int SUNDAY = 7; + + public static void main(String[] args) { + int day = WEDNESDAY; + + switch (day) { + case MONDAY, FRIDAY, SUNDAY -> System.out.println(" 6"); + case TUESDAY -> System.out.println(" 7"); + case THURSDAY, SATURDAY -> System.out.println(" 8"); + case WEDNESDAY -> System.out.println(" 9"); + } + } +}