[java] Support Unnamed Classes and Instance Main Methods for Java 21 Preview

JEP 445
This commit is contained in:
Andreas Dangel
2023-08-05 17:10:21 +02:00
parent 216dd09405
commit f4c86b25ae
21 changed files with 431 additions and 16 deletions

View File

@ -1131,11 +1131,23 @@ ASTCompilationUnit CompilationUnit() :
{
[ LOOKAHEAD( ( Annotation() )* "package" ) PackageDeclaration() ( EmptyDeclaration() )* ]
( ImportDeclaration() ( EmptyDeclaration() )* )*
// ModularCompilationUnit:
// the module decl lookahead needs to be before the type declaration branch,
// looking for annotations + "open" | "module" will fail faster if it's *not*
// a module (most common case)
[ LOOKAHEAD(ModuleDeclLahead()) ModuleDeclaration() ( EmptyDeclaration() )* ]
// OrdinaryCompilationUnit: -> TopLevelClassOrInterfaceDeclaration
( TypeDeclaration() ( EmptyDeclaration() )* )*
// UnnamedClassCompilationUnit:
[
( LOOKAHEAD(3) ClassMemberDeclarationNoMethod() )*
ModifierList() MethodDeclaration()
( ClassOrInterfaceBodyDeclaration() )*
]
<EOF>
{
jjtThis.setComments(token_source.comments);
@ -1143,6 +1155,20 @@ ASTCompilationUnit CompilationUnit() :
}
}
// see ClassOrInterfaceBodyDeclaration()
void ClassMemberDeclarationNoMethod() #void:
{}
{
ModifierList()
( LOOKAHEAD(3) ClassOrInterfaceDeclaration()
| LOOKAHEAD({isKeyword("enum")}) EnumDeclaration()
| LOOKAHEAD({isKeyword("record")}) RecordDeclaration()
| LOOKAHEAD( [ TypeParameters() ] <IDENTIFIER> "(" ) ConstructorDeclaration()
| LOOKAHEAD( Type() <IDENTIFIER> (AnnotationList() "[" "]")* ( "," | "=" | ";" ) ) FieldDeclaration()
| LOOKAHEAD(2) AnnotationTypeDeclaration()
)
}
private void ModuleDeclLahead() #void:
{}
{

View File

@ -9,6 +9,7 @@ import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.ast.AstInfo;
import net.sourceforge.pmd.lang.ast.NodeStream;
import net.sourceforge.pmd.lang.ast.RootNode;
@ -16,6 +17,7 @@ import net.sourceforge.pmd.lang.ast.impl.GenericNode;
import net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
import net.sourceforge.pmd.lang.rule.xpath.NoAttribute;
/**
@ -23,7 +25,8 @@ import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
*
* <pre class="grammar">
*
* CompilationUnit ::= RegularCompilationUnit
* CompilationUnit ::= OrdinaryCompilationUnit
* | UnnamedClassCompilationUnit
* | ModularCompilationUnit
*
* RegularCompilationUnit ::=
@ -31,11 +34,20 @@ import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
* {@linkplain ASTImportDeclaration ImportDeclaration}*
* {@linkplain ASTAnyTypeDeclaration TypeDeclaration}*
*
* UnnamedClassCompilationUnit ::=
* {@linkplain ASTImportDeclaration ImportDeclaration}*
* {@linkplain ASTFieldDeclaration FieldDeclaration}*
* {@linkplain ASTMethodDeclaration MethodDeclaration}
* {@linkplain ASTBodyDeclaration BodyDeclaration}*
*
* ModularCompilationUnit ::=
* {@linkplain ASTImportDeclaration ImportDeclaration}*
* {@linkplain ASTModuleDeclaration ModuleDeclaration}
*
* </pre>
*
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a> (Java 21)
* @see #isUnnamedClass()
*/
public final class ASTCompilationUnit extends AbstractJavaNode implements JavaNode, GenericNode<JavaNode>, RootNode {
@ -126,4 +138,9 @@ public final class ASTCompilationUnit extends AbstractJavaNode implements JavaNo
return lazyTypeResolver;
}
@Experimental
@NoAttribute
public boolean isUnnamedClass() {
return children(ASTMethodDeclaration.class).nonEmpty();
}
}

View File

@ -166,6 +166,20 @@ public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDecla
&& "main".equals(this.getName())
&& this.isVoid()
&& this.getArity() == 1
&& TypeTestUtil.isExactlyA(String[].class, this.getFormalParameters().get(0));
&& TypeTestUtil.isExactlyA(String[].class, this.getFormalParameters().get(0))
|| isMainMethodUnnamedClass();
}
/**
* With JEP 445 (Java 21 Preview) the main method does not need to be static anymore and
* does not need to be public or have a formal parameter.
*/
private boolean isMainMethodUnnamedClass() {
return this.getRoot().isUnnamedClass()
&& "main".equals(this.getName())
&& !this.hasModifiers(JModifier.PRIVATE)
&& this.isVoid()
&& (this.getArity() == 0
|| this.getArity() == 1 && TypeTestUtil.isExactlyA(String[].class, this.getFormalParameters().get(0)));
}
}

View File

@ -206,7 +206,8 @@ public final class ASTModifierList extends AbstractJavaNode {
@Override
public Void visit(ASTFieldDeclaration node, Set<JModifier> effective) {
if (node.getEnclosingType().isInterface()) {
ASTAnyTypeDeclaration enclosingType = node.getEnclosingType();
if (enclosingType != null && enclosingType.isInterface()) {
effective.add(PUBLIC);
effective.add(STATIC);
effective.add(FINAL);
@ -266,8 +267,8 @@ public final class ASTModifierList extends AbstractJavaNode {
@Override
public Void visit(ASTMethodDeclaration node, Set<JModifier> effective) {
if (node.getEnclosingType().isInterface()) {
ASTAnyTypeDeclaration enclosingType = node.getEnclosingType();
if (enclosingType != null && enclosingType.isInterface()) {
Set<JModifier> declared = node.getModifiers().explicitModifiers;

View File

@ -151,6 +151,10 @@ public final class JavaAstUtils {
}
public static boolean hasField(ASTAnyTypeDeclaration node, String name) {
if (node == null) {
return false;
}
for (JFieldSymbol f : node.getSymbol().getDeclaredFields()) {
String fname = f.getSimpleName();
if (fname.startsWith("m_") || fname.startsWith("_")) {
@ -445,7 +449,7 @@ public final class JavaAstUtils {
}
public static boolean hasAnyAnnotation(Annotatable node, Collection<String> qualifiedNames) {
return qualifiedNames.stream().anyMatch(node::isAnnotationPresent);
return node != null && qualifiedNames.stream().anyMatch(node::isAnnotationPresent);
}
/**
@ -586,8 +590,10 @@ public final class JavaAstUtils {
if (usage instanceof ASTVariableAccess) {
return !Modifier.isStatic(((JFieldSymbol) symbol).getModifiers());
} else if (usage instanceof ASTFieldAccess) {
return Objects.equals(((JFieldSymbol) symbol).getEnclosingClass(),
usage.getEnclosingType().getSymbol());
if (usage.getEnclosingType() != null) {
return Objects.equals(((JFieldSymbol) symbol).getEnclosingClass(),
usage.getEnclosingType().getSymbol());
}
}
return false;
}

View File

@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAssertStatement;
import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
import net.sourceforge.pmd.lang.java.ast.ASTCatchClause;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTForeachStatement;
@ -86,7 +87,7 @@ public class LanguageLevelChecker<T> {
public void check(JavaNode node) {
T accumulator = reportingStrategy.createAccumulator();
node.descendants(JavaNode.class).crossFindBoundaries().forEach(n -> n.acceptVisitor(visitor, accumulator));
node.descendantsOrSelf().crossFindBoundaries().filterIs(JavaNode.class).forEach(n -> n.acceptVisitor(visitor, accumulator));
reportingStrategy.done(accumulator);
}
@ -178,6 +179,12 @@ public class LanguageLevelChecker<T> {
*/
UNNAMED_PATTERNS_AND_VARIABLES(21, 21, false),
/**
* Unnamed Classes and Instance Main Methods
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a> (Java 21)
*/
UNNAMED_CLASSES(21, 21, false),
; // SUPPRESS CHECKSTYLE enum trailing semi is awesome
@ -409,6 +416,14 @@ public class LanguageLevelChecker<T> {
return null;
}
@Override
public Void visit(ASTCompilationUnit node, T data) {
if (node.isUnnamedClass()) {
check(node, PreviewFeature.UNNAMED_CLASSES, data);
}
return null;
}
@Override
public Void visit(ASTStringLiteral node, T data) {
if (node.isStringLiteral() && SPACE_ESCAPE_PATTERN.matcher(node.getImage()).find()) {
@ -540,7 +555,7 @@ public class LanguageLevelChecker<T> {
check(node, RegularLanguageFeature.DEFAULT_METHODS, data);
}
if (node.isPrivate() && node.getEnclosingType().isInterface()) {
if (node.isPrivate() && node.getEnclosingType() != null && node.getEnclosingType().isInterface()) {
check(node, RegularLanguageFeature.PRIVATE_METHODS_IN_INTERFACES, data);
}

View File

@ -150,7 +150,9 @@ public class CommentDefaultAccessModifierRule extends AbstractJavaRulechainRule
return isMissingComment(decl)
&& isNotIgnored(decl)
&& !(decl instanceof ASTFieldDeclaration && enclosing.isAnnotationPresent("lombok.Value"));
&& !(decl instanceof ASTFieldDeclaration
&& enclosing != null
&& enclosing.isAnnotationPresent("lombok.Value"));
}
private boolean isMissingComment(AccessNode decl) {

View File

@ -127,7 +127,7 @@ public class CouplingBetweenObjectsRule extends AbstractJavaRule {
* @return boolean true if variableType is not what we care about
*/
private boolean ignoreType(ASTType typeNode, JTypeMirror t) {
if (typeNode.getEnclosingType().getSymbol().equals(t.getSymbol())) {
if (typeNode.getEnclosingType() != null && typeNode.getEnclosingType().getSymbol().equals(t.getSymbol())) {
return true;
}
JTypeDeclSymbol symbol = t.getSymbol();

View File

@ -138,7 +138,8 @@ public final class TestFrameworksUtil {
* True if this is a {@code TestCase} class for Junit 3.
*/
public static boolean isJUnit3Class(ASTAnyTypeDeclaration node) {
return node.isRegularClass()
return node != null
&& node.isRegularClass()
&& !node.isNested()
&& !node.isAbstract()
&& TypeTestUtil.isA(JUNIT3_CLASS_NAME, node);

View File

@ -6,9 +6,11 @@ package net.sourceforge.pmd.lang.java.symbols.internal.ast;
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
@ -76,7 +78,7 @@ final class AstSymFactory {
return new AstClassSym(klass, this, enclosing);
}
JClassSymbol setClassSymbol(@NonNull ASTCompilationUnit compilationUnit) {
return new AstUnnamedClassSym(compilationUnit, this);
}
}

View File

@ -78,6 +78,17 @@ final class AstSymbolMakerVisitor extends JavaVisitorBase<AstSymFactory, Void> {
|| node.getParent() instanceof ASTFormalParameter);
}
@Override
public Void visit(ASTCompilationUnit node, AstSymFactory data) {
if (node.isUnnamedClass()) {
JClassSymbol sym = data.setClassSymbol(node);
enclosingSymbols.push(sym);
visitChildren(node, data);
enclosingSymbols.pop();
return null;
}
return super.visit(node, data);
}
@Override
public Void visitTypeDecl(ASTAnyTypeDeclaration node, AstSymFactory data) {

View File

@ -0,0 +1,179 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.symbols.internal.ast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.JModifier;
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.types.JClassType;
import net.sourceforge.pmd.lang.java.types.JTypeVar;
import net.sourceforge.pmd.lang.java.types.Substitution;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
class AstUnnamedClassSym implements JClassSymbol {
private ASTCompilationUnit node;
private final List<JMethodSymbol> declaredMethods;
private final List<JFieldSymbol> declaredFields;
AstUnnamedClassSym(ASTCompilationUnit node, AstSymFactory factory) {
this.node = node;
final List<JMethodSymbol> myMethods = new ArrayList<>();
final List<JFieldSymbol> myFields = new ArrayList<>();
node.children(ASTMethodDeclaration.class).forEach(dnode -> {
myMethods.add(new AstMethodSym((ASTMethodDeclaration) dnode, factory, this));
});
node.children(ASTFieldDeclaration.class).forEach(dnode -> {
for (ASTVariableDeclaratorId varId : dnode.getVarIds()) {
myFields.add(new AstFieldSym(varId, factory, this));
}
});
this.declaredMethods = Collections.unmodifiableList(myMethods);
this.declaredFields = Collections.unmodifiableList(myFields);
}
@Override
public int getModifiers() {
return JModifier.toReflect(EnumSet.of(JModifier.FINAL));
}
@Override
public @Nullable JClassSymbol getEnclosingClass() {
return null;
}
@Override
public @NonNull String getPackageName() {
return "";
}
@Override
public @NonNull String getBinaryName() {
return "UnnamedClass";
}
@Override
public @Nullable String getCanonicalName() {
return null;
}
@Override
public @Nullable JExecutableSymbol getEnclosingMethod() {
return null;
}
@Override
public List<JClassSymbol> getDeclaredClasses() {
return Collections.emptyList();
}
@Override
public List<JMethodSymbol> getDeclaredMethods() {
return declaredMethods;
}
@Override
public List<JConstructorSymbol> getConstructors() {
return Collections.emptyList();
}
@Override
public List<JFieldSymbol> getDeclaredFields() {
return declaredFields;
}
@Override
public List<JClassType> getSuperInterfaceTypes(Substitution substitution) {
return Collections.emptyList();
}
@Override
public @Nullable JClassType getSuperclassType(Substitution substitution) {
return null;
}
@Override
public @Nullable JClassSymbol getSuperclass() {
return null;
}
@Override
public List<JClassSymbol> getSuperInterfaces() {
return Collections.emptyList();
}
@Override
public @Nullable JTypeDeclSymbol getArrayComponent() {
return null;
}
@Override
public boolean isArray() {
return false;
}
@Override
public boolean isPrimitive() {
return false;
}
@Override
public boolean isEnum() {
return false;
}
@Override
public boolean isRecord() {
return false;
}
@Override
public boolean isAnnotation() {
return false;
}
@Override
public boolean isLocalClass() {
return false;
}
@Override
public boolean isAnonymousClass() {
return false;
}
@Override
public TypeSystem getTypeSystem() {
return node.getTypeSystem();
}
@Override
public @NonNull String getSimpleName() {
return "<UnnamedClass>";
}
@Override
public List<JTypeVar> getTypeParameters() {
return Collections.emptyList();
}
}

View File

@ -698,6 +698,9 @@ public final class SymbolTableResolver {
}
private JClassType enclosing() {
if (enclosingType.isEmpty()) {
return null;
}
return enclosingType.getFirst().getTypeMirror();
}

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.java.types.internal.infer.ast;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -51,6 +52,8 @@ class MethodInvocMirror extends BaseInvocMirror<ASTMethodCall> implements Invoca
if (lhs == null) {
// already filters accessibility
return myNode.getSymbolTable().methods().resolve(getName());
} else if (myNode.getEnclosingType() == null) {
return Collections.emptyList();
} else {
JTypeMirror lhsType;
if (lhs instanceof ASTConstructorCall) {

View File

@ -7,6 +7,8 @@ package net.sourceforge.pmd.lang.java.ast;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -67,4 +69,41 @@ class Java21PreviewTreeDumpTest extends BaseTreeDumpTest {
thrown = assertThrows(ParseException.class, () -> java21.parseResource("Jep443_UnnamedPatternsAndVariables2.java"));
assertThat(thrown.getMessage(), containsString("Unnamed patterns and variables is a preview feature of JDK 21, you should select your language version accordingly"));
}
@Test
void unnamedClasses1() {
doTest("Jep445_UnnamedClasses1");
ASTCompilationUnit compilationUnit = java21p.parseResource("Jep445_UnnamedClasses1.java");
assertTrue(compilationUnit.isUnnamedClass());
ASTMethodCall methodCall = compilationUnit.descendants(ASTMethodCall.class).first();
assertNotNull(methodCall.getTypeMirror());
}
@Test
void unnamedClasses2() {
doTest("Jep445_UnnamedClasses2");
}
@Test
void unnamedClasses3() {
doTest("Jep445_UnnamedClasses3");
}
@Test
void unnamedClassesBeforeJava21Preview() {
ParseException thrown = assertThrows(ParseException.class, () -> java21.parseResource("Jep445_UnnamedClasses1.java"));
assertThat(thrown.getMessage(), containsString("Unnamed classes is a preview feature of JDK 21, you should select your language version accordingly"));
}
@Test
void testOrdinaryCompilationUnit() {
ASTCompilationUnit compilationUnit = java21.parse("public class Foo { public static void main(String[] args) {}}");
assertFalse(compilationUnit.isUnnamedClass());
}
@Test
void testModularCompilationUnit() {
ASTCompilationUnit compilationUnit = java21.parse("module foo {}");
assertFalse(compilationUnit.isUnnamedClass());
}
}

View File

@ -0,0 +1,13 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/**
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a>
*/
void main() {
System.out.println("Hello World");
}

View File

@ -0,0 +1,13 @@
+- CompilationUnit[@PackageName = ""]
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "main", @MainMethod = true, @MethodName = "main", @Name = "main", @Native = false, @Overridden = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @Transient = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true, @Volatile = false]
+- ModifierList[]
+- VoidType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = false, @PrimitiveType = false, @TypeImage = "void"]
+- FormalParameters[@Empty = true, @Size = 0]
+- Block[@Empty = false, @Size = 1, @containsComment = false]
+- ExpressionStatement[]
+- MethodCall[@CompileTimeConstant = false, @Expression = true, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false]
+- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Expression = true, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false]
| +- TypeExpression[@CompileTimeConstant = false, @Expression = true, @ParenthesisDepth = 0, @Parenthesized = false]
| +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "System", @TypeImage = "System"]
+- ArgumentList[@Empty = false, @Size = 1]
+- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "Hello World", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"Hello World\"", @IntLiteral = false, @Length = 11, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false]

View File

@ -0,0 +1,15 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/**
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a>
*/
String greeting() { return "Hello, World!"; }
void main() {
System.out.println(greeting());
}

View File

@ -0,0 +1,21 @@
+- CompilationUnit[@PackageName = ""]
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "greeting", @MainMethod = false, @MethodName = "greeting", @Name = "greeting", @Native = false, @Overridden = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @Transient = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = false, @Volatile = false]
| +- ModifierList[]
| +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "String", @TypeImage = "String"]
| +- FormalParameters[@Empty = true, @Size = 0]
| +- Block[@Empty = false, @Size = 1, @containsComment = false]
| +- ReturnStatement[]
| +- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "Hello, World!", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"Hello, World!\"", @IntLiteral = false, @Length = 13, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false]
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "main", @MainMethod = true, @MethodName = "main", @Name = "main", @Native = false, @Overridden = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @Transient = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true, @Volatile = false]
+- ModifierList[]
+- VoidType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = false, @PrimitiveType = false, @TypeImage = "void"]
+- FormalParameters[@Empty = true, @Size = 0]
+- Block[@Empty = false, @Size = 1, @containsComment = false]
+- ExpressionStatement[]
+- MethodCall[@CompileTimeConstant = false, @Expression = true, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false]
+- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Expression = true, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false]
| +- TypeExpression[@CompileTimeConstant = false, @Expression = true, @ParenthesisDepth = 0, @Parenthesized = false]
| +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "System", @TypeImage = "System"]
+- ArgumentList[@Empty = false, @Size = 1]
+- MethodCall[@CompileTimeConstant = false, @Expression = true, @Image = "greeting", @MethodName = "greeting", @ParenthesisDepth = 0, @Parenthesized = false]
+- ArgumentList[@Empty = true, @Size = 0]

View File

@ -0,0 +1,15 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/**
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a>
*/
String greeting = "Hello, World!";
void main() {
System.out.println(greeting);
}

View File

@ -0,0 +1,19 @@
+- CompilationUnit[@PackageName = ""]
+- FieldDeclaration[@Abstract = false, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @Transient = false, @VariableName = "greeting", @Visibility = Visibility.V_PACKAGE, @Volatile = false]
| +- ModifierList[]
| +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "String", @TypeImage = "String"]
| +- VariableDeclarator[@Initializer = true, @Name = "greeting"]
| +- VariableDeclaratorId[@Abstract = false, @ArrayType = false, @EffectiveVisibility = Visibility.V_PACKAGE, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = true, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @Image = "greeting", @LambdaParameter = false, @LocalVariable = false, @Name = "greeting", @Native = false, @PackagePrivate = true, @PatternBinding = false, @Private = false, @Protected = false, @Public = false, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @Transient = false, @TypeInferred = false, @VariableName = "greeting", @Visibility = Visibility.V_PACKAGE, @Volatile = false]
| +- StringLiteral[@BooleanLiteral = false, @CharLiteral = false, @CompileTimeConstant = true, @ConstValue = "Hello, World!", @DoubleLiteral = false, @Empty = false, @Expression = true, @FloatLiteral = false, @Image = "\"Hello, World!\"", @IntLiteral = false, @Length = 13, @LongLiteral = false, @NullLiteral = false, @NumericLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @StringLiteral = true, @TextBlock = false]
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "main", @MainMethod = true, @MethodName = "main", @Name = "main", @Native = false, @Overridden = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyFinal = false, @SyntacticallyPublic = false, @SyntacticallyStatic = false, @Transient = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true, @Volatile = false]
+- ModifierList[]
+- VoidType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = false, @PrimitiveType = false, @TypeImage = "void"]
+- FormalParameters[@Empty = true, @Size = 0]
+- Block[@Empty = false, @Size = 1, @containsComment = false]
+- ExpressionStatement[]
+- MethodCall[@CompileTimeConstant = false, @Expression = true, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false]
+- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Expression = true, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false]
| +- TypeExpression[@CompileTimeConstant = false, @Expression = true, @ParenthesisDepth = 0, @Parenthesized = false]
| +- ClassOrInterfaceType[@ArrayDepth = 0, @ArrayType = false, @ClassOrInterfaceType = true, @FullyQualified = false, @PrimitiveType = false, @ReferenceToClassSameCompilationUnit = false, @SimpleName = "System", @TypeImage = "System"]
+- ArgumentList[@Empty = false, @Size = 1]
+- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Expression = true, @Image = "greeting", @Name = "greeting", @ParenthesisDepth = 0, @Parenthesized = false]