[java] Improve module grammar

Type names in "provides" directives are disambiguated
like any other type name.

ASTName is made redundant and removed for good.
This commit is contained in:
Clément Fournier
2022-04-01 18:56:16 +02:00
parent 9d816d28b9
commit bc19d2cc40
29 changed files with 482 additions and 228 deletions

View File

@ -1,4 +1,9 @@
/**
* Improve module grammar.
* Type names in "provides" directives are disambiguated like any other type name.
* ASTName is made redundant and removed for good.
* Clément Fournier 03/2022
*====================================================================
* Support "JEP 420: Pattern Matching for switch (Second Preview)" for Java 18 Preview
* There were no grammar changes between 18-preview and 17-preview
* Remove support for Java 16 preview language features
@ -2734,40 +2739,34 @@ void DefaultValue():
}
void ModuleDeclaration():
{}
{
StringBuilder s = new StringBuilder();
JavaccToken t;
}
{
( Annotation() )* [LOOKAHEAD({isKeyword("open")}) <IDENTIFIER> {jjtThis.setOpen(true);}] LOOKAHEAD({isKeyword("module")}) <IDENTIFIER>
t=<IDENTIFIER> { s.append(t.getImage()); }
( "." t=<IDENTIFIER> { s.append('.').append(t.getImage()); } )* { jjtThis.setImage(s.toString()); }
AnnotationList()
[LOOKAHEAD({isKeyword("open")}) <IDENTIFIER> {jjtThis.setOpen(true);}]
LOOKAHEAD({isKeyword("module")}) <IDENTIFIER>
ModuleName()
"{" (ModuleDirective())* "}"
}
void ModuleDirective():
{}
void ModuleDirective() #void:
{String packageName;}
{
( LOOKAHEAD({isKeyword("requires")}) <IDENTIFIER> { jjtThis.setType(ASTModuleDirective.DirectiveType.REQUIRES); }
(LOOKAHEAD({isKeyword("transitive")}) <IDENTIFIER> { jjtThis.setRequiresModifier(ASTModuleDirective.RequiresModifier.TRANSITIVE); } |
"static" { jjtThis.setRequiresModifier(ASTModuleDirective.RequiresModifier.STATIC); } )?
ModuleName() ";" )
| ( LOOKAHEAD({isKeyword("exports")}) <IDENTIFIER> { jjtThis.setType(ASTModuleDirective.DirectiveType.EXPORTS); } Name() [ LOOKAHEAD({isKeyword("to")}) <IDENTIFIER> ModuleName() ("," ModuleName())*] ";" )
| ( LOOKAHEAD({isKeyword("opens")}) <IDENTIFIER> { jjtThis.setType(ASTModuleDirective.DirectiveType.OPENS); } Name() [ LOOKAHEAD({isKeyword("to")}) <IDENTIFIER> ModuleName() ("," ModuleName())*] ";" )
| ( LOOKAHEAD({isKeyword("uses")}) <IDENTIFIER> { jjtThis.setType(ASTModuleDirective.DirectiveType.USES); } Name() ";" )
| ( LOOKAHEAD({isKeyword("provides")}) <IDENTIFIER> { jjtThis.setType(ASTModuleDirective.DirectiveType.PROVIDES); } Name() LOOKAHEAD({isKeyword("with")}) <IDENTIFIER> Name() ("," Name() )* ";" )
( LOOKAHEAD({isKeyword("requires")}) <IDENTIFIER>
[
LOOKAHEAD({isKeyword("transitive")}) <IDENTIFIER> { jjtThis.setTransitive(); }
| "static" { jjtThis.setStatic(); }
]
ModuleName() ";" ) #ModuleRequiresDirective
| ( LOOKAHEAD({isKeyword("exports")}) <IDENTIFIER> packageName=VoidNameNoLookahead() {jjtThis.setPackageName(packageName);} [ LOOKAHEAD({isKeyword("to")}) <IDENTIFIER> ModuleName() ("," ModuleName())* ] ";" ) #ModuleExportsDirective
| ( LOOKAHEAD({isKeyword("opens")}) <IDENTIFIER> packageName=VoidNameNoLookahead() {jjtThis.setPackageName(packageName);} [ LOOKAHEAD({isKeyword("to")}) <IDENTIFIER> ModuleName() ("," ModuleName())* ] ";" ) #ModuleOpensDirective
| ( LOOKAHEAD({isKeyword("uses")}) <IDENTIFIER> packageName=VoidNameNoLookahead() {jjtThis.setPackageName(packageName);} ";" ) #ModuleUsesDirective
| ( LOOKAHEAD({isKeyword("provides")}) <IDENTIFIER> ClassName() LOOKAHEAD({isKeyword("with")}) <IDENTIFIER> ClassName() ("," ClassName() )* ";" ) #ModuleProvidesDirective
}
void ModuleName():
{ String name; }
{
name=VoidName() { jjtThis.setImage(name); }
}
void Name():
{ String name; }
{
name=VoidName() { jjtThis.setImage(name); }
name=VoidNameNoLookahead() { jjtThis.setImage(name); }
}
void AmbiguousName():
@ -2793,6 +2792,18 @@ String VoidName() #void:
{return s.toString();}
}
String VoidNameNoLookahead() #void:
{
StringBuilder s = new StringBuilder();
JavaccToken t;
}
{
t=<IDENTIFIER> { s.append(t.getImage()); }
( "." t=<IDENTIFIER> { s.append('.').append(t.getImage()); } )*
{return s.toString();}
}
// Produces a ClassOrInterfaceType, possibly with an ambiguous LHS
void ClassName() #void:
{}

View File

@ -73,8 +73,7 @@ public final class ASTCompilationUnit extends AbstractJavaTypeNode implements Ja
* Returns the package name of this compilation unit. If there is no
* package declaration, then returns the empty string.
*/
@NonNull
public String getPackageName() {
public @NonNull String getPackageName() {
ASTPackageDeclaration pack = getPackageDeclaration();
return pack == null ? "" : pack.getName();
}
@ -88,6 +87,13 @@ public final class ASTCompilationUnit extends AbstractJavaTypeNode implements Ja
return children(ASTAnyTypeDeclaration.class);
}
/**
* Returns the module declaration, if this is a modular compilation unit.
*/
public @Nullable ASTModuleDeclaration getModuleDeclaration() {
return firstChild(ASTModuleDeclaration.class);
}
@Override
public @NonNull JSymbolTable getSymbolTable() {
assert symbolTable != null : "Symbol table wasn't set";

View File

@ -4,7 +4,21 @@
package net.sourceforge.pmd.lang.java.ast;
public final class ASTModuleDeclaration extends AbstractJavaNode {
import net.sourceforge.pmd.lang.ast.NodeStream;
/**
* A module declaration. This is found at the top-level of a
* {@linkplain ASTCompilationUnit modular compilation unit}.
*
* <pre clas="grammar">
*
* ModuleDeclaration ::= {@linkplain ASTModifierList AnnotationList} "open"?
* "module" {@linkplain ASTModuleName ModuleName}
* "{" {@linkplain ASTModuleDirective ModuleDirective}* "}"
*
* </pre>
*/
public final class ASTModuleDeclaration extends AbstractJavaNode implements Annotatable {
private boolean open;
@ -18,6 +32,20 @@ public final class ASTModuleDeclaration extends AbstractJavaNode {
return visitor.visit(this, data);
}
/**
* Returns the name of the declared module. Module names look
* like package names, eg {@code java.base}.
*/
public String getName() {
return firstChild(ASTModuleName.class).getName();
}
/**
* Returns a stream with all directives declared by the module.
*/
public NodeStream<ASTModuleDirective> getDirectives() {
return children(ASTModuleDirective.class);
}
void setOpen(boolean open) {
this.open = open;

View File

@ -4,44 +4,47 @@
package net.sourceforge.pmd.lang.java.ast;
public final class ASTModuleDirective extends AbstractJavaNode {
import net.sourceforge.pmd.lang.rule.xpath.NoAttribute;
/**
* A directive of a {@linkplain ASTModuleDeclaration module declaration}.
* Implementations provide more specific attributes.
*
* <pre class="grammar">
*
* ModuleDirective ::= {@linkplain ASTModuleRequiresDirective ModuleRequiresDirective}
* | {@linkplain ASTModuleOpensDirective ModuleOpensDirective}
* | {@linkplain ASTModuleExportsDirective ModuleExportsDirective}
* | {@linkplain ASTModuleProvidesDirective ModuleProvidesDirective}
* | {@linkplain ASTModuleUsesDirective ModuleUsesDirective}
*
* </pre>
*/
public abstract class ASTModuleDirective extends AbstractJavaNode {
private final DirectiveType type;
ASTModuleDirective(int id, DirectiveType type) {
super(id);
this.type = type;
}
/**
* Returns the kind of the directive.
*/
@NoAttribute
public DirectiveType getType() {
return type;
}
/**
* Kind of a module directive. Specific kinds are represented by
* specific subclasses.
*/
public enum DirectiveType {
REQUIRES, EXPORTS, OPENS, USES, PROVIDES
}
public enum RequiresModifier {
STATIC, TRANSITIVE
}
private DirectiveType type;
private RequiresModifier requiresModifier;
ASTModuleDirective(int id) {
super(id);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
void setType(DirectiveType type) {
this.type = type;
}
public String getType() {
return String.valueOf(type);
}
void setRequiresModifier(RequiresModifier requiresModifier) {
this.requiresModifier = requiresModifier;
}
public String getRequiresModifier() {
return requiresModifier == null ? null : requiresModifier.name();
}
}

View File

@ -0,0 +1,39 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.NodeStream;
/**
* An "exports" directive of a {@linkplain ASTModuleDeclaration module declaration}.
*
* <pre class="grammar">
*
* ModuleExportsDirective ::=
* "exports" &lt;PACKAGE_NAME&gt;
* ( "to" {@linkplain ASTModuleName ModuleName} ( "," {@linkplain ASTModuleName ModuleName})* )?
* ";"
*
* </pre>
*/
public final class ASTModuleExportsDirective extends AbstractPackageNameModuleDirective {
ASTModuleExportsDirective(int id) {
super(id, DirectiveType.EXPORTS);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
/**
* Returns a stream of the module names that are found after the "to" keyword.
* May be empty
*/
public NodeStream<ASTModuleName> getTargetModules() {
return children(ASTModuleName.class);
}
}

View File

@ -4,6 +4,10 @@
package net.sourceforge.pmd.lang.java.ast;
/**
* The name of a module. Module names look like package names, eg
* {@code java.base}.
*/
public final class ASTModuleName extends AbstractJavaNode {
ASTModuleName(int id) {
@ -15,4 +19,19 @@ public final class ASTModuleName extends AbstractJavaNode {
public <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
@Override
@Deprecated
public String getImage() {
return null;
}
/**
* Returns the name of the declared module. Module names look
* like package names, eg {@code java.base}.
*/
public String getName() {
return super.getImage();
}
}

View File

@ -0,0 +1,40 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.NodeStream;
/**
* An "opens" directive of a {@linkplain ASTModuleDeclaration module declaration}.
*
* <pre class="grammar">
*
* ModuleOpensDirective ::=
* "opens" &lt;PACKAGE_NAME&gt;
* ( "to" {@linkplain ASTModuleName ModuleName} ( "," {@linkplain ASTModuleName ModuleName})* )?
* ";"
*
* </pre>
*/
public final class ASTModuleOpensDirective extends AbstractPackageNameModuleDirective {
ASTModuleOpensDirective(int id) {
super(id, DirectiveType.OPENS);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
/**
* Returns a stream of the module names that are found after the "to" keyword.
* May be empty
*/
public NodeStream<ASTModuleName> getTargetModules() {
return children(ASTModuleName.class);
}
}

View File

@ -0,0 +1,39 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.NodeStream;
/**
* A "provides" directive of a {@linkplain ASTModuleDeclaration module declaration}.
*
* <pre class="grammar">
*
* ModuleProvidesDirective ::=
* "provides" {@linkplain ASTClassOrInterfaceType ClassType}
* "with" {@linkplain ASTClassOrInterfaceType ClassType} ( "," {@linkplain ASTClassOrInterfaceType ClassType} )*
* ";"
*
* </pre>
*/
public final class ASTModuleProvidesDirective extends ASTModuleDirective {
ASTModuleProvidesDirective(int id) {
super(id, DirectiveType.PROVIDES);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
public ASTClassOrInterfaceType getProvidedInterface() {
return firstChild(ASTClassOrInterfaceType.class);
}
public NodeStream<ASTClassOrInterfaceType> getImplementations() {
return children(ASTClassOrInterfaceType.class).drop(1);
}
}

View File

@ -0,0 +1,57 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* A "requires" directive of a {@linkplain ASTModuleDeclaration module declaration}.
*
* <pre class="grammar">
*
* ModuleRequiresDirective ::=
* "requires" ( "transitive" | "static" )? {@linkplain ASTModuleName ModuleName} ";"
*
* </pre>
*/
public final class ASTModuleRequiresDirective extends ASTModuleDirective {
private boolean isStatic;
private boolean isTransitive;
ASTModuleRequiresDirective(int id) {
super(id, DirectiveType.REQUIRES);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
/**
* Returns the name of the required module.
*/
public @NonNull ASTModuleName getRequiredModule() {
return Objects.requireNonNull(firstChild(ASTModuleName.class));
}
public boolean isStatic() {
return isStatic;
}
public boolean isTransitive() {
return isTransitive;
}
void setTransitive() {
isTransitive = true;
}
void setStatic() {
isStatic = true;
}
}

View File

@ -0,0 +1,26 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
/**
* A "uses" directive of a {@linkplain ASTModuleDeclaration module declaration}.
*
* <pre class="grammar">
*
* ModuleUsesDirective ::= "uses" &lt;PACKAGE_NAME&gt; ";"
*
* </pre>
*/
public final class ASTModuleUsesDirective extends AbstractPackageNameModuleDirective {
ASTModuleUsesDirective(int id) {
super(id, DirectiveType.USES);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
}

View File

@ -1,46 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
public class ASTName extends AbstractJavaTypeNode {
private NameDeclaration nd;
/**
* Constructor for a synthetic node.
* @param image Image of the new node
*/
@InternalApi
@Deprecated
public ASTName(String image, AbstractJavaNode parent) {
super(JavaParserImplTreeConstants.JJTNAME);
setImage(image);
setParent(parent);
}
ASTName(int id) {
super(id);
}
@InternalApi
@Deprecated
public void setNameDeclaration(NameDeclaration nd) {
this.nd = nd;
}
public NameDeclaration getNameDeclaration() {
return this.nd;
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
}

View File

@ -6,8 +6,7 @@ package net.sourceforge.pmd.lang.java.ast;
/**
* Package declaration at the top of a {@linkplain ASTCompilationUnit source file}.
* Since 7.0, there is no {@linkplain ASTName Name} node anymore. Use
* {@link #getName()} instead.
* Since 7.0, there is no Name node anymore. Use {@link #getName()} instead.
*
*
* <pre class="grammar">

View File

@ -30,15 +30,6 @@ public class ASTYieldStatement extends AbstractStatement {
return visitor.visit(this, data);
}
@Override
public String getImage() {
String result = super.getImage();
if (result == null && hasDescendantOfType(ASTName.class)) {
result = getFirstDescendantOfType(ASTName.class).getImage();
}
return result;
}
/** Returns the yielded expression. */
public ASTExpression getExpr() {

View File

@ -0,0 +1,26 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
public abstract class AbstractPackageNameModuleDirective extends ASTModuleDirective {
AbstractPackageNameModuleDirective(int id, DirectiveType type) {
super(id, type);
}
public final String getPackageName() {
return super.getImage();
}
@Override
@Deprecated
public final String getImage() {
return null;
}
final void setPackageName(String name) {
super.setImage(name);
}
}

View File

@ -18,6 +18,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
import net.sourceforge.pmd.lang.java.ast.ASTExtendsList;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTModuleProvidesDirective;
import net.sourceforge.pmd.lang.java.ast.ASTSuperExpression;
import net.sourceforge.pmd.lang.java.ast.ASTThisExpression;
import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression;
@ -62,6 +63,7 @@ public class LooseCouplingRule extends AbstractJavaRulechainRule {
|| parent instanceof ASTExtendsList // extends AbstractMap<...>
|| parent instanceof ASTThisExpression // Enclosing.this
|| parent instanceof ASTSuperExpression // Enclosing.super
|| parent instanceof ASTModuleProvidesDirective // provides <interface> with <implementation>
|| parent instanceof ASTArrayType && parent.getParent() instanceof ASTArrayAllocation;
}

View File

@ -42,7 +42,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
import net.sourceforge.pmd.lang.java.ast.ASTLambdaParameter;
import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
import net.sourceforge.pmd.lang.java.ast.ASTMethodReference;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTPattern;
@ -152,11 +151,6 @@ public final class LazyTypeResolver extends JavaVisitorBase<TypingContext, @NonN
return ts.NO_TYPE; // TODO shouldn't be a typenode (do you mean type of variable, or type of initializer?)
}
@Override
public JTypeMirror visit(ASTName node, TypingContext ctx) {
return ts.NO_TYPE; // TODO shouldn't be a typenode (basically an AmbiguousName)
}
@Override
public JTypeMirror visit(ASTCompilationUnit node, TypingContext ctx) {
return ts.NO_TYPE; // TODO shouldn't be a typenode

View File

@ -1,14 +1,10 @@
/**
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.Test;
@ -17,51 +13,11 @@ import net.sourceforge.pmd.lang.java.BaseParserTest;
public class ASTModuleDeclarationTest extends BaseParserTest {
@Test
public final void jdk9ModuleInfo() {
ASTCompilationUnit ast = java9.parseResource("jdkversiontests/jdk9_module_info.java");
ASTModuleDeclaration module = ast.descendants(ASTModuleDeclaration.class).firstOrThrow();
assertTrue(module.isOpen());
assertEquals("com.example.foo", module.getImage());
assertEquals(7, module.getNumChildren());
List<ASTModuleDirective> directives = module.children(ASTModuleDirective.class).toList();
assertEquals(7, directives.size());
// requires com.example.foo.http;
assertEquals(ASTModuleDirective.DirectiveType.REQUIRES.name(), 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.name(), 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.name(), directives.get(2).getType());
assertEquals(ASTModuleDirective.RequiresModifier.TRANSITIVE.name(), 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.name(), 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.name(), 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.name(), 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.name(), 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());
public void testAnnotatable() {
ASTCompilationUnit root = java9.parse("@A @B module foo { } ");
ASTModuleDeclaration mod = root.getModuleDeclaration();
assertTrue(mod.isAnnotationPresent("A"));
assertTrue(mod.isAnnotationPresent("B"));
}
}

View File

@ -11,12 +11,15 @@ import static org.junit.Assert.fail;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.java.BaseJavaTreeDumpTest;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class JDKVersionTest {
public class JDKVersionTest extends BaseJavaTreeDumpTest {
private final JavaParsingHelper java3 = JavaParsingHelper.DEFAULT
.withDefaultVersion("1.3")
@ -210,7 +213,7 @@ public class JDKVersionTest {
@Test(expected = ParseException.class)
public final void jdk9PrivateInterfaceMethodsInJava18() {
java8.parseResource("jdk9_private_interface_methods.java");
java8.parseResource("java9/jdk9_private_interface_methods.java");
}
@Test
@ -225,52 +228,52 @@ public class JDKVersionTest {
@Test
public final void jdk9PrivateInterfaceMethods() {
java9.parseResource("jdk9_private_interface_methods.java");
java9.parseResource("java9/jdk9_private_interface_methods.java");
}
@Test
public final void jdk9InvalidIdentifierInJava18() {
java8.parseResource("jdk9_invalid_identifier.java");
java8.parseResource("java9/jdk9_invalid_identifier.java");
}
@Test(expected = ParseException.class)
public final void jdk9InvalidIdentifier() {
java9.parseResource("jdk9_invalid_identifier.java");
java9.parseResource("java9/jdk9_invalid_identifier.java");
}
@Test(expected = ParseException.class)
public final void jdk9AnonymousDiamondInJava8() {
java8.parseResource("jdk9_anonymous_diamond.java");
java8.parseResource("java9/jdk9_anonymous_diamond.java");
}
@Test
public final void jdk9AnonymousDiamond() {
java9.parseResource("jdk9_anonymous_diamond.java");
java9.parseResource("java9/jdk9_anonymous_diamond.java");
}
@Test(expected = ParseException.class)
public final void jdk9ModuleInfoInJava8() {
java8.parseResource("jdk9_module_info.java");
java8.parseResource("java9/jdk9_module_info.java");
}
@Test
public final void jdk9ModuleInfo() {
java9.parseResource("jdk9_module_info.java");
java9.parseResource("java9/jdk9_module_info.java");
}
@Test
public void testAnnotatedModule() {
java9.parseResource("jdk9_module_info_with_annot.java");
java9.parseResource("java9/jdk9_module_info_with_annot.java");
}
@Test(expected = ParseException.class)
public final void jdk9TryWithResourcesInJava8() {
java8.parseResource("jdk9_try_with_resources.java");
java8.parseResource("java9/jdk9_try_with_resources.java");
}
@Test
public final void jdk9TryWithResources() {
java9.parseResource("jdk9_try_with_resources.java");
java9.parseResource("java9/jdk9_try_with_resources.java");
}
@Test
@ -292,4 +295,9 @@ public class JDKVersionTest {
assertTrue(e.getMessage().startsWith("Line 19"));
}
}
@Override
public @NonNull BaseParsingHelper<?, ?> getParser() {
return java9;
}
}

View File

@ -0,0 +1,34 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.java.BaseJavaTreeDumpTest;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java9TreeDumpTest extends BaseJavaTreeDumpTest {
private final JavaParsingHelper java9 = JavaParsingHelper.DEFAULT
.withDefaultVersion("9")
.withResourceContext(Java9TreeDumpTest.class, "jdkversiontests/java9");
@Test
public void testModule() {
doTest("jdk9_module_info");
}
@Test
public void testAnnotatedModule() {
doTest("jdk9_module_info_with_annot");
}
@Override
public @NonNull BaseParsingHelper<?, ?> getParser() {
return java9;
}
}

View File

@ -6,8 +6,6 @@ package net.sourceforge.pmd.lang.java.ast;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.List;
@ -66,27 +64,6 @@ public class SimpleNodeTest extends BaseParserTest {
assertFalse(ucd.getChild(0) instanceof ASTImplementsList);
}
@Test
public void testColumnsOnQualifiedName() {
for (Node node : java.getNodes(ASTName.class, QUALIFIED_NAME)) {
if (node.getImage().equals("java.io.File")) {
TestUtilsKt.assertPosition(node, 1, 8, 1, 20);
}
}
}
@Test
public void testLineNumbersForNameSplitOverTwoLines() {
for (Node node : java.getNodes(ASTName.class, BROKEN_LINE_IN_NAME)) {
if (node.getImage().equals("java.io.File")) {
TestUtilsKt.assertPosition(node, 1, 8, 2, 5);
}
if (node.getImage().equals("Foo")) {
TestUtilsKt.assertPosition(node, 2, 15, 2, 19);
}
}
}
// @Test
// public void testLineNumbersAreSetOnAllSiblings() {
// for (ASTBlock b : java.getNodes(ASTBlock.class, LINE_NUMBERS_ON_SIBLINGS)) {
@ -172,22 +149,6 @@ public class SimpleNodeTest extends BaseParserTest {
// assertEquals(x2, n);
// }
@Test
public void testParentMethods() {
ASTCompilationUnit u = java.parse(TEST1);
ASTMethodDeclarator d = u.getFirstDescendantOfType(ASTMethodDeclarator.class);
assertSame("getFirstParentOfType ASTMethodDeclaration", d.getParent(),
d.getFirstParentOfType(ASTMethodDeclaration.class));
assertNull("getFirstParentOfType ASTName", d.getFirstParentOfType(ASTName.class));
assertSame("getNthParent 1", d.getParent(), d.getNthParent(1));
assertSame("getNthParent 2", d.getParent().getParent(), d.getNthParent(2));
assertSame("getNthParent 6", u, d.getNthParent(6));
assertNull("getNthParent 7", d.getNthParent(7));
assertNull("getNthParent 8", d.getNthParent(8));
}
private static final String TEST1 = "public class Test {\n void bar(String s) {\n s = s.toLowerCase();\n }\n}";
@Ignore

View File

@ -4,6 +4,8 @@
package net.sourceforge.pmd.lang.java.ast
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeSameInstanceAs
import net.sourceforge.pmd.lang.ast.test.*
@ -70,6 +72,30 @@ class TypeDisambiguationTest : ParserTestSpec({
}
}
parserTest("Package names in module") {
val code = """
module java.base {
opens java.util;
provides java.util.Map with java.util.HashMap;
// ^^^^^^^^^ ^^^^^^^^^
}
"""
doTest("test without disambig") {
val acu = parser.parse(code)
acu.descendants(ASTAmbiguousName::class.java).toList().shouldHaveSize(2)
}
doTest("test with disambig") {
enableProcessing()
val acu = parser.parse(code)
acu.descendants(ASTAmbiguousName::class.java).toList().shouldBeEmpty()
}
}
parserTest("Failures") {
val logger = enableProcessing()

View File

@ -12,4 +12,5 @@ open module com.example.foo {
uses com.example.foo.spi.Intf;
provides com.example.foo.spi.Intf with com.example.foo.Impl;
}
provides com.example.foo.spi.Intf2 with com.example.foo.Impl, com.example.foo.Impl2;
}

View File

@ -0,0 +1,20 @@
+- CompilationUnit[@PackageName = ""]
+- ModuleDeclaration[@Name = "com.example.foo", @Open = "true"]
+- ModuleName[@Name = "com.example.foo"]
+- ModuleRequiresDirective[@Static = "false", @Transitive = "false"]
| +- ModuleName[@Name = "com.example.foo.http"]
+- ModuleRequiresDirective[@Static = "false", @Transitive = "false"]
| +- ModuleName[@Name = "java.logging"]
+- ModuleRequiresDirective[@Static = "false", @Transitive = "true"]
| +- ModuleName[@Name = "com.example.foo.network"]
+- ModuleExportsDirective[@PackageName = "com.example.foo.bar"]
+- ModuleExportsDirective[@PackageName = "com.example.foo.internal"]
| +- ModuleName[@Name = "com.example.foo.probe"]
+- ModuleUsesDirective[@PackageName = "com.example.foo.spi.Intf"]
+- ModuleProvidesDirective[]
| +- ClassOrInterfaceType[@FullyQualified = "true", @SimpleName = "Intf"]
| +- ClassOrInterfaceType[@FullyQualified = "true", @SimpleName = "Impl"]
+- ModuleProvidesDirective[]
+- ClassOrInterfaceType[@FullyQualified = "true", @SimpleName = "Intf2"]
+- ClassOrInterfaceType[@FullyQualified = "true", @SimpleName = "Impl"]
+- ClassOrInterfaceType[@FullyQualified = "true", @SimpleName = "Impl2"]

View File

@ -0,0 +1,10 @@
+- CompilationUnit[@PackageName = ""]
+- ModuleDeclaration[@Name = "jdk.pack", @Open = "false"]
+- Annotation[@SimpleName = "Deprecated"]
| +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"]
| +- AnnotationMemberList[@Empty = "false", @Size = "2"]
| +- MemberValuePair[@Image = "since", @Name = "since", @Shorthand = "false"]
| | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "11", @Empty = "false", @Image = "\"11\"", @Length = "2", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"]
| +- MemberValuePair[@Image = "forRemoval", @Name = "forRemoval", @Shorthand = "false"]
| +- BooleanLiteral[@CompileTimeConstant = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @True = "true"]
+- ModuleName[@Name = "jdk.pack"]

View File

@ -1,3 +1,7 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
public class InputJava9TryWithResources {
public static void main() {
MyResource resource1 = new MyResource();