Enhance grammar to parse Java 9 module-info.java
This commit is contained in:
@ -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());}
|
||||
}
|
||||
|
@ -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) */
|
@ -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)
|
||||
*/
|
@ -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)
|
||||
*/
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
Reference in New Issue
Block a user