Merge branch 'java-16-support' into pmd7-java-16-support

This commit is contained in:
Andreas Dangel
2021-02-18 10:44:23 +01:00
62 changed files with 2135 additions and 457 deletions

View File

@ -19,10 +19,40 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
#### Java 16 Support
This release of PMD brings support for Java 16. PMD supports [JEP 394: Pattern Matching for instanceof](https://openjdk.java.net/jeps/394) and [JEP 395: Records](https://openjdk.java.net/jeps/395). Both have been promoted
to be a standard language feature of Java 16.
PMD also supports [JEP 397: Sealed Classes (Second Preview)](https://openjdk.java.net/jeps/397) as a preview
language feature. In order to analyze a project with PMD that uses these language features, you'll need to enable
it via the environment variable `PMD_JAVA_OPTS` and select the new language version `16-preview`:
export PMD_JAVA_OPTS=--enable-preview
./run.sh pmd -language java -version 16-preview ...
Note: Support for Java 14 preview language features have been removed. The version "14-preview" is no longer available.
### Fixed Issues
### API Changes
#### pmd-java
* The experimental class `ASTTypeTestPattern` has been renamed to {% jdoc java::lang.java.ast.ASTTypePattern %}
in order to align the naming to the JLS.
* The experimental class `ASTRecordConstructorDeclaration` has been renamed to {% jdoc java::lang.java.ast.ASTCompactConstructorDeclaration %}
in order to align the naming to the JLS.
* The AST types and APIs around Pattern Matching and Records are not experimental anymore:
* {% jdoc !!java::lang.java.ast.ASTVariableDeclaratorId#isPatternBinding() %}
* {% jdoc java::lang.java.ast.ASTPattern %}
* {% jdoc java::lang.java.ast.ASTTypePattern %}
* {% jdoc java::lang.java.ast.ASTRecordDeclaration %}
* {% jdoc java::lang.java.ast.ASTRecordComponentList %}
* {% jdoc java::lang.java.ast.ASTRecordComponent %}
* {% jdoc java::lang.java.ast.ASTRecordBody %}
* {% jdoc java::lang.java.ast.ASTCompactConstructorDeclaration %}
### External Contributions
{% endtocmaker %}

View File

@ -1,4 +1,10 @@
/**
* Remove support for Java 14 preview language features
* JEP 397: Sealed Classes (Second Preview) for Java16 Preview
* JEP 395: Records for Java16
* JEP 394: Pattern Matching for instanceof for Java16
* Andreas Dangel 02/2021
*====================================================================
* Remove support for Java 13 preview language features.
* Promote text blocks as a permanent language features with Java 15.
* Support Pattern Matching for instanceof with Java 15 Preview.
@ -273,7 +279,7 @@ class JavaParserImpl {
private boolean isRecordTypeSupported() {
return (jdkVersion == 14 || jdkVersion == 15) && preview;
return jdkVersion == 15 && preview || jdkVersion >= 16;
}
private boolean localTypesSupported() {
@ -281,10 +287,9 @@ class JavaParserImpl {
}
private boolean isSealedClassSupported() {
return jdkVersion == 15 && preview;
return jdkVersion == 15 && preview || jdkVersion == 16 && preview;
}
/**
* Keeps track during tree construction, whether we are currently building a switch label.
* A switch label must not contain a LambdaExpression.
@ -295,7 +300,6 @@ class JavaParserImpl {
*/
private boolean inSwitchLabel = false;
// This is a semantic LOOKAHEAD to determine if we're dealing with an assert
// Note that this can't be replaced with a syntactic lookahead
// since "assert" isn't a string literal token
@ -1037,8 +1041,8 @@ void PermittedSubclasses() #PermitsList:
throw new ParseException("ERROR: expecting permits");
}
}
(TypeAnnotation())* ClassOrInterfaceType()
( "," (TypeAnnotation())* ClassOrInterfaceType() )*
TypeName()
( "," TypeName() )*
}
void EnumDeclaration():
@ -1084,15 +1088,21 @@ void RecordDeclaration():
}
t=<IDENTIFIER> {jjtThis.setImage(t.image);}
[ TypeParameters() ]
RecordComponentList()
RecordHeader()
[ ImplementsList() ]
RecordBody()
}
void RecordHeader() #void:
{}
{
"(" RecordComponentList() ")"
}
void RecordComponentList() :
{}
{
"(" [ RecordComponent() ("," RecordComponent())* ] ")"
[ RecordComponent() ("," RecordComponent())* ]
}
void RecordComponent():
@ -1100,7 +1110,7 @@ void RecordComponent():
{
ModAnnotationList()
FormalParamType()
VariableIdWithDims()
VariableDeclaratorId()
}
void RecordBody():
@ -1114,18 +1124,18 @@ void RecordBody():
void RecordBodyDeclaration() #void :
{}
{
LOOKAHEAD(RecordCtorLookahead()) ModifierList() RecordConstructorDeclaration()
LOOKAHEAD(CompactConstructorDeclarationLookahead()) ModifierList() CompactConstructorDeclaration()
|
ClassOrInterfaceBodyDeclaration()
}
private void RecordCtorLookahead() #void:
private void CompactConstructorDeclarationLookahead() #void:
{}
{
ModifierList() <IDENTIFIER> "{"
}
void RecordConstructorDeclaration():
void CompactConstructorDeclaration():
{}
{
<IDENTIFIER> { jjtThis.setImage(token.image); }
@ -1313,6 +1323,17 @@ void Initializer() :
[ "static" {jjtThis.setStatic();} ] Block()
}
void TypeName() #ClassOrInterfaceType:
{
StringBuilder s = new StringBuilder();
Token t;
}
{
t=<IDENTIFIER> {s.append(t.image);}
( "." t=<IDENTIFIER> {s.append('.').append(t.image);} )*
{jjtThis.setImage(s.toString());}
}
@ -1669,7 +1690,11 @@ void InstanceOfExpression() #void:
RelationalExpression() [
LOOKAHEAD(1)
("instanceof"
AnnotatedRefType() [ BindingVarId() #TypeTestPattern(2) ]
(
AnnotatedRefType() [ BindingVarId() #TypePattern(2) ]
|
( LocalVarModifierList() ReferenceType() BindingVarId() ) #TypePattern(3)
)
{
jjtThis.setOp(BinaryOp.INSTANCEOF);
AbstractJavaNode top = jjtree.popNode();

View File

@ -29,9 +29,10 @@ public class JavaLanguageModule extends BaseLanguageModule {
addVersion("12", new JavaLanguageHandler(12));
addVersion("13", new JavaLanguageHandler(13));
addVersion("14", new JavaLanguageHandler(14));
addVersion("14-preview", new JavaLanguageHandler(14, true));
addDefaultVersion("15", new JavaLanguageHandler(15)); // 15 is the default
addVersion("15", new JavaLanguageHandler(15));
addVersion("15-preview", new JavaLanguageHandler(15, true));
addDefaultVersion("16", new JavaLanguageHandler(16)); // 16 is the default
addVersion("16-preview", new JavaLanguageHandler(16, true));
}
}

View File

@ -5,13 +5,11 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
/**
* This defines a compact constructor for a {@link ASTRecordDeclaration RecordDeclaration}
* (JDK 14 and JDK 15 preview feature). Compact constructors implicitly declares formal
* parameters corresponding to the record component list. These can be
* This defines a compact constructor for a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 16 feature).
* Compact constructors implicitly declares formal parameters corresponding to the record component list. These can be
* fetched from {@link #getSymbol()}.
*
* <p>Compact record constructors must be declared "public".
@ -20,16 +18,15 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
*
* <pre class="grammar">
*
* RecordConstructorDeclaration ::= {@link ASTModifierList Modifiers}
* CompactConstructorDeclaration ::= {@link ASTModifierList Modifiers}
* &lt;IDENTIFIER&gt;
* {@link ASTBlock Block}
*
* </pre>
*/
@Experimental
public final class ASTRecordConstructorDeclaration extends AbstractJavaNode implements ASTBodyDeclaration, SymbolDeclaratorNode, AccessNode {
public final class ASTCompactConstructorDeclaration extends AbstractJavaNode implements ASTBodyDeclaration, SymbolDeclaratorNode, AccessNode {
ASTRecordConstructorDeclaration(int id) {
ASTCompactConstructorDeclaration(int id) {
super(id);
}
@ -42,7 +39,7 @@ public final class ASTRecordConstructorDeclaration extends AbstractJavaNode impl
return getFirstChildOfType(ASTBlock.class);
}
public ASTRecordConstructorDeclaration getDeclarationNode() {
public ASTCompactConstructorDeclaration getDeclarationNode() {
return this;
}

View File

@ -1,29 +1,25 @@
/**
/*
* 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.Experimental;
/**
* A pattern (for pattern matching constructs like {@link ASTInstanceOfExpression InstanceOfExpression}).
* This is a JDK 14 and JDK 15 preview feature and is subject to change.
* This is a JDK 16 feature.
*
* <p>This interface will be implemented by all forms of patterns. For
* now, only type test patterns are supported. Record deconstruction
* patterns are in the works for JDK 15 preview.
*
* <p>See https://openjdk.java.net/jeps/305, https://openjdk.java.net/jeps/8235186
* patterns is planned for a future JDK version.
*
* <pre class="grammar">
*
* Pattern ::= {@link ASTTypeTestPattern TypeTestPattern}
* Pattern ::= {@link ASTTypePattern TypePattern}
*
* </pre>
*
* @see <a href="https://openjdk.java.net/jeps/394">JEP 394: Pattern Matching for instanceof</a>
*/
@Experimental
public interface ASTPattern extends JavaNode {
}

View File

@ -11,14 +11,14 @@ import net.sourceforge.pmd.lang.java.ast.ASTList.ASTNonEmptyList;
/**
* Represents the {@code permits} clause of a (sealed) class declaration.
*
* <p>This is a Java 15 Preview feature.
* <p>This is a Java 15 Preview and Java 16 Preview feature.
*
* <p>See https://openjdk.java.net/jeps/360
* <p>See https://openjdk.java.net/jeps/397
*
* <pre class="grammar">
*
* PermittedSubclasses ::= "permits" (TypeAnnotation)* ClassOrInterfaceType
* ( "," (TypeAnnotation)* ClassOrInterfaceType )*
* PermittedSubclasses ::= "permits" ClassOrInterfaceType
* ( "," ClassOrInterfaceType )*
* </pre>
*/
@Experimental

View File

@ -5,22 +5,18 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
/**
* Defines the body of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 and JDK 15 preview feature).
* Defines the body of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 16 feature).
* This can contain additional methods and or constructors.
*
* <pre class="grammar">
*
* RecordBody ::= "{" ( {@linkplain ASTRecordConstructorDeclaration RecordConstructorDeclaration}
* | {@linkplain ASTClassOrInterfaceBodyDeclaration ClassOrInterfaceBodyDeclaration} )*
* "}"
* RecordBody ::= "{" ( {@linkplain ASTCompactConstructorDeclaration CompactConstructorDeclaration}
* | {@linkplain ASTClassOrInterfaceBodyDeclaration ClassOrInterfaceBodyDeclaration} )* "}"
*
* </pre>
*
*/
@Experimental
public final class ASTRecordBody extends ASTTypeBody {
ASTRecordBody(int id) {
super(id);

View File

@ -5,13 +5,12 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.VariableIdOwner;
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
/**
* Defines a single component of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 and JDK 15 preview feature).
* Defines a single component of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 16 feature).
*
* <p>The varargs ellipsis {@code "..."} is parsed as an {@linkplain ASTArrayTypeDim array dimension}
* in the type node.
@ -35,7 +34,6 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
*
* </pre>
*/
@Experimental
public final class ASTRecordComponent extends AbstractJavaNode implements AccessNode, VariableIdOwner {
ASTRecordComponent(int id) {

View File

@ -5,13 +5,12 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.java.ast.ASTList.ASTMaybeEmptyListOf;
import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.AllChildrenAreOfType;
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
/**
* Defines the state description of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 and JDK 15 preview feature).
* Defines the state description of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 16 feature).
*
* <pre class="grammar">
*
@ -19,13 +18,11 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
*
* </pre>
*/
@Experimental
public final class ASTRecordComponentList extends ASTMaybeEmptyListOf<ASTRecordComponent>
implements SymbolDeclaratorNode, AllChildrenAreOfType<ASTRecordComponent> {
private JConstructorSymbol symbol;
ASTRecordComponentList(int id) {
super(id, ASTRecordComponent.class);
}

View File

@ -7,12 +7,11 @@ package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.NodeStream;
/**
* A record declaration is a special data class type (JDK 14 and JDK 15 preview feature).
* A record declaration is a special data class type (JDK 16 feature).
* This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods.
*
* <pre class="grammar">
@ -27,9 +26,8 @@ import net.sourceforge.pmd.lang.ast.NodeStream;
*
* </pre>
*
* @see <a href="https://openjdk.java.net/jeps/384">JEP 384: Records (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/395">JEP 395: Records</a>
*/
@Experimental
public final class ASTRecordDeclaration extends AbstractAnyTypeDeclaration {
ASTRecordDeclaration(int id) {
super(id);

View File

@ -0,0 +1,54 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.List;
/**
* A type pattern (JDK16). This can be found on
* the right-hand side of an {@link ASTInfixExpression InstanceOfExpression},
* in a {@link ASTPatternExpression PatternExpression}.
*
* <pre class="grammar">
*
* TypePattern ::= ( "final" | {@linkplain ASTAnnotation Annotation} )* {@linkplain ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
*
* </pre>
*
* @see <a href="https://openjdk.java.net/jeps/394">JEP 394: Pattern Matching for instanceof</a>
*/
public final class ASTTypePattern extends AbstractJavaNode implements ASTPattern {
private boolean isFinal;
ASTTypePattern(int id) {
super(id);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
/**
* Gets the type against which the expression is tested.
*/
public ASTType getTypeNode() {
return getFirstChildOfType(ASTType.class);
}
/** Returns the declared variable. */
public ASTVariableDeclaratorId getVarId() {
return getFirstChildOfType(ASTVariableDeclaratorId.class);
}
void setFinal(boolean isFinal) {
this.isFinal = isFinal;
}
boolean isFinal() {
return isFinal;
}
}

View File

@ -1,45 +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.Experimental;
/**
* A type test pattern (JDK 14 preview feature). This can be found on
* the right-hand side of an {@link ASTInfixExpression InstanceOfExpression},
* in a {@link ASTPatternExpression PatternExpression}.
*
* <pre class="grammar">
*
* TypeTestPattern ::= {@linkplain ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
*
* </pre>
*/
@Experimental
public final class ASTTypeTestPattern extends AbstractJavaNode implements ASTPattern {
ASTTypeTestPattern(int id) {
super(id);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
/**
* Gets the type against which the expression is tested.
*/
public ASTType getTypeNode() {
return (ASTType) getChild(0);
}
/** Returns the declared variable. */
public ASTVariableDeclaratorId getVarId() {
return (ASTVariableDeclaratorId) getChild(1);
}
}

View File

@ -9,7 +9,6 @@ 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.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol;
@ -112,7 +111,7 @@ public final class ASTVariableDeclaratorId extends AbstractTypedSymbolDeclarator
JavaNode parent = getParent();
if (parent instanceof ASTVariableDeclarator) {
return (AccessNode) parent.getParent();
} else if (parent instanceof ASTTypeTestPattern) {
} else if (parent instanceof ASTTypePattern) {
return this; // this is pretty weird
}
return (AccessNode) parent;
@ -239,7 +238,6 @@ public final class ASTVariableDeclaratorId extends AbstractTypedSymbolDeclarator
* Returns true if this is a binding variable in a
* {@linkplain ASTPattern pattern}.
*/
@Experimental
public boolean isPatternBinding() {
return getParent() instanceof ASTPattern;
}

View File

@ -339,8 +339,8 @@ final class LazyTypeResolver extends JavaVisitorBase<Void, @NonNull JTypeMirror>
@Override
public JTypeMirror visit(ASTPatternExpression node, Void data) {
ASTPattern pattern = node.getPattern();
if (pattern instanceof ASTTypeTestPattern) {
return ((ASTTypeTestPattern) pattern).getTypeNode().getTypeMirror();
if (pattern instanceof ASTTypePattern) {
return ((ASTTypePattern) pattern).getTypeNode().getTypeMirror();
}
throw new IllegalArgumentException("Unknown pattern " + pattern);
}

View File

@ -41,7 +41,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
import net.sourceforge.pmd.lang.java.ast.ASTType;
import net.sourceforge.pmd.lang.java.ast.ASTTypeArguments;
import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters;
import net.sourceforge.pmd.lang.java.ast.ASTTypeTestPattern;
import net.sourceforge.pmd.lang.java.ast.ASTTypePattern;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.ASTYieldStatement;
import net.sourceforge.pmd.lang.java.ast.JModifier;
@ -110,21 +110,79 @@ public class LanguageLevelChecker<T> {
/** Those are just for the preview features. */
private enum PreviewFeature implements LanguageFeature {
/**
* Note: Withdrawn with JEP 354, yield statement is used instead.
* @see #YIELD_STATEMENTS
* @see <a href="https://openjdk.java.net/jeps/325">JEP 325: Switch Expressions (Preview)</a>
*/
BREAK__WITH__VALUE_STATEMENTS(12, 12, false),
/**
* @see <a href="https://openjdk.java.net/jeps/325">JEP 325: Switch Expressions (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/354">JEP 354: Switch Expressions (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/361">JEP 361: Switch Expressions</a>
*/
COMPOSITE_CASE_LABEL(12, 13, true),
/**
* @see <a href="https://openjdk.java.net/jeps/325">JEP 325: Switch Expressions (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/354">JEP 354: Switch Expressions (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/361">JEP 361: Switch Expressions</a>
*/
SWITCH_EXPRESSIONS(12, 13, true),
/**
* @see <a href="https://openjdk.java.net/jeps/325">JEP 325: Switch Expressions (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/354">JEP 354: Switch Expressions (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/361">JEP 361: Switch Expressions</a>
*/
SWITCH_RULES(12, 13, true),
TEXT_BLOCK_LITERALS(13, 14, true),
/**
* @see #SWITCH_EXPRESSIONS
* @see <a href="https://openjdk.java.net/jeps/354">JEP 354: Switch Expressions (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/361">JEP 361: Switch Expressions</a>
*/
YIELD_STATEMENTS(13, 13, true),
/** \s */
/**
* @see <a href="https://openjdk.java.net/jeps/355">JEP 355: Text Blocks (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/368">JEP 368: Text Blocks (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/378">JEP 378: Text Blocks</a>
*/
TEXT_BLOCK_LITERALS(13, 14, true),
/**
* The new escape sequence {@code \s} simply translates to a single space {@code \u0020}.
*
* @see #TEXT_BLOCK_LITERALS
* @see <a href="https://openjdk.java.net/jeps/368">JEP 368: Text Blocks (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/378">JEP 378: Text Blocks</a>
*/
SPACE_STRING_ESCAPES(14, 14, true),
RECORD_DECLARATIONS(14, 15, false),
TYPE_TEST_PATTERNS_IN_INSTANCEOF(14, 15, false),
SEALED_CLASSES(15, 15, false),
STATIC_LOCAL_TYPE_DECLARATIONS(15, 15, false), // part of the sealed classes JEP
/**
* @see <a href="https://openjdk.java.net/jeps/359">JEP 359: Records (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/384">JEP 384: Records (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/395">JEP 395: Records</a>
*/
RECORD_DECLARATIONS(14, 15, true),
/**
* @see <a href="https://openjdk.java.net/jeps/305">JEP 305: Pattern Matching for instanceof (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/375">JEP 375: Pattern Matching for instanceof (Second Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/394">JEP 394: Pattern Matching for instanceof</a>
*/
TYPE_PATTERNS_IN_INSTANCEOF(14, 15, true),
/**
* Part of the records JEP 394.
* @see #RECORD_DECLARATIONS
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8253374">JLS changes for Static Members of Inner Classes</a>
*/
STATIC_LOCAL_TYPE_DECLARATIONS(15, 15, true),
/**
* @see <a href="https://openjdk.java.net/jeps/360">JEP 360: Sealed Classes (Preview)</a>
* @see <a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a>
*/
SEALED_CLASSES(15, 16, false),
; // SUPPRESS CHECKSTYLE enum trailing semi is awesome
@ -164,18 +222,37 @@ public class LanguageLevelChecker<T> {
}
}
/** Those use a max valid version. */
private enum ReservedIdentifiers implements LanguageFeature {
/**
* Those use a max valid version.
*
* @see <a href="http://cr.openjdk.java.net/~gbierman/jep397/jep397-20201204/specs/contextual-keywords-jls.html">Contextual Keywords</a>
*/
private enum Keywords implements LanguageFeature {
/**
* ReservedKeyword since Java 1.4.
*/
ASSERT_AS_AN_IDENTIFIER(4, "assert"),
/**
* ReservedKeyword since Java 1.5.
*/
ENUM_AS_AN_IDENTIFIER(5, "enum"),
/**
* ReservedKeyword since Java 9.
*/
UNDERSCORE_AS_AN_IDENTIFIER(9, "_"),
/**
* ContextualKeyword since Java 10.
*/
VAR_AS_A_TYPE_NAME(10, "var"),
// yield -> PreviewFeature.YIELD_STATEMENTS
RECORD_AS_A_TYPE_NAME(14, "record");
// sealed -> PreviewFeature.SEALED_CLASSES
// permits -> PreviewFeature.SEALED_CLASSES
private final int maxJdkVersion;
private final String reserved;
ReservedIdentifiers(int minJdkVersion, String reserved) {
Keywords(int minJdkVersion, String reserved) {
this.maxJdkVersion = minJdkVersion;
this.reserved = reserved;
}
@ -411,8 +488,8 @@ public class LanguageLevelChecker<T> {
}
@Override
public Void visit(ASTTypeTestPattern node, T data) {
check(node, PreviewFeature.TYPE_TEST_PATTERNS_IN_INSTANCEOF, data);
public Void visit(ASTTypePattern node, T data) {
check(node, PreviewFeature.TYPE_PATTERNS_IN_INSTANCEOF, data);
return null;
}
@ -484,9 +561,9 @@ public class LanguageLevelChecker<T> {
}
String simpleName = node.getSimpleName();
if ("var".equals(simpleName)) {
check(node, ReservedIdentifiers.VAR_AS_A_TYPE_NAME, data);
check(node, Keywords.VAR_AS_A_TYPE_NAME, data);
} else if ("record".equals(simpleName)) {
check(node, ReservedIdentifiers.RECORD_AS_A_TYPE_NAME, data);
check(node, Keywords.RECORD_AS_A_TYPE_NAME, data);
}
checkIdent(node, simpleName, data);
return null;
@ -494,11 +571,11 @@ public class LanguageLevelChecker<T> {
private void checkIdent(JavaNode node, String simpleName, T acc) {
if ("enum".equals(simpleName)) {
check(node, ReservedIdentifiers.ENUM_AS_AN_IDENTIFIER, acc);
check(node, Keywords.ENUM_AS_AN_IDENTIFIER, acc);
} else if ("assert".equals(simpleName)) {
check(node, ReservedIdentifiers.ASSERT_AS_AN_IDENTIFIER, acc);
check(node, Keywords.ASSERT_AS_AN_IDENTIFIER, acc);
} else if ("_".equals(simpleName)) {
check(node, ReservedIdentifiers.UNDERSCORE_AS_AN_IDENTIFIER, acc);
check(node, Keywords.UNDERSCORE_AS_AN_IDENTIFIER, acc);
}
}

View File

@ -17,6 +17,7 @@ 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.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTCompactConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
@ -26,7 +27,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodReference;
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
import net.sourceforge.pmd.lang.java.ast.ASTRecordConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.AccessNode;
@ -70,7 +70,7 @@ public final class JavaDesignerBindings extends DefaultDesignerBindings {
} else if (node instanceof ASTMethodDeclaration) {
return TreeIconId.METHOD;
} else if (node instanceof ASTConstructorDeclaration
|| node instanceof ASTRecordConstructorDeclaration) {
|| node instanceof ASTCompactConstructorDeclaration) {
return TreeIconId.CONSTRUCTOR;
} else if (node instanceof ASTVariableDeclaratorId) {
return TreeIconId.VARIABLE;

View File

@ -61,5 +61,4 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse
return true;
}
}

View File

@ -26,6 +26,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTCatchClause;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTCompactConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
@ -40,7 +41,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTLocalClassStatement;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTModifierList;
import net.sourceforge.pmd.lang.java.ast.ASTRecordConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTResource;
import net.sourceforge.pmd.lang.java.ast.ASTResourceList;
import net.sourceforge.pmd.lang.java.ast.ASTStatement;
@ -299,7 +299,7 @@ public final class SymbolTableResolver {
@Override
public Void visit(ASTRecordConstructorDeclaration node, @NonNull ReferenceCtx ctx) {
public Void visit(ASTCompactConstructorDeclaration node, @NonNull ReferenceCtx ctx) {
setTopSymbolTable(node.getModifiers());
int pushed = pushOnStack(f.recordCtor(top(), enclosing(), node.getSymbol()));
setTopSymbolTableAndRecurse(node, ctx);

View File

@ -24,6 +24,7 @@ import net.sourceforge.pmd.annotation.InternalApi;
@Deprecated
@InternalApi
public class PMDASMVisitor extends ClassVisitor {
private static final int ASM_API = Opcodes.ASM9; // latest, non-experimental API version
private String outerName;
@ -40,7 +41,7 @@ public class PMDASMVisitor extends ClassVisitor {
public List<String> innerClasses;
public PMDASMVisitor(String outerName) {
super(Opcodes.ASM9);
super(ASM_API);
this.outerName = outerName;
}
@ -181,7 +182,7 @@ public class PMDASMVisitor extends ClassVisitor {
private PMDASMVisitor parent;
PMDFieldVisitor(PMDASMVisitor visitor) {
super(Opcodes.ASM9);
super(ASM_API);
parent = visitor;
}
@ -196,7 +197,7 @@ public class PMDASMVisitor extends ClassVisitor {
private PMDASMVisitor parent;
PMDAnnotationVisitor(PMDASMVisitor visitor) {
super(Opcodes.ASM9);
super(ASM_API);
parent = visitor;
}
@ -228,7 +229,7 @@ public class PMDASMVisitor extends ClassVisitor {
private PMDASMVisitor parent;
PMDSignatureVisitor(PMDASMVisitor visitor) {
super(Opcodes.ASM9);
super(ASM_API);
this.parent = visitor;
}
@ -292,7 +293,7 @@ public class PMDASMVisitor extends ClassVisitor {
private PMDASMVisitor parent;
PMDMethodVisitor(PMDASMVisitor visitor) {
super(Opcodes.ASM9);
super(ASM_API);
parent = visitor;
}

View File

@ -19,15 +19,27 @@ public class LanguageVersionDiscovererTest {
/**
* Test on Java file with default options.
* Always the latest non-preview version will be the default version.
*/
@Test
public void testJavaFileUsingDefaults() {
LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer();
File javaFile = new File("/path/to/MyClass.java");
LanguageVersion latest = determineLatestNonPreviewVersion();
LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(javaFile);
assertEquals("LanguageVersion must be Java 15 !",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"), languageVersion);
assertEquals("Latest language version must be default", latest, languageVersion);
}
private LanguageVersion determineLatestNonPreviewVersion() {
LanguageVersion latest = null;
for (LanguageVersion lv : LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersions()) {
if (!lv.getName().endsWith("preview")) {
latest = lv;
}
}
return latest;
}
/**
@ -48,7 +60,7 @@ public class LanguageVersionDiscovererTest {
public void testLanguageVersionDiscoverer() {
PMDConfiguration configuration = new PMDConfiguration();
LanguageVersionDiscoverer languageVersionDiscoverer = configuration.getLanguageVersionDiscoverer();
assertEquals("Default Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"),
assertEquals("Default Java version", determineLatestNonPreviewVersion(),
languageVersionDiscoverer
.getDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)));
configuration

View File

@ -53,9 +53,13 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest {
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "14-preview",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14-preview"), },
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "15",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"), },
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "15-preview",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15-preview"), },
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"), },
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "15-preview",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15-preview"), },
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "16",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("16"), },
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "16-preview",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("16-preview"), },
// this one won't be found: case sensitive!
{ "JAVA", "JAVA", "1.7", null, },

View File

@ -1,164 +0,0 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
/**
* Tests new java14 preview features.
*/
public class Java14PreviewTest {
private final JavaParsingHelper java14 =
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("14")
.withResourceContext(getClass(), "jdkversiontests/java14/");
private final JavaParsingHelper java14p = java14.withDefaultVersion("14-preview");
private final JavaParsingHelper java13 = java14.withDefaultVersion("13");
@Test
public void textBlocks() {
ASTCompilationUnit compilationUnit = java14p.parseResource("TextBlocks.java");
List<ASTStringLiteral> literals = compilationUnit.findDescendantsOfType(ASTStringLiteral.class);
Assert.assertEquals(22, literals.size());
Assert.assertFalse(literals.get(2).isTextBlock());
Assert.assertFalse(literals.get(12).isTextBlock());
Assert.assertFalse(literals.get(17).isTextBlock());
Assert.assertFalse(literals.get(18).isTextBlock());
Assert.assertFalse(literals.get(20).isTextBlock());
Assert.assertFalse(literals.get(21).isTextBlock());
List<ASTStringLiteral> textBlocks = new ArrayList<>();
for (ASTStringLiteral literal : literals) {
if (literal.isTextBlock()) {
textBlocks.add(literal);
}
}
Assert.assertEquals(16, textBlocks.size());
Assert.assertEquals("\"\"\"\n"
+ " <html> \n"
+ " <body>\n"
+ " <p>Hello, world</p> \n"
+ " </body> \n"
+ " </html> \n"
+ " \"\"\"",
textBlocks.get(0).getImage());
Assert.assertEquals("<html>\n"
+ " <body>\n"
+ " <p>Hello, world</p>\n"
+ " </body>\n"
+ "</html>\n", textBlocks.get(0).getConstValue());
// Note: More tests are in ASTLiteralTest.
}
@Test(expected = ParseException.class)
public void textBlocksBeforeJava14PreviewShouldFail() {
java13.parseResource("TextBlocks.java");
}
@Test(expected = ParseException.class)
public void stringEscapeSequenceShouldFail() {
java14.parse("class Foo { String s =\"a\\sb\"; }");
}
@Test
public void recordPoint() {
ASTCompilationUnit compilationUnit = java14p.parseResource("Point.java");
ASTRecordDeclaration recordDecl = compilationUnit.getFirstDescendantOfType(ASTRecordDeclaration.class);
Assert.assertEquals("Point", recordDecl.getImage());
Assert.assertFalse(recordDecl.isNested());
List<ASTRecordComponent> components = recordDecl.getFirstChildOfType(ASTRecordComponentList.class)
.findChildrenOfType(ASTRecordComponent.class);
Assert.assertEquals(2, components.size());
Assert.assertEquals("x", components.get(0).getVarId().getImage());
Assert.assertEquals("y", components.get(1).getVarId().getImage());
// TODO: type resolution for record components
// Assert.assertNull(components.get(0).getVarId().getNameDeclaration().getAccessNodeParent());
// Assert.assertEquals(Integer.TYPE, components.get(0).getVarId().getNameDeclaration().getType());
// Assert.assertEquals("int", components.get(0).getVarId().getNameDeclaration().getTypeImage());
}
@Test(expected = ParseException.class)
public void recordPointBeforeJava14PreviewShouldFail() {
java14.parseResource("Point.java");
}
@Test(expected = ParseException.class)
public void recordCtorWithThrowsShouldFail() {
java14p.parse(" record R {"
+ " R throws IOException {}"
+ " }");
}
@Test
public void innerRecords() {
ASTCompilationUnit compilationUnit = java14p.parseResource("Records.java");
List<ASTRecordDeclaration> recordDecls = compilationUnit.findDescendantsOfType(ASTRecordDeclaration.class, true);
Assert.assertEquals(7, recordDecls.size());
ASTRecordDeclaration complex = recordDecls.get(0);
Assert.assertEquals("MyComplex", complex.getSimpleName());
Assert.assertTrue(complex.isNested());
Assert.assertEquals(0, getComponent(complex, 0).getDeclaredAnnotations().count());
Assert.assertEquals(1, getComponent(complex, 1).getDeclaredAnnotations().count());
Assert.assertEquals(2, complex.getDeclarations().count());
Assert.assertTrue(complex.getDeclarations().get(0) instanceof ASTConstructorDeclaration);
Assert.assertTrue(complex.getDeclarations().get(1) instanceof ASTRecordDeclaration);
ASTRecordDeclaration nested = recordDecls.get(1);
Assert.assertEquals("Nested", nested.getSimpleName());
Assert.assertTrue(nested.isNested());
ASTRecordDeclaration range = recordDecls.get(2);
Assert.assertEquals("Range", range.getSimpleName());
Assert.assertEquals(2, range.getRecordComponents().size());
List<ASTRecordConstructorDeclaration> rangeConstructors = range.findDescendantsOfType(ASTRecordConstructorDeclaration.class);
Assert.assertEquals(1, rangeConstructors.size());
Assert.assertEquals("Range", rangeConstructors.get(0).getImage());
JavaNode mods = rangeConstructors.get(0).getChild(0);
Assert.assertTrue(mods instanceof ASTModifierList);
Assert.assertEquals(1, mods.getNumChildren());
Assert.assertEquals(2, range.getDeclarations().count());
ASTRecordDeclaration varRec = recordDecls.get(3);
Assert.assertEquals("VarRec", varRec.getSimpleName());
Assert.assertEquals("x", getComponent(varRec, 0).getVarId().getImage());
Assert.assertTrue(getComponent(varRec, 0).isVarargs());
Assert.assertEquals(2, getComponent(varRec, 0).getDeclaredAnnotations().count());
Assert.assertEquals(1, getComponent(varRec, 0).getTypeNode().descendants(ASTAnnotation.class).count());
ASTRecordDeclaration arrayRec = recordDecls.get(4);
Assert.assertEquals("ArrayRec", arrayRec.getSimpleName());
Assert.assertEquals("x", getComponent(arrayRec, 0).getVarId().getImage());
Assert.assertTrue(getComponent(arrayRec, 0).getVarId().hasArrayType());
ASTRecordDeclaration emptyRec = recordDecls.get(5);
Assert.assertEquals("EmptyRec", emptyRec.getSimpleName());
Assert.assertEquals(0, emptyRec.getRecordComponents().size());
ASTRecordDeclaration personRec = recordDecls.get(6);
Assert.assertEquals("PersonRecord", personRec.getSimpleName());
ASTImplementsList impl = personRec.getFirstChildOfType(ASTImplementsList.class);
Assert.assertEquals(2, impl.findChildrenOfType(ASTClassOrInterfaceType.class).size());
}
private ASTRecordComponent getComponent(ASTRecordDeclaration arrayRec, int index) {
return arrayRec.getRecordComponents().getChild(index);
}
@Test(expected = ParseException.class)
public void recordIsARestrictedIdentifier() {
java14p.parse("public class record {}");
}
}

View File

@ -27,7 +27,6 @@ public class Java14Test {
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("14")
.withResourceContext(Java14Test.class, "jdkversiontests/java14/");
private final JavaParsingHelper java14p = java14.withDefaultVersion("14-preview");
private final JavaParsingHelper java13 = java14.withDefaultVersion("13");
/**
@ -36,7 +35,6 @@ public class Java14Test {
@Test
public void switchExpressions() {
parseAndCheckSwitchExpression(java14);
parseAndCheckSwitchExpression(java14p);
}
/**
@ -135,7 +133,6 @@ public class Java14Test {
@Test
public void multipleCaseLabels() {
multipleCaseLabels(java14);
multipleCaseLabels(java14p);
}
private void multipleCaseLabels(JavaParsingHelper parser) {
@ -150,7 +147,6 @@ public class Java14Test {
@Test
public void switchRules() {
switchRules(java14);
switchRules(java14p);
}
private void switchRules(JavaParsingHelper parser) {
@ -177,7 +173,6 @@ public class Java14Test {
@Test
public void simpleSwitchExpressions() {
simpleSwitchExpressions(java14);
simpleSwitchExpressions(java14p);
}
private void simpleSwitchExpressions(JavaParsingHelper parser) {

View File

@ -0,0 +1,54 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest;
import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java16PreviewTreeDumpTest extends BaseTreeDumpTest {
private final JavaParsingHelper java16p =
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16-preview")
.withResourceContext(Java16PreviewTreeDumpTest.class, "jdkversiontests/java16p/");
private final JavaParsingHelper java16 = java16p.withDefaultVersion("16");
public Java16PreviewTreeDumpTest() {
super(new RelevantAttributePrinter(), ".java");
}
@Override
public BaseParsingHelper<?, ?> getParser() {
return java16p;
}
@Test(expected = ParseException.class)
public void sealedClassBeforeJava16Preview() {
java16.parseResource("geometry/Shape.java");
}
@Test
public void sealedClass() {
doTest("geometry/Shape");
}
@Test
public void nonSealedClass() {
doTest("geometry/Square");
}
@Test(expected = ParseException.class)
public void sealedInterfaceBeforeJava15Preview() {
java16.parseResource("expression/Expr.java");
}
@Test
public void sealedInterface() {
doTest("expression/Expr");
}
}

View File

@ -0,0 +1,121 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest;
import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java16TreeDumpTest extends BaseTreeDumpTest {
private final JavaParsingHelper java16 =
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16")
.withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java16/");
private final JavaParsingHelper java16p = java16.withDefaultVersion("16-preview");
private final JavaParsingHelper java15 = java16.withDefaultVersion("15");
public Java16TreeDumpTest() {
super(new RelevantAttributePrinter(), ".java");
}
@Override
public BaseParsingHelper<?, ?> getParser() {
return java16;
}
@Test
public void patternMatchingInstanceof() {
doTest("PatternMatchingInstanceof");
// extended tests for type resolution etc.
ASTCompilationUnit compilationUnit = java16.parseResource("PatternMatchingInstanceof.java");
List<ASTInstanceOfExpression> instanceOfExpressions = compilationUnit.findDescendantsOfType(ASTInstanceOfExpression.class);
for (ASTInstanceOfExpression expr : instanceOfExpressions) {
ASTVariableDeclaratorId variable = expr.getChild(1).getFirstChildOfType(ASTVariableDeclaratorId.class);
Assert.assertEquals(String.class, variable.getType());
// Note: these variables are not part of the symbol table
// See ScopeAndDeclarationFinder#visit(ASTVariableDeclaratorId, Object)
Assert.assertNull(variable.getNameDeclaration());
}
}
@Test(expected = ParseException.class)
public void patternMatchingInstanceofBeforeJava16ShouldFail() {
java15.parseResource("PatternMatchingInstanceof.java");
}
@Test
public void localClassAndInterfaceDeclarations() {
doTest("LocalClassAndInterfaceDeclarations");
}
@Test(expected = ParseException.class)
public void localClassAndInterfaceDeclarationsBeforeJava16ShouldFail() {
java15.parseResource("LocalClassAndInterfaceDeclarations.java");
}
@Test(expected = ParseException.class)
public void localAnnotationsAreNotAllowed() {
java16.parse("public class Foo { { @interface MyLocalAnnotation {} } }");
}
@Test
public void localRecords() {
doTest("LocalRecords");
}
@Test
public void recordPoint() {
doTest("Point");
// extended tests for type resolution etc.
ASTCompilationUnit compilationUnit = java16.parseResource("Point.java");
ASTRecordDeclaration recordDecl = compilationUnit.getFirstDescendantOfType(ASTRecordDeclaration.class);
List<ASTRecordComponent> components = recordDecl.getFirstChildOfType(ASTRecordComponentList.class)
.findChildrenOfType(ASTRecordComponent.class);
Assert.assertNull(components.get(0).getVarId().getNameDeclaration().getAccessNodeParent());
Assert.assertEquals(Integer.TYPE, components.get(0).getVarId().getNameDeclaration().getType());
Assert.assertEquals("int", components.get(0).getVarId().getNameDeclaration().getTypeImage());
}
@Test(expected = ParseException.class)
public void recordPointBeforeJava16ShouldFail() {
java15.parseResource("Point.java");
}
@Test(expected = ParseException.class)
public void recordCtorWithThrowsShouldFail() {
java16.parse(" record R {"
+ " R throws IOException {}"
+ " }");
}
@Test(expected = ParseException.class)
public void recordMustNotExtend() {
java16.parse("record RecordEx(int x) extends Number { }");
}
@Test
public void innerRecords() {
doTest("Records");
}
@Test(expected = ParseException.class)
public void recordIsARestrictedIdentifier() {
java16.parse("public class record {}");
}
@Test
public void sealedAndNonSealedIdentifiers() {
doTest("NonSealedIdentifier");
java16p.parseResource("NonSealedIdentifier.java"); // make sure we can parse it with preview as well
}
}

View File

@ -46,7 +46,7 @@ class ASTLiteralTest : ParserTestSpec({
}
}
parserTest("Text block literal", javaVersions = since(J14__PREVIEW)) {
parserTest("Text block literal", javaVersions = since(J15__PREVIEW)) {
val delim = "\"\"\""

View File

@ -6,25 +6,24 @@ package net.sourceforge.pmd.lang.java.ast
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBe
import net.sourceforge.pmd.lang.java.ast.JavaVersion
import net.sourceforge.pmd.lang.java.ast.JavaVersion.J14__PREVIEW
import net.sourceforge.pmd.lang.java.ast.JavaVersion.J15__PREVIEW
import net.sourceforge.pmd.lang.ast.test.shouldBe as typeShouldBe
import net.sourceforge.pmd.lang.java.ast.JavaVersion.*
import java.io.IOException
class ASTPatternTest : ParserTestSpec({
parserTest("Test patterns only available on JDK 14+15 (preview)", javaVersions = JavaVersion.except(J14__PREVIEW, J15__PREVIEW)) {
val typePatternsVersions = JavaVersion.since(J16).plus(J15__PREVIEW)
parserTest("Test patterns only available on JDK 15 (preview) and JDK16 and JDK16 (preview)", javaVersions = typePatternsVersions) {
inContext(ExpressionParsingCtx) {
"obj instanceof Class c" should throwParseException {
it.message.shouldContain("Type test patterns in instanceof is a preview feature of JDK")
it.message.shouldContain("Type patterns in instanceof was only standardized in Java 16")
}
}
}
parserTest("Test simple patterns", javaVersions = listOf(J14__PREVIEW, J15__PREVIEW)) {
parserTest("Test simple patterns", javaVersions = typePatternsVersions) {
importedTypes += IOException::class.java
inContext(ExpressionParsingCtx) {
@ -33,11 +32,50 @@ class ASTPatternTest : ParserTestSpec({
infixExpr(BinaryOp.INSTANCEOF) {
variableAccess("obj")
child<ASTPatternExpression> {
it::getPattern shouldBe child<ASTTypeTestPattern> {
//it.isAnnotationPresent("java.lang.Deprecated") shouldBe false
it::getPattern shouldBe child<ASTTypePattern> {
it::getTypeNode shouldBe classType("Class")
it::getVarId shouldBe variableId("c") {
it::getModifiers shouldBe modifiers { } // dummy modifier list
it.hasExplicitModifiers(JModifier.FINAL) shouldBe false
it.hasModifiers(JModifier.FINAL) shouldBe false
}
}
}
}
}
"obj instanceof final Class c" should parseAs {
infixExpr(BinaryOp.INSTANCEOF) {
variableAccess("obj")
child<ASTPatternExpression> {
//it.isAnnotationPresent("java.lang.Deprecated") shouldBe false
it::getPattern shouldBe child<ASTTypePattern> {
it::getTypeNode shouldBe classType("Class")
it::getVarId shouldBe variableId("c") {
it::getModifiers shouldBe modifiers { } // dummy modifier list
it.hasExplicitModifiers(JModifier.FINAL) shouldBe true
it.hasModifiers(JModifier.FINAL) shouldBe true
}
}
}
}
}
"obj instanceof @Deprecated Class c" should parseAs {
infixExpr(BinaryOp.INSTANCEOF) {
variableAccess("obj")
child<ASTPatternExpression> {
child<ASTAnnotation>(ignoreChildren = true) {
it.annotationName shouldBe "Deprecated"
}
//it.isAnnotationPresent("java.lang.Deprecated") shouldBe true
it::getPattern shouldBe child<ASTTypePattern> {
it::getTypeNode shouldBe classType("Class")
it::getVarId shouldBe variableId("c") {
it::getModifiers shouldBe modifiers { } // dummy modifier list
it.hasExplicitModifiers(JModifier.FINAL) shouldBe true
it.hasModifiers(JModifier.FINAL) shouldBe true
}
}
@ -46,6 +84,4 @@ class ASTPatternTest : ParserTestSpec({
}
}
}
})

View File

@ -33,8 +33,9 @@ enum class JavaVersion : Comparable<JavaVersion> {
J1_3, J1_4, J1_5, J1_6, J1_7, J1_8, J9, J10, J11,
J12,
J13,
J14, J14__PREVIEW,
J15, J15__PREVIEW;
J14,
J15, J15__PREVIEW,
J16, J16__PREVIEW;
/** Name suitable for use with e.g. [JavaParsingHelper.parse] */
val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').toLowerCase()

View File

@ -1,113 +0,0 @@
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
/**
* @see <a href="https://openjdk.java.net/jeps/368">JEP 368: Text Blocks (Second Preview)</a>
*/
public class TextBlocks {
public static void main(String[] args) throws Exception {
// note: there is trailing whitespace!!
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
System.out.println(html);
String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;
""";
System.out.println(query);
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("""
function hello() {
print('"Hello, world"');
}
hello();
""");
// Escape sequences
String htmlWithEscapes = """
<html>\r
<body>\r
<p>Hello, world</p>\r
</body>\r
</html>\r
""";
System.out.println(htmlWithEscapes);
String season = """
winter"""; // the six characters w i n t e r
String period = """
winter
"""; // the seven characters w i n t e r LF
String greeting =
"""
Hi, "Bob"
"""; // the ten characters H i , SP " B o b " LF
String salutation =
"""
Hi,
"Bob"
"""; // the eleven characters H i , LF SP " B o b " LF
String empty = """
"""; // the empty string (zero length)
String quote = """
"
"""; // the two characters " LF
String backslash = """
\\
"""; // the two characters \ LF
String normalStringLiteral = "test";
String code =
"""
String text = \"""
A text block inside a text block
\""";
""";
// new escape sequences
String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et dolore magna aliqua.\
""";
System.out.println(text);
String colors = """
red \s
green\s
blue \s
""";
System.out.println(colors);
// empty new line as first content
String emptyLine = """
test
""";
System.out.println(emptyLine.replaceAll("\n", "<LF>"));
// backslash escapes
String bs = """
\\test
""";
System.out.println(bs.replaceAll("\n", "<LF>"));
}
}

View File

@ -14,6 +14,7 @@ public class LocalInterfacesAndEnums {
enum MyLocalEnum { A }
@interface MyLocalAnnotation {}
// not supported anymore with Java16
//@interface MyLocalAnnotation {}
}
}

Some files were not shown because too many files have changed in this diff Show More