[java] Support Unnamed Classes and Instance Main Methods for Java 21 Preview
JEP 445
This commit is contained in:
@ -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:
|
||||
{}
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -698,6 +698,9 @@ public final class SymbolTableResolver {
|
||||
}
|
||||
|
||||
private JClassType enclosing() {
|
||||
if (enclosingType.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return enclosingType.getFirst().getTypeMirror();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
@ -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]
|
@ -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());
|
||||
}
|
@ -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]
|
@ -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);
|
||||
}
|
@ -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]
|
Reference in New Issue
Block a user