Add Java9 Language Module, Update grammar

* Java8 mode now rejects private methods in interfaces
* Java9 mode now rejects "_" as identifier
This commit is contained in:
Andreas Dangel
2017-09-22 12:16:51 +02:00
parent df87db8d7a
commit cfa4b5b85c
8 changed files with 166 additions and 2 deletions

View File

@ -1,4 +1,9 @@
/**
* Add support for Java 9 changes
* private interface methods are only allowed with java9,
* a single underscore "_" is an invalid identifier in java9.
* Andreas Dangel 09/2017
*====================================================================
* Add support for new Java 8 annotation locations.
* Bugs #414, #415, #417
* @Snap252 06/2017
@ -319,6 +324,22 @@ public class JavaParser {
throwParseException("Cannot use explicit receiver parameters when running in JDK inferior to 1.8 mode!");
}
}
/**
* Keeps track whether we are dealing with an interface or not. Needed since the tree is
* is not fully constructed yet, when we check for private interface methods.
* The flag is updated, if entering ClassOrInterfaceDeclaration and reset when leaving.
*/
private boolean inInterface = false;
private void checkForBadPrivateInterfaceMethod(ASTMethodDeclaration node) {
if (jdkVersion < 9 && inInterface && node.isPrivate()) {
throwParseException("Cannot use private interface methods when running in JDK inferior to 9 mode!");
}
}
private void checkForBadIdentifier(String image) {
if (jdkVersion >= 9 && "_".equals(image)) {
throwParseException("With JDK 9, '_' is a keyword, and may not be used as an identifier!");
}
}
// 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
@ -1343,12 +1364,14 @@ jjtThis.setModifiers(modifiers);
}
{
( /* See note about this optional final modifier in BlockStatement */ ["final"|"abstract"] "class" | "interface" { jjtThis.setInterface(); } )
( /* See note about this optional final modifier in BlockStatement */
["final"|"abstract"] "class" | "interface" { jjtThis.setInterface(); inInterface = true; } )
t=<IDENTIFIER> { jjtThis.setImage(t.image); }
[ TypeParameters() ]
[ ExtendsList() ]
[ ImplementsList() ]
ClassOrInterfaceBody()
{ inInterface = false; } // always reset the flag after leaving the node
}
void ExtendsList():
@ -1446,6 +1469,7 @@ void ClassOrInterfaceBodyDeclaration():
";"
}
void FieldDeclaration(int modifiers) :
{jjtThis.setModifiers(modifiers);}
{
@ -1471,6 +1495,7 @@ void VariableDeclaratorId() :
{
checkForBadAssertUsage(image, "a variable name");
checkForBadEnumUsage(image, "a variable name");
checkForBadIdentifier(image);
jjtThis.setImage( image );
}
}
@ -1489,7 +1514,10 @@ void ArrayInitializer() :
}
void MethodDeclaration(int modifiers) :
{jjtThis.setModifiers(modifiers);}
{
jjtThis.setModifiers(modifiers);
{ checkForBadPrivateInterfaceMethod(jjtThis); }
}
{
[ TypeParameters() ]
(Annotation() {checkForBadTypeAnnotations();})* ResultType() MethodDeclarator() [ "throws" NameList() ]

View File

@ -0,0 +1,15 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.ParserOptions;
public class Java19Handler extends AbstractJavaHandler {
public Parser getParser(ParserOptions parserOptions) {
return new Java19Parser(parserOptions);
}
}

View File

@ -0,0 +1,28 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java;
import java.io.Reader;
import net.sourceforge.pmd.lang.ParserOptions;
import net.sourceforge.pmd.lang.java.ast.JavaParser;
import net.sourceforge.pmd.lang.java.ast.ParseException;
/**
* Adapter for the JavaParser, using Java 1.9 grammar.
*/
public class Java19Parser extends AbstractJavaParser {
public Java19Parser(ParserOptions parserOptions) {
super(parserOptions);
}
@Override
protected JavaParser createJavaParser(Reader source) throws ParseException {
JavaParser javaParser = super.createJavaParser(source);
javaParser.setJdkVersion(9);
return javaParser;
}
}

View File

@ -23,6 +23,7 @@ public class JavaLanguageModule extends BaseLanguageModule {
addVersion("1.6", new Java16Handler(), false);
addVersion("1.7", new Java17Handler(), false);
addVersion("1.8", new Java18Handler(), true);
addVersion("9", new Java19Handler(), false);
}
}

View File

@ -145,6 +145,11 @@ public class ParserTstUtil {
return parseJava(getLanguageVersionHandler("1.8"), code);
}
/** @see #parseJava(LanguageVersionHandler, String) */
public static ASTCompilationUnit parseJava9(String code) {
return parseJava(getLanguageVersionHandler("9"), code);
}
/** @see #parseJava(LanguageVersionHandler, String) */
public static ASTCompilationUnit parseJava13(Class<?> source) {
return parseJava13(getSourceFromClass(source));
@ -170,6 +175,11 @@ public class ParserTstUtil {
return parseJava18(getSourceFromClass(source));
}
/** @see #parseJava(LanguageVersionHandler, String) */
public static ASTCompilationUnit parseJava9(Class<?> source) {
return parseJava9(getSourceFromClass(source));
}
/** @see #parseJava(LanguageVersionHandler, String) */
public static ASTCompilationUnit parseJavaDefaultVersion(String source) {
return parseJava(getDefaultLanguageVersionHandler(), source);

View File

@ -8,6 +8,8 @@ import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava13;
import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava14;
import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava15;
import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava17;
import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava18;
import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava9;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@ -205,4 +207,29 @@ public class JDKVersionTest {
public final void testMulticatchWithAnnotations() {
parseJava17(loadSource("jdk17_multicatch_with_annotations.java"));
}
@Test(expected = ParseException.class)
public final void jdk9PrivateInterfaceMethodsInJava18() {
parseJava18(loadSource("jdk9_private_interface_methods.java"));
}
@Test
public final void testPrivateMethods() {
parseJava18("public class Foo { private void bar() { } }");
}
@Test
public final void jdk9PrivateInterfaceMethods() {
parseJava9(loadSource("jdk9_private_interface_methods.java"));
}
@Test
public final void jdk9InvalidIdentifierInJava18() {
parseJava18(loadSource("jdk9_invalid_identifier.java"));
}
@Test(expected = ParseException.class)
public final void jdk9InvalidIdentifier() {
parseJava9(loadSource("jdk9_invalid_identifier.java"));
}
}

View File

@ -0,0 +1,15 @@
/**
* Using "_" as an identifier is not allowed anymore with java9.
*/
public class Java9Identifier {
/*
* see https://bugs.openjdk.java.net/browse/JDK-8061549
*/
public interface Lambda {
public int a(int _);
public default void t(Lambda l) {
t(_ -> 0);
}
}
}

View File

@ -0,0 +1,40 @@
/**
* With java9, private methods are allowed in interface.
*/
public class Java9Interface {
public interface Tool {
void use();
default String getName() {
return determineName();
}
default String getStaticName() {
return determineNameStatic();
}
private String determineName() {
return "unknown:" + this.getClass().getSimpleName();
}
private static String determineNameStatic() {
return "unknown:" + Tool.class.getSimpleName();
}
}
public static class SampleTool implements Tool {
public void use() {
if (true) { // force a PMD violation: java-basic/UnconditionalIfStatement
System.out.println("Instance: " + getName());
System.out.println(" Static: " + getStaticName());
}
}
}
public static void main(String... args) {
Tool tool = new SampleTool();
tool.use();
}
}