From 9eb6602b7c9e6f5a983142371a550fd54d847695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 22 Aug 2020 21:39:14 +0200 Subject: [PATCH] Fix merge wip --- pmd-java/etc/grammar/Java.jjt | 47 +++++------------ .../ast/ASTClassOrInterfaceDeclaration.java | 4 +- .../pmd/lang/java/ast/ASTModifierList.java | 1 + .../pmd/lang/java/ast/ASTPermitsList.java | 12 ++--- .../pmd/lang/java/ast/JModifier.java | 30 +++++++++-- .../ast/internal/LanguageLevelChecker.java | 6 +-- .../pmd/lang/java/symbols/JClassSymbol.java | 5 ++ .../pmd/lang/java/ast/ASTLiteralTest.kt | 5 +- .../lang/java/ast/ASTSwitchExpressionTests.kt | 2 +- .../pmd/lang/java/ast/Java15KotlinTest.kt | 50 ++++++++----------- .../pmd/lang/java/ast/KotlinTestingDsl.kt | 2 + 11 files changed, 79 insertions(+), 85 deletions(-) diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 6c63fd9ca2..48d8ae0d44 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -951,8 +951,8 @@ void ModifierList(): | "volatile" { modifiers.add(JModifier.VOLATILE); } | "strictfp" { modifiers.add(JModifier.STRICTFP); } | "default" { modifiers.add(JModifier.DEFAULT); } - | LOOKAHEAD({isKeyword("sealed")}) { modifiers |= AccessNode.SEALED; } - | LOOKAHEAD({isNonSealedModifier()}) { modifiers |= AccessNode.NON_SEALED; } + | LOOKAHEAD({isKeyword("sealed")}) { modifiers.add(JModifier.SEALED); } + | LOOKAHEAD({isNonSealedModifier()}) { modifiers.add(JModifier.NON_SEALED); } | Annotation() ) )* @@ -2162,7 +2162,7 @@ void Block() : } void BlockStatement() #void: -{int mods = 0;} +{} { LOOKAHEAD( { isNextTokenAnAssert() } ) AssertStatement() | LOOKAHEAD( { isYieldStart() } ) YieldStatement() @@ -2171,34 +2171,14 @@ void BlockStatement() #void: // or a local variable declaration follows. // This allows more modifiers for local variables than actually allowed // and the annotations for local variables need to be moved in the AST down again. - mods=Modifiers() + ModifierList() ( - LOOKAHEAD({localTypeDeclLookahead()}) LocalTypeDecl(mods) + LOOKAHEAD({localTypeDeclLookahead()}) LocalTypeDecl() | - { - List annotations = new ArrayList(); - while (jjtree.peekNode() instanceof ASTAnnotation) { - annotations.add((ASTAnnotation) jjtree.popNode()); - } - } - LocalVariableDeclaration() - { - ASTLocalVariableDeclaration localVarDecl = (ASTLocalVariableDeclaration) jjtree.peekNode(); - if ((mods & AccessNode.FINAL) == AccessNode.FINAL) { - localVarDecl.setFinal(true); - } - if (!annotations.isEmpty()) { - Collections.reverse(annotations); - for (ASTAnnotation a : annotations) { - localVarDecl.insertChild(a, 0); - } - } - } - ";" + LocalVariableDeclaration() ";" ) | LOOKAHEAD({classModifierLookahead() || localTypeDeclLookahead()}) - mods=Modifiers() - LocalTypeDecl(mods) + ModifierList() LocalTypeDecl() | LOOKAHEAD(Type() ) LocalVariableDeclaration() ";" { // make it so that the LocalVariableDeclaration's last token is the semicolon @@ -2214,16 +2194,13 @@ void BlockStatement() #void: Statement() } -void LocalTypeDecl(int mods) #LocalClassStatement: +void LocalTypeDecl() #LocalClassStatement: {} { - ( - LOOKAHEAD() ClassOrInterfaceDeclaration(mods) - | LOOKAHEAD() ClassOrInterfaceDeclaration(mods) - | LOOKAHEAD({isKeyword("record")}) RecordDeclaration(mods) - | LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(mods) - | AnnotationTypeDeclaration(mods) - ) + ClassOrInterfaceDeclaration() + | AnnotationTypeDeclaration() + | LOOKAHEAD({isKeyword("record")}) RecordDeclaration() + | LOOKAHEAD({isKeyword("enum")}) EnumDeclaration() } /* diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java index 6d7d3c0b6c..8e37479791 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java @@ -4,12 +4,10 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.Collections; import java.util.List; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.util.CollectionUtil; /** @@ -75,7 +73,7 @@ public final class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclara @Experimental public List getPermittedSubclasses() { - return ASTList.orEmpty(children(ASTPermitsList.class)); + return ASTList.orEmpty(children(ASTPermitsList.class).first()); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java index 8645a3108d..74b7945a6d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java @@ -167,6 +167,7 @@ public final class ASTModifierList extends AbstractJavaNode { private static final EffectiveModifierVisitor INSTANCE = new EffectiveModifierVisitor(); // TODO strictfp modifier is also implicitly given to descendants + // TODO final modifier is implicitly given to direct subclasses of sealed interface/class @Override public Void visit(ASTAnyTypeDeclaration node, Set effective) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java index 890e4293d8..75a4e18e9e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java @@ -4,9 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.Iterator; - import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.lang.java.ast.ASTList.ASTNonEmptyList; /** @@ -23,19 +22,14 @@ import net.sourceforge.pmd.annotation.Experimental; * */ @Experimental -public final class ASTPermitsList extends AbstractJavaNode implements Iterable { +public final class ASTPermitsList extends ASTNonEmptyList { ASTPermitsList(int id) { - super(id); + super(id, ASTClassOrInterfaceType.class); } @Override protected R acceptVisitor(JavaVisitor visitor, P data) { return visitor.visit(this, data); } - - @Override - public Iterator iterator() { - return children(ASTClassOrInterfaceType.class).iterator(); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java index 649a96810e..db8230d3c1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java @@ -9,6 +9,8 @@ import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Locale; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; + /** * A Java modifier. The ordering of constants respects the ordering * recommended by the JLS. @@ -21,6 +23,11 @@ public enum JModifier { PROTECTED(Modifier.PROTECTED), PRIVATE(Modifier.PRIVATE), + /** Modifier {@code "sealed"} (preview feature of JDK 15). */ + SEALED(0), + /** Modifier {@code "non-sealed"} (preview feature of JDK 15). */ + NON_SEALED("non-sealed", 0), + ABSTRACT(Modifier.ABSTRACT), STATIC(Modifier.STATIC), FINAL(Modifier.FINAL), @@ -38,17 +45,34 @@ public enum JModifier { VOLATILE(Modifier.VOLATILE); - private final String token = name().toLowerCase(Locale.ROOT); + private final String token; private final int reflect; JModifier(int reflect) { + this.token = name().toLowerCase(Locale.ROOT); + this.reflect = reflect; + } + + JModifier(String token, int reflect) { + this.token = token; this.reflect = reflect; } /** * Returns the constant of java.lang.reflect.Modifier that this - * modifier corresponds to. Be aware that {@link #DEFAULT} has - * no equivalent in {@link Modifier}. + * modifier corresponds to. Be aware that the following constants + * are source-level modifiers only, for which this method returns 0: + *
    + *
  • {@link #DEFAULT}: this doesn't exist at the class file level. + * A default method is a non-static non-abstract public method declared + * in an interface ({@link JMethodSymbol#isDefault()}). + *
  • {@link #SEALED}: a sealed class has an attribute {@code PermittedSubclasses} + * with a non-zero length (in the compiled class file) + *
  • {@link #NON_SEALED}: this doesn't exist at the class file level at all. + * But a class must have the non-sealed modifier in source if it + * is neither sealed, nor final, and appears in the {@code PermittedSubclasses} + * attribute of some direct supertype. + *
*/ public int getReflectMod() { return reflect; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java index 45d9577069..753f5b73e4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java @@ -15,7 +15,6 @@ import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind; import net.sourceforge.pmd.lang.java.ast.ASTAssertStatement; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.java.ast.ASTCastExpression; @@ -45,7 +44,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters; import net.sourceforge.pmd.lang.java.ast.ASTTypeTestPattern; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.ASTYieldStatement; -import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.JavaVisitorBase; @@ -478,9 +476,9 @@ public class LanguageLevelChecker { @Override public Void visit(ASTAnyTypeDeclaration node, T data) { - if ((node.getModifiers() & (AccessNode.SEALED | AccessNode.NON_SEALED)) != 0) { + if (node.getModifiers().hasAnyExplicitly(JModifier.SEALED, JModifier.NON_SEALED)) { check(node, PreviewFeature.SEALED_CLASSES, data); - } else if (node.isLocal() && node.getTypeKind() != TypeKind.CLASS) { + } else if (node.isLocal() && node.isInterface() || node.isEnum()) { check(node, PreviewFeature.SEALED_CLASSES, data); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java index 2b273a1f71..85dbe28318 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java @@ -173,6 +173,11 @@ public interface JClassSymbol extends JTypeDeclSymbol, boolean isAnonymousClass(); + // todo isSealed + getPermittedSubclasses + // (isNonSealed is not so useful I think) + + // todo getEnumConstants + /** * This returns true if this is not an interface, primitive or array. */ diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt index 62c084ebb4..8150db9691 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt @@ -12,6 +12,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType.* import net.sourceforge.pmd.lang.java.ast.JavaVersion.* import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Earliest import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest +import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.since import net.sourceforge.pmd.lang.java.ast.UnaryOp.UNARY_MINUS /** @@ -45,7 +46,7 @@ class ASTLiteralTest : ParserTestSpec({ } } - parserTest("Text block literal", javaVersion = J13__PREVIEW) { + parserTest("Text block literal", javaVersions = since(J14__PREVIEW)) { val delim = "\"\"\"" @@ -127,7 +128,7 @@ $delim } - parserTest("Text block literal on non-JDK13 preview", javaVersions = JavaVersion.except(J13__PREVIEW, J14__PREVIEW)) { + parserTest("Text block literal on non-JDK13 preview", javaVersions = Earliest.rangeTo(J14)) { val delim = "\"\"\"" diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpressionTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpressionTests.kt index 448e6b59af..19ca27bb07 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpressionTests.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTSwitchExpressionTests.kt @@ -18,7 +18,7 @@ import net.sourceforge.pmd.lang.java.ast.UnaryOp.UNARY_MINUS */ class ASTSwitchExpressionTests : ParserTestSpec({ - val switchVersions = listOf(J13__PREVIEW, J14, J14__PREVIEW) + val switchVersions = JavaVersion.since(J14) val notSwitchVersions = JavaVersion.except(switchVersions) parserTest("No switch expr before j13 preview", javaVersions = notSwitchVersions) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java15KotlinTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java15KotlinTest.kt index 6bc3facde9..ec2676dcf1 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java15KotlinTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java15KotlinTest.kt @@ -6,38 +6,32 @@ package net.sourceforge.pmd.lang.java.ast import net.sourceforge.pmd.lang.ast.test.shouldBe -class Java15KotlinTest: ParserTestSpec( { +class Java15KotlinTest : ParserTestSpec({ // Note: More tests are in ASTLiteralTest. parserTest("textBlocks", javaVersions = JavaVersion.J15..JavaVersion.Latest) { - ("\"\"\"\n" + - " \n" + - " \n" + - "

Hello, world

\n" + - " \n" + - " \n" + - " \"\"\"") should matchExpr { - child { - child { - child { - it::isTextBlock shouldBe true - it::getEscapedStringLiteral shouldBe - "\"\"\"\n" + - " \n" + - " \n" + - "

Hello, world

\n" + - " \n" + - " \n" + - " \"\"\"" - it::getTextBlockContent shouldBe - "\n" + - " \n" + - "

Hello, world

\n" + - " \n" + - "\n" - } - } + + val tblock = "\"\"\"\n" + + // 4 spaces of insignificant indentation + " \n" + + " \n" + + "

Hello, world

\n" + + " \n" + + " \n" + + " \"\"\"" + + inContext(ExpressionParsingCtx) { + tblock should parseAs { + textBlock { + it::getConstValue shouldBe "\n" + + " \n" + + "

Hello, world

\n" + + " \n" + + "\n" + + it::getImage shouldBe tblock } + } } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt index 0c889e864b..2cddf62fe2 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt @@ -56,6 +56,8 @@ enum class JavaVersion : Comparable { val Latest = values().last() val Earliest = values().first() + fun since(v: JavaVersion) = v.rangeTo(Latest) + fun except(v1: JavaVersion, vararg versions: JavaVersion) = values().toList() - v1 - versions