Enhance grammar to parse Java 9 module-info.java

This commit is contained in:
Andreas Dangel
2017-09-22 21:35:24 +02:00
parent cb772167bb
commit 1020bf73db
12 changed files with 337 additions and 1 deletions

View File

@ -3,6 +3,7 @@
* Private interface methods are only allowed with java9.
* A single underscore "_" is an invalid identifier in java9.
* Diamond operator for anonymous classes is only allowed with java9.
* Add support for module-info.java.
* Andreas Dangel 09/2017
*====================================================================
* Add support for new Java 8 annotation locations.
@ -350,6 +351,11 @@ public class JavaParser {
throwParseException("With JDK 9, '_' is a keyword, and may not be used as an identifier!");
}
}
private void checkForBadModuleUsage() {
if (jdkVersion < 9) {
throwParseException("Cannot use module declaration when running in JDK inferior to 9 mode!");
}
}
// 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
@ -491,6 +497,16 @@ TOKEN :
| < VOLATILE: "volatile" >
| < WHILE: "while" >
| < STRICTFP: "strictfp" >
| < OPEN: "open" >
| < MODULE: "module" >
| < REQUIRES: "requires" >
| < TRANSITIVE: "transitive" >
| < EXPORTS: "exports" >
| < OPENS: "opens" >
| < TO: "to" >
| < USES: "uses" >
| < PROVIDES: "provides" >
| < WITH: "with" >
}
/* LITERALS */
@ -1294,7 +1310,8 @@ ASTCompilationUnit CompilationUnit() :
{
[ LOOKAHEAD( ( Annotation() )* "package" ) PackageDeclaration() ( EmptyStatement() )* ]
( ImportDeclaration() ( EmptyStatement() )* )*
( TypeDeclaration() ( EmptyStatement() )* )*
( LOOKAHEAD(2) TypeDeclaration() ( EmptyStatement() )* )*
[ ModuleDeclaration() ( EmptyStatement() )* ]
( < "\u001a" > )?
( < "~[]" > )?
<EOF>
@ -2380,3 +2397,41 @@ void DefaultValue():
{
"default" MemberValue()
}
void ModuleDeclaration():
{
StringBuilder s = new StringBuilder();
Token t;
checkForBadModuleUsage();
}
{
( Annotation() )* ["open" {jjtThis.setOpen(true);}] "module"
t=<IDENTIFIER> { s.append(t.image); }
( "." t=<IDENTIFIER> { s.append('.').append(t.image); } )* { jjtThis.setImage(s.toString()); }
"{" (ModuleDirective())* "}"
}
void ModuleDirective():
{}
{
( "requires" { jjtThis.setType(ASTModuleDirective.DirectiveType.REQUIRES); }
("transitive" { jjtThis.setRequiresModifier(ASTModuleDirective.RequiresModifier.TRANSITIVE); } |
"static" { jjtThis.setRequiresModifier(ASTModuleDirective.RequiresModifier.STATIC); } )?
ModuleName() ";" )
| ( "exports" { jjtThis.setType(ASTModuleDirective.DirectiveType.EXPORTS); } Name() [ "to" ModuleName() ("," ModuleName())*] ";" )
| ( "opens" { jjtThis.setType(ASTModuleDirective.DirectiveType.OPENS); } Name() [ "to" ModuleName() ("," ModuleName())*] ";" )
| ( "uses" { jjtThis.setType(ASTModuleDirective.DirectiveType.USES); } Name() ";" )
| ( "provides" { jjtThis.setType(ASTModuleDirective.DirectiveType.PROVIDES); } Name() "with" Name() ("," Name() )* ";" )
}
// Similar to Name()
void ModuleName():
{
StringBuilder s = new StringBuilder();
Token t;
}
{
t=<IDENTIFIER> { s.append(t.image); }
( "." t=<IDENTIFIER> {s.append('.').append(t.image);} )*
{jjtThis.setImage(s.toString());}
}

View File

@ -0,0 +1,33 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/* Generated By:JJTree: Do not edit this line. ASTModuleDeclaration.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 ASTModuleDeclaration extends AbstractJavaNode {
private boolean open;
public ASTModuleDeclaration(int id) {
super(id);
}
public ASTModuleDeclaration(JavaParser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public void setOpen(boolean open) {
this.open = open;
}
public boolean isOpen() {
return open;
}
}
/* JavaCC - OriginalChecksum=752bbec72a6d0d96c4c69a2d08c73614 (do not edit this line) */

View File

@ -0,0 +1,50 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/* Generated By:JJTree: Do not edit this line. ASTModuleDirective.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 ASTModuleDirective extends AbstractJavaNode {
public enum DirectiveType {
REQUIRES, EXPORTS, OPENS, USES, PROVIDES;
}
public enum RequiresModifier {
STATIC, TRANSITIVE;
}
private DirectiveType type;
private RequiresModifier requiresModifier;
public ASTModuleDirective(int id) {
super(id);
}
public ASTModuleDirective(JavaParser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public void setType(DirectiveType type) {
this.type = type;
}
public String getType() {
return String.valueOf(type);
}
public void setRequiresModifier(RequiresModifier requiresModifier) {
this.requiresModifier = requiresModifier;
}
public String getRequiresModifier() {
return requiresModifier == null ? null : requiresModifier.name();
}
}
/*
* JavaCC - OriginalChecksum=93c74930e5df0269e81ce18b4efa6378 (do not edit this
* line)
*/

View File

@ -0,0 +1,27 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/* Generated By:JJTree: Do not edit this line. ASTModuleName.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 ASTModuleName extends AbstractJavaNode {
public ASTModuleName(int id) {
super(id);
}
public ASTModuleName(JavaParser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}
/*
* JavaCC - OriginalChecksum=7be9235079394543d4574d840ebb5235 (do not edit this
* line)
*/

View File

@ -192,6 +192,12 @@ public class DumpFacade extends JavaParserVisitorAdapter {
if (((ASTTryStatement) node).hasFinally()) {
extras.add("has finally");
}
} else if (node instanceof ASTModuleDirective) {
ASTModuleDirective directive = (ASTModuleDirective)node;
extras.add(directive.getType());
if (directive.getRequiresModifier() != null) {
extras.add(directive.getRequiresModifier());
}
}
// Output image and extras

View File

@ -838,4 +838,23 @@ public class JavaParserDecoratedVisitor implements JavaParserVisitor {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
public Object visit(ASTModuleDeclaration node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
public Object visit(ASTModuleDirective node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
@Override
public Object visit(ASTModuleName node, Object data) {
visitor.visit(node, data);
return visit((JavaNode) node, data);
}
}

View File

@ -461,4 +461,16 @@ public class JavaParserVisitorAdapter implements JavaParserVisitor {
public Object visit(ASTMethodReference node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTModuleDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTModuleDirective node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTModuleName node, Object data) {
return visit((JavaNode) node, data);
}
}

View File

@ -706,4 +706,22 @@ public class JavaParserVisitorDecorator implements JavaParserControllessVisitor
public Object visit(ASTMethodReference node, Object data) {
return visitor.visit(node, data);
}
@Override
public Object visit(ASTModuleDeclaration node, Object data) {
return visitor.visit(node, data);
}
@Override
public Object visit(ASTModuleDirective node, Object data) {
return visitor.visit(node, data);
}
@Override
public Object visit(ASTModuleName node, Object data) {
return visitor.visit(node, data);
}
}

View File

@ -527,4 +527,16 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse
public Object visit(ASTMethodReference node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTModuleDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTModuleDirective node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTModuleName node, Object data) {
return visit((JavaNode) node, data);
}
}

View File

@ -0,0 +1,79 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava9;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
public class ASTModuleDeclarationTest {
private static String loadSource(String name) {
try {
return IOUtils.toString(ASTModuleDeclarationTest.class.getResourceAsStream("jdkversiontests/" + name),
StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
public final void jdk9ModuleInfo() {
ASTCompilationUnit ast = parseJava9(loadSource("jdk9_module_info.java"));
List<ASTModuleDeclaration> modules = ast.findDescendantsOfType(ASTModuleDeclaration.class);
assertEquals(1, modules.size());
ASTModuleDeclaration module = modules.get(0);
assertTrue(module.isOpen());
assertEquals("com.example.foo", module.getImage());
assertEquals(7, module.jjtGetNumChildren());
List<ASTModuleDirective> directives = module.findChildrenOfType(ASTModuleDirective.class);
assertEquals(7, directives.size());
// requires com.example.foo.http;
assertEquals(ASTModuleDirective.DirectiveType.REQUIRES, directives.get(0).getType());
assertNull(directives.get(0).getRequiresModifier());
assertEquals("com.example.foo.http", directives.get(0).getFirstChildOfType(ASTModuleName.class).getImage());
// requires java.logging;
assertEquals(ASTModuleDirective.DirectiveType.REQUIRES, directives.get(1).getType());
assertNull(directives.get(1).getRequiresModifier());
assertEquals("java.logging", directives.get(1).getFirstChildOfType(ASTModuleName.class).getImage());
// requires transitive com.example.foo.network;
assertEquals(ASTModuleDirective.DirectiveType.REQUIRES, directives.get(2).getType());
assertEquals(ASTModuleDirective.RequiresModifier.TRANSITIVE, directives.get(2).getRequiresModifier());
assertEquals("com.example.foo.network", directives.get(2).getFirstChildOfType(ASTModuleName.class).getImage());
// exports com.example.foo.bar;
assertEquals(ASTModuleDirective.DirectiveType.EXPORTS, directives.get(3).getType());
assertNull(directives.get(3).getRequiresModifier());
assertEquals("com.example.foo.bar", directives.get(3).getFirstChildOfType(ASTName.class).getImage());
// exports com.example.foo.internal to com.example.foo.probe;
assertEquals(ASTModuleDirective.DirectiveType.EXPORTS, directives.get(4).getType());
assertNull(directives.get(4).getRequiresModifier());
assertEquals("com.example.foo.internal", directives.get(4).getFirstChildOfType(ASTName.class).getImage());
assertEquals("com.example.foo.probe", directives.get(4).getFirstChildOfType(ASTModuleName.class).getImage());
// uses com.example.foo.spi.Intf;
assertEquals(ASTModuleDirective.DirectiveType.USES, directives.get(5).getType());
assertNull(directives.get(5).getRequiresModifier());
assertEquals("com.example.foo.spi.Intf", directives.get(5).getFirstChildOfType(ASTName.class).getImage());
// provides com.example.foo.spi.Intf with com.example.foo.Impl;
assertEquals(ASTModuleDirective.DirectiveType.PROVIDES, directives.get(6).getType());
assertNull(directives.get(6).getRequiresModifier());
assertEquals("com.example.foo.spi.Intf", directives.get(6).getFirstChildOfType(ASTName.class).getImage());
assertEquals("com.example.foo.Impl", directives.get(6).findChildrenOfType(ASTName.class).get(1).getImage());
}
}

View File

@ -247,4 +247,14 @@ public class JDKVersionTest {
public final void jdk9AnonymousDiamond() {
parseJava9(loadSource("jdk9_anonymous_diamond.java"));
}
@Test(expected = ParseException.class)
public final void jdk9ModuleInfoInJava8() {
parseJava18(loadSource("jdk9_module_info.java"));
}
@Test
public final void jdk9ModuleInfo() {
parseJava9(loadSource("jdk9_module_info.java"));
}
}

View File

@ -0,0 +1,15 @@
/*
* See §7.7 Module Declarations in JLS
*/
open module com.example.foo {
requires com.example.foo.http;
requires java.logging;
requires transitive com.example.foo.network;
exports com.example.foo.bar;
exports com.example.foo.internal to com.example.foo.probe;
uses com.example.foo.spi.Intf;
provides com.example.foo.spi.Intf with com.example.foo.Impl;
}