Merge branch '7.0.x' into java-grammar
This commit is contained in:
@ -185,7 +185,7 @@ Example:
|
||||
* [apex](pmd_rules_apex.html) (Salesforce Apex)
|
||||
* [java](pmd_rules_java.html)
|
||||
* Supported Versions: 1.3, 1.4, 1.5, 5, 1.6, 6, 1.7, 7, 1.8, 8, 9, 1.9, 10, 1.10, 11, 12,
|
||||
13, 13-preview, 14 (default), 14-preview
|
||||
13, 14, 14-preview, 15 (default), 15-preview
|
||||
* [ecmascript](pmd_rules_ecmascript.html) (JavaScript)
|
||||
* [jsp](pmd_rules_jsp.html)
|
||||
* [modelica](pmd_rules_modelica.html)
|
||||
|
@ -19,6 +19,24 @@ This is a {{ site.pmd.release_type }} release.
|
||||
|
||||
### New and noteworthy
|
||||
|
||||
#### Java 15 Support
|
||||
|
||||
This release of PMD brings support for Java 15. PMD can parse [Text Blocks](https://openjdk.java.net/jeps/378)
|
||||
which have been promoted to be a standard language feature of Java.
|
||||
|
||||
PMD also supports [Pattern Matching for instanceof](https://openjdk.java.net/jeps/375),
|
||||
[Records](https://openjdk.java.net/jeps/384), and [Sealed Classes](https://openjdk.java.net/jeps/360).
|
||||
|
||||
Note: The Pattern Matching for instanceof, Records, and Sealed Classes are all preview language features of OpenJDK 15
|
||||
and are not enabled by default. 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 `15-preview`:
|
||||
|
||||
export PMD_JAVA_OPTS=--enable-preview
|
||||
./run.sh pmd -language java -version 15-preview ...
|
||||
|
||||
Note: Support for Java 13 preview language features have been removed. The version "13-preview" is no longer available.
|
||||
|
||||
#### Changes in how tab characters are handled
|
||||
|
||||
In the past, tab characters in source files has been handled differently in different languages by PMD.
|
||||
@ -65,6 +83,8 @@ See also [[all] Ensure PMD/CPD uses tab width of 1 for tabs consistently #2656](
|
||||
* [#2653](https://github.com/pmd/pmd/issues/2653): \[lang-test] Upgrade kotlintest to Kotest
|
||||
* [#2656](https://github.com/pmd/pmd/pull/2656): \[all] Ensure PMD/CPD uses tab width of 1 for tabs consistently
|
||||
* [#2690](https://github.com/pmd/pmd/pull/2690): \[core] Fix java7 compatibility
|
||||
* java
|
||||
* [#2646](https://github.com/pmd/pmd/issues/2646): \[java] Support JDK 15
|
||||
* java-bestpractices
|
||||
* [#2471](https://github.com/pmd/pmd/issues/2471): \[java] New Rule: AvoidReassigningCatchVariables
|
||||
* [#2663](https://github.com/pmd/pmd/issues/2663): \[java] NoClassDefFoundError on upgrade from 6.25.0 to 6.26.0
|
||||
|
@ -1,4 +1,12 @@
|
||||
/**
|
||||
* 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.
|
||||
* Support Records with Java 15 Preview.
|
||||
* Support Local Records with Java 15 Preview.
|
||||
* Support Sealed Classes with Java 15 Preview.
|
||||
* Andreas Dangel 08/2020
|
||||
*====================================================================
|
||||
* Add support for record types introduced as a preview language
|
||||
* feature with Java 14. See JEP 359.
|
||||
* Andreas Dangel 02/2020
|
||||
@ -227,6 +235,7 @@ PARSER_BEGIN(JavaParserImpl)
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -234,6 +243,7 @@ import java.util.Map;
|
||||
import net.sourceforge.pmd.lang.ast.CharStream;
|
||||
import net.sourceforge.pmd.lang.ast.GenericToken;
|
||||
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
|
||||
@ -260,6 +270,16 @@ class JavaParserImpl {
|
||||
throw new ParseException("Line " + line + ", Column " + col + ": " + message);
|
||||
}
|
||||
|
||||
|
||||
private boolean isRecordTypeSupported() {
|
||||
return (jdkVersion == 14 || jdkVersion == 15) && preview;
|
||||
}
|
||||
|
||||
private boolean isSealedClassSupported() {
|
||||
return jdkVersion == 15 && preview;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Keeps track during tree construction, whether we are currently building a switch label.
|
||||
* A switch label must not contain a LambdaExpression.
|
||||
@ -285,9 +305,65 @@ class JavaParserImpl {
|
||||
/**
|
||||
* Semantic lookahead to check if the next identifier is a
|
||||
* specific restricted keyword.
|
||||
*
|
||||
* <p>Restricted keywords are:
|
||||
* var, yield, record, sealed, permits, "non" + "-" + "sealed"
|
||||
*
|
||||
* <p>enum and assert is used like restricted keywords, as they were not keywords
|
||||
* in the early java versions.
|
||||
*/
|
||||
private boolean isKeyword(String keyword) {
|
||||
return getToken(1).kind == IDENTIFIER && getToken(1).getImage().equals(keyword);
|
||||
private boolean isKeyword(String image) {
|
||||
return isKeyword(1, image);
|
||||
}
|
||||
|
||||
private boolean isKeyword(int index, String image) {
|
||||
Token token = getToken(index);
|
||||
return token.kind == IDENTIFIER && token.image.equals(image);
|
||||
}
|
||||
|
||||
private boolean isToken(int index, int kind) {
|
||||
return getToken(index).kind == kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Semantic lookahead which matches "non-sealed".
|
||||
*
|
||||
* <p>"non-sealed" cannot be a token, for several reasons:
|
||||
* It is only a keyword with java15 preview+, it consists actually
|
||||
* of several separate tokens, which are valid on their own.
|
||||
*/
|
||||
private boolean isNonSealedModifier() {
|
||||
if (isKeyword(1, "non") && isToken(2, MINUS) && isKeyword(3, "sealed")) {
|
||||
JavaccToken nonToken = getToken(1);
|
||||
JavaccToken minusToken = getToken(2);
|
||||
JavaccToken sealedToken = getToken(3);
|
||||
return nonToken.getEndColumn() == minusToken.getBeginColumn()
|
||||
&& minusToken.getEndColumn() == sealedToken.getBeginColumn();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean classModifierLookahead() {
|
||||
Token next = getToken(1);
|
||||
return next.kind == AT
|
||||
|| next.kind == PUBLIC
|
||||
|| next.kind == PROTECTED
|
||||
|| next.kind == PRIVATE
|
||||
|| next.kind == ABSTRACT
|
||||
|| next.kind == STATIC
|
||||
|| next.kind == FINAL
|
||||
|| next.kind == STRICTFP
|
||||
|| isSealedClassSupported() && isKeyword("sealed")
|
||||
|| isSealedClassSupported() && isNonSealedModifier();
|
||||
}
|
||||
|
||||
private boolean localTypeDeclLookahead() {
|
||||
Token next = getToken(1);
|
||||
return next.kind == CLASS
|
||||
|| isRecordTypeSupported() && next.kind == INTERFACE
|
||||
|| isRecordTypeSupported() && next.kind == AT && isToken(2, INTERFACE)
|
||||
|| isRecordTypeSupported() && next.kind == IDENTIFIER && next.getImage().equals("enum")
|
||||
|| isRecordTypeSupported() && next.kind == IDENTIFIER && next.image.equals("record");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -875,6 +951,8 @@ void ModifierList():
|
||||
| "volatile" { modifiers.add(JModifier.VOLATILE); }
|
||||
| "strictfp" { modifiers.add(JModifier.STRICTFP); }
|
||||
| "default" { modifiers.add(JModifier.DEFAULT); }
|
||||
| LOOKAHEAD({isKeyword("sealed")}) <IDENTIFIER> { modifiers |= AccessNode.SEALED; }
|
||||
| LOOKAHEAD({isNonSealedModifier()}) <IDENTIFIER> <MINUS> <IDENTIFIER> { modifiers |= AccessNode.NON_SEALED; }
|
||||
| Annotation()
|
||||
)
|
||||
)*
|
||||
@ -906,6 +984,7 @@ void ClassOrInterfaceDeclaration():
|
||||
[ TypeParameters() ]
|
||||
[ ExtendsList() ]
|
||||
[ ImplementsList() ]
|
||||
[ LOOKAHEAD({isKeyword("permits")}) PermittedSubclasses() ]
|
||||
ClassOrInterfaceBody()
|
||||
}
|
||||
|
||||
@ -923,6 +1002,20 @@ void ImplementsList():
|
||||
( "," AnnotatedClassOrInterfaceType() )*
|
||||
}
|
||||
|
||||
void PermittedSubclasses() #PermitsList:
|
||||
{
|
||||
Token t;
|
||||
}
|
||||
{
|
||||
t = <IDENTIFIER> {
|
||||
if (!"permits".equals(t.image)) {
|
||||
throw new ParseException("ERROR: expecting permits");
|
||||
}
|
||||
}
|
||||
(TypeAnnotation())* ClassOrInterfaceType()
|
||||
( "," (TypeAnnotation())* ClassOrInterfaceType() )*
|
||||
}
|
||||
|
||||
void EnumDeclaration():
|
||||
{
|
||||
JavaccToken t;
|
||||
@ -2069,13 +2162,45 @@ void Block() :
|
||||
}
|
||||
|
||||
void BlockStatement() #void:
|
||||
{}
|
||||
{int mods = 0;}
|
||||
{
|
||||
LOOKAHEAD( { isNextTokenAnAssert() } ) AssertStatement()
|
||||
| LOOKAHEAD( { isYieldStart() } ) YieldStatement()
|
||||
|
|
||||
LOOKAHEAD(( "final" | Annotation() )* Type() <IDENTIFIER>)
|
||||
LocalVariableDeclaration() ";" {
|
||||
| LOOKAHEAD( "@" | "final" )
|
||||
// this eagerly parses all modifiers and annotations. After that, either a local type declaration
|
||||
// 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()
|
||||
(
|
||||
LOOKAHEAD({localTypeDeclLookahead()}) LocalTypeDecl(mods)
|
||||
|
|
||||
{
|
||||
List<ASTAnnotation> annotations = new ArrayList<ASTAnnotation>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
";"
|
||||
)
|
||||
| LOOKAHEAD({classModifierLookahead() || localTypeDeclLookahead()})
|
||||
mods=Modifiers()
|
||||
LocalTypeDecl(mods)
|
||||
| LOOKAHEAD(Type() <IDENTIFIER>)
|
||||
LocalVariableDeclaration() ";" {
|
||||
// make it so that the LocalVariableDeclaration's last token is the semicolon
|
||||
AbstractJavaNode top = (AbstractJavaNode) jjtree.peekNode();
|
||||
top.setLastToken(getToken(0));
|
||||
@ -2084,25 +2209,21 @@ void BlockStatement() #void:
|
||||
// we need to lookahead until the "class" token,
|
||||
// because a method ref may be annotated
|
||||
// -> so Expression, and hence Statement, may start with "@"
|
||||
LOOKAHEAD(ModifierList() "class") LocalClassDecl()
|
||||
LOOKAHEAD(ModifierList() "class") LocalTypeDecl()
|
||||
|
|
||||
Statement()
|
||||
}
|
||||
|
||||
void LocalClassDecl() #LocalClassStatement:
|
||||
void LocalTypeDecl(int mods) #LocalClassStatement:
|
||||
{}
|
||||
{
|
||||
// this preserves the modifiers of the local class.
|
||||
// it allows for modifiers that are forbidden for local classes,
|
||||
// but anyway we are *not* checking modifiers for incompatibilities
|
||||
// anywhere else in this grammar (and indeed the production Modifiers
|
||||
// accepts any modifier explicitly for the purpose of forgiving modifier errors,
|
||||
// and reporting them later if needed --see its documentation).
|
||||
|
||||
// In particular, it unfortunately allows local class declarations to start
|
||||
// with a "default" modifier, which introduces an ambiguity with default
|
||||
// switch labels. This is guarded by a custom lookahead around SwitchLabel
|
||||
ModifierList() ClassOrInterfaceDeclaration()
|
||||
(
|
||||
LOOKAHEAD(<CLASS>) ClassOrInterfaceDeclaration(mods)
|
||||
| LOOKAHEAD(<INTERFACE>) ClassOrInterfaceDeclaration(mods)
|
||||
| LOOKAHEAD({isKeyword("record")}) RecordDeclaration(mods)
|
||||
| LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(mods)
|
||||
| AnnotationTypeDeclaration(mods)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -28,9 +28,10 @@ public class JavaLanguageModule extends BaseLanguageModule {
|
||||
addVersion("11", new JavaLanguageHandler(11));
|
||||
addVersion("12", new JavaLanguageHandler(12));
|
||||
addVersion("13", new JavaLanguageHandler(13));
|
||||
addVersion("13-preview", new JavaLanguageHandler(13, true));
|
||||
addDefaultVersion("14", new JavaLanguageHandler(14)); // 14 is the default
|
||||
addVersion("14", new JavaLanguageHandler(14));
|
||||
addVersion("14-preview", new JavaLanguageHandler(14, true));
|
||||
addDefaultVersion("15", new JavaLanguageHandler(15)); // 15 is the default
|
||||
addVersion("15-preview", new JavaLanguageHandler(15, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,12 @@
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
@ -67,4 +72,10 @@ public final class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclara
|
||||
return extendsList == null ? null : extendsList.iterator().next();
|
||||
}
|
||||
|
||||
|
||||
@Experimental
|
||||
public List<ASTClassOrInterfaceType> getPermittedSubclasses() {
|
||||
return ASTList.orEmpty(children(ASTPermitsList.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ public interface ASTLiteral extends ASTPrimaryExpression {
|
||||
return this instanceof ASTStringLiteral;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this is a {@linkplain ASTCharLiteral character literal}.
|
||||
*/
|
||||
|
@ -8,7 +8,7 @@ import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
/**
|
||||
* A pattern (for pattern matching constructs like {@link ASTInstanceOfExpression InstanceOfExpression}).
|
||||
* This is a JDK 14 preview feature and is subject to change.
|
||||
* This is a JDK 14 and JDK 15 preview feature and is subject to change.
|
||||
*
|
||||
* <p>This interface will be implemented by all forms of patterns. For
|
||||
* now, only type test patterns are supported. Record deconstruction
|
||||
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
|
||||
/**
|
||||
* Represents the {@code permits} clause of a (sealed) class declaration.
|
||||
*
|
||||
* <p>This is a Java 15 Preview feature.
|
||||
*
|
||||
* <p>See https://openjdk.java.net/jeps/360
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
* PermittedSubclasses ::= "permits" (TypeAnnotation)* ClassOrInterfaceType
|
||||
* ( "," (TypeAnnotation)* ClassOrInterfaceType )*
|
||||
* </pre>
|
||||
*/
|
||||
@Experimental
|
||||
public final class ASTPermitsList extends AbstractJavaNode implements Iterable<ASTClassOrInterfaceType> {
|
||||
|
||||
ASTPermitsList(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ASTClassOrInterfaceType> iterator() {
|
||||
return children(ASTClassOrInterfaceType.class).iterator();
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
/**
|
||||
* Defines the body of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 preview feature).
|
||||
* Defines the body of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 and JDK 15 preview feature).
|
||||
* This can contain additional methods and or constructors.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
|
@ -11,7 +11,7 @@ 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 preview feature).
|
||||
* Defines a single component of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 and JDK 15 preview feature).
|
||||
*
|
||||
* <p>The varargs ellipsis {@code "..."} is parsed as an {@linkplain ASTArrayTypeDim array dimension}
|
||||
* in the type node.
|
||||
|
@ -11,7 +11,7 @@ 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 preview feature).
|
||||
* Defines the state description of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 14 and JDK 15 preview feature).
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
|
@ -10,7 +10,7 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
|
||||
/**
|
||||
* This defines a compact constructor for a {@link ASTRecordDeclaration RecordDeclaration}
|
||||
* (JDK 14 preview feature). Compact constructors implicitly declares formal
|
||||
* (JDK 14 and JDK 15 preview feature). Compact constructors implicitly declares formal
|
||||
* parameters corresponding to the record component list. These can be
|
||||
* fetched from {@link #getSymbol()}.
|
||||
*
|
||||
|
@ -12,7 +12,7 @@ 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 preview feature).
|
||||
* A record declaration is a special data class type (JDK 14 and JDK 15 preview feature).
|
||||
* This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
@ -27,7 +27,7 @@ import net.sourceforge.pmd.lang.ast.NodeStream;
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
@Experimental
|
||||
public final class ASTRecordDeclaration extends AbstractAnyTypeDeclaration {
|
||||
|
@ -50,6 +50,11 @@ abstract class AbstractJavaNode extends AbstractJjtreeNode<AbstractJavaNode, Jav
|
||||
super.addChild(child, index);
|
||||
}
|
||||
|
||||
@Override // override to make it accessible to tests that build nodes (which have been removed on java-grammar)
|
||||
protected void insertChild(AbstractJavaNode child, int index) {
|
||||
super.insertChild(child, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeChildAtIndex(int childIndex) {
|
||||
super.removeChildAtIndex(childIndex);
|
||||
|
@ -15,6 +15,7 @@ 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;
|
||||
@ -44,6 +45,7 @@ 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;
|
||||
@ -116,13 +118,16 @@ public class LanguageLevelChecker<T> {
|
||||
SWITCH_EXPRESSIONS(12, 13, true),
|
||||
SWITCH_RULES(12, 13, true),
|
||||
|
||||
TEXT_BLOCK_LITERALS(13, 14, false),
|
||||
TEXT_BLOCK_LITERALS(13, 14, true),
|
||||
YIELD_STATEMENTS(13, 13, true),
|
||||
|
||||
/** \s */
|
||||
SPACE_STRING_ESCAPES(14, 14, false),
|
||||
RECORD_DECLARATIONS(14, 14, false),
|
||||
TYPE_TEST_PATTERNS_IN_INSTANCEOF(14, 14, false);
|
||||
SPACE_STRING_ESCAPES(14, 14, true),
|
||||
RECORD_DECLARATIONS(14, 15, false),
|
||||
TYPE_TEST_PATTERNS_IN_INSTANCEOF(14, 15, false),
|
||||
SEALED_CLASSES(15, 15, false),
|
||||
|
||||
; // SUPPRESS CHECKSTYLE enum trailing semi is awesome
|
||||
|
||||
|
||||
private final int minPreviewVersion;
|
||||
@ -473,6 +478,12 @@ public class LanguageLevelChecker<T> {
|
||||
|
||||
@Override
|
||||
public Void visit(ASTAnyTypeDeclaration node, T data) {
|
||||
if ((node.getModifiers() & (AccessNode.SEALED | AccessNode.NON_SEALED)) != 0) {
|
||||
check(node, PreviewFeature.SEALED_CLASSES, data);
|
||||
} else if (node.isLocal() && node.getTypeKind() != TypeKind.CLASS) {
|
||||
check(node, PreviewFeature.SEALED_CLASSES, data);
|
||||
}
|
||||
|
||||
String simpleName = node.getSimpleName();
|
||||
if ("var".equals(simpleName)) {
|
||||
check(node, ReservedIdentifiers.VAR_AS_A_TYPE_NAME, data);
|
||||
|
@ -40,7 +40,7 @@ public class PMDASMVisitor extends ClassVisitor {
|
||||
public List<String> innerClasses;
|
||||
|
||||
public PMDASMVisitor(String outerName) {
|
||||
super(Opcodes.ASM7);
|
||||
super(Opcodes.ASM9);
|
||||
this.outerName = outerName;
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ public class PMDASMVisitor extends ClassVisitor {
|
||||
private PMDASMVisitor parent;
|
||||
|
||||
PMDFieldVisitor(PMDASMVisitor visitor) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM9);
|
||||
parent = visitor;
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ public class PMDASMVisitor extends ClassVisitor {
|
||||
private PMDASMVisitor parent;
|
||||
|
||||
PMDAnnotationVisitor(PMDASMVisitor visitor) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM9);
|
||||
parent = visitor;
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@ public class PMDASMVisitor extends ClassVisitor {
|
||||
private PMDASMVisitor parent;
|
||||
|
||||
PMDSignatureVisitor(PMDASMVisitor visitor) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM9);
|
||||
this.parent = visitor;
|
||||
}
|
||||
|
||||
@ -292,7 +292,7 @@ public class PMDASMVisitor extends ClassVisitor {
|
||||
private PMDASMVisitor parent;
|
||||
|
||||
PMDMethodVisitor(PMDASMVisitor visitor) {
|
||||
super(Opcodes.ASM5);
|
||||
super(Opcodes.ASM9);
|
||||
parent = visitor;
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,8 @@ public class LanguageVersionDiscovererTest {
|
||||
File javaFile = new File("/path/to/MyClass.java");
|
||||
|
||||
LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(javaFile);
|
||||
assertEquals("LanguageVersion must be Java 14 !",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14"), languageVersion);
|
||||
assertEquals("LanguageVersion must be Java 15 !",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"), languageVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +48,7 @@ public class LanguageVersionDiscovererTest {
|
||||
public void testLanguageVersionDiscoverer() {
|
||||
PMDConfiguration configuration = new PMDConfiguration();
|
||||
LanguageVersionDiscoverer languageVersionDiscoverer = configuration.getLanguageVersionDiscoverer();
|
||||
assertEquals("Default Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14"),
|
||||
assertEquals("Default Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"),
|
||||
languageVersionDiscoverer
|
||||
.getDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)));
|
||||
configuration
|
||||
|
@ -52,6 +52,10 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest {
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14"), },
|
||||
{ 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"), },
|
||||
|
||||
// this one won't be found: case sensitive!
|
||||
{ "JAVA", "JAVA", "1.7", null, },
|
||||
|
@ -1,52 +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.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.ParseException;
|
||||
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
|
||||
|
||||
@Ignore("those tests depend on type resolution")
|
||||
public class Java13Test {
|
||||
|
||||
|
||||
private final JavaParsingHelper java12 =
|
||||
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("12")
|
||||
.withResourceContext(getClass(), "jdkversiontests/java13/");
|
||||
|
||||
private final JavaParsingHelper java13p = java12.withDefaultVersion("13-preview");
|
||||
|
||||
@Test
|
||||
public void testTextBlocks() {
|
||||
ASTCompilationUnit compilationUnit = java13p.parseResource("TextBlocks.java");
|
||||
List<ASTStringLiteral> literals = compilationUnit.findDescendantsOfType(ASTStringLiteral.class);
|
||||
Assert.assertEquals(10, literals.size());
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ASTStringLiteral literal = literals.get(i);
|
||||
Assert.assertTrue(literal.isTextBlock());
|
||||
}
|
||||
Assert.assertEquals("\"\"\"\n"
|
||||
+ " <html>\n"
|
||||
+ " <body>\n"
|
||||
+ " <p>Hello, world</p>\n"
|
||||
+ " </body>\n"
|
||||
+ " </html>\n"
|
||||
+ " \"\"\"",
|
||||
literals.get(0).getImage());
|
||||
Assert.assertFalse(literals.get(8).isTextBlock());
|
||||
Assert.assertTrue(literals.get(9).isTextBlock());
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testTextBlocksBeforeJava13() {
|
||||
java12.parseResource("TextBlocks.java");
|
||||
}
|
||||
|
||||
}
|
@ -29,15 +29,12 @@ public class Java14Test {
|
||||
|
||||
private final JavaParsingHelper java14p = java14.withDefaultVersion("14-preview");
|
||||
private final JavaParsingHelper java13 = java14.withDefaultVersion("13");
|
||||
private final JavaParsingHelper java13p = java14.withDefaultVersion("13-preview");
|
||||
|
||||
/**
|
||||
* Tests switch expressions with yield.
|
||||
* The switch expressions have no changed between java 13-preview and 14, so behave exactly the same.
|
||||
*/
|
||||
@Test
|
||||
public void switchExpressions() {
|
||||
parseAndCheckSwitchExpression(java13p);
|
||||
parseAndCheckSwitchExpression(java14);
|
||||
parseAndCheckSwitchExpression(java14p);
|
||||
}
|
||||
@ -87,11 +84,6 @@ public class Java14Test {
|
||||
|
||||
@Test
|
||||
public void checkYieldConditionalBehaviour() {
|
||||
checkYieldStatements(java13p);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkYieldConditionalBehaviourJ14() {
|
||||
checkYieldStatements(java14);
|
||||
}
|
||||
|
||||
@ -142,7 +134,6 @@ public class Java14Test {
|
||||
|
||||
@Test
|
||||
public void multipleCaseLabels() {
|
||||
multipleCaseLabels(java13p);
|
||||
multipleCaseLabels(java14);
|
||||
multipleCaseLabels(java14p);
|
||||
}
|
||||
@ -158,7 +149,6 @@ public class Java14Test {
|
||||
|
||||
@Test
|
||||
public void switchRules() {
|
||||
switchRules(java13p);
|
||||
switchRules(java14);
|
||||
switchRules(java14p);
|
||||
}
|
||||
@ -186,7 +176,6 @@ public class Java14Test {
|
||||
|
||||
@Test
|
||||
public void simpleSwitchExpressions() {
|
||||
simpleSwitchExpressions(java13p);
|
||||
simpleSwitchExpressions(java14);
|
||||
simpleSwitchExpressions(java14p);
|
||||
}
|
||||
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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 Java15PreviewTreeDumpTest extends BaseTreeDumpTest {
|
||||
private final JavaParsingHelper java15p =
|
||||
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15-preview")
|
||||
.withResourceContext(Java15PreviewTreeDumpTest.class, "jdkversiontests/java15p/");
|
||||
private final JavaParsingHelper java15 = java15p.withDefaultVersion("15");
|
||||
|
||||
public Java15PreviewTreeDumpTest() {
|
||||
super(new RelevantAttributePrinter(), ".java");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseParsingHelper<?, ?> getParser() {
|
||||
return java15p;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternMatchingInstanceof() {
|
||||
doTest("PatternMatchingInstanceof");
|
||||
|
||||
// extended tests for type resolution etc.
|
||||
ASTCompilationUnit compilationUnit = java15p.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 patternMatchingInstanceofBeforeJava15PreviewShouldFail() {
|
||||
java15.parseResource("PatternMatchingInstanceof.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordPoint() {
|
||||
doTest("Point");
|
||||
|
||||
// extended tests for type resolution etc.
|
||||
ASTCompilationUnit compilationUnit = java15p.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 recordPointBeforeJava15PreviewShouldFail() {
|
||||
java15.parseResource("Point.java");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void recordCtorWithThrowsShouldFail() {
|
||||
java15p.parse(" record R {"
|
||||
+ " R throws IOException {}"
|
||||
+ " }");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void recordMustNotExtend() {
|
||||
java15p.parse("record RecordEx(int x) extends Number { }");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void innerRecords() {
|
||||
doTest("Records");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void recordIsARestrictedIdentifier() {
|
||||
java15p.parse("public class record {}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localRecords() {
|
||||
doTest("LocalRecords");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void sealedClassBeforeJava15Preview() {
|
||||
java15.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() {
|
||||
java15.parseResource("expression/Expr.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sealedInterface() {
|
||||
doTest("expression/Expr");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localInterfaceAndEnums() {
|
||||
doTest("LocalInterfacesAndEnums");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void localInterfacesAndEnumsBeforeJava15PreviewShouldFail() {
|
||||
java15.parseResource("LocalInterfacesAndEnums.java");
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 Java15TreeDumpTest extends BaseTreeDumpTest {
|
||||
private final JavaParsingHelper java15 =
|
||||
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15")
|
||||
.withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java15/");
|
||||
private final JavaParsingHelper java15p = java15.withDefaultVersion("15-preview");
|
||||
private final JavaParsingHelper java14 = java15.withDefaultVersion("14");
|
||||
|
||||
public Java15TreeDumpTest() {
|
||||
super(new RelevantAttributePrinter(), ".java");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseParsingHelper<?, ?> getParser() {
|
||||
return java15;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textBlocks() {
|
||||
doTest("TextBlocks");
|
||||
java15p.parseResource("TextBlocks.java"); // make sure we can parse it with preview as well
|
||||
}
|
||||
|
||||
@Test(expected = net.sourceforge.pmd.lang.ast.ParseException.class)
|
||||
public void textBlocksBeforeJava15ShouldFail() {
|
||||
java14.parseResource("TextBlocks.java");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void stringEscapeSequenceShouldFail() {
|
||||
java14.parse("class Foo { String s =\"a\\sb\"; }");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sealedAndNonSealedIdentifiers() {
|
||||
doTest("NonSealedIdentifier");
|
||||
java15p.parseResource("NonSealedIdentifier.java"); // make sure we can parse it with preview as well
|
||||
}
|
||||
}
|
@ -7,22 +7,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 java.io.IOException
|
||||
|
||||
class ASTPatternTest : ParserTestSpec({
|
||||
|
||||
parserTest("Test patterns only available on JDK 14 (preview)", javaVersions = !J14__PREVIEW) {
|
||||
parserTest("Test patterns only available on JDK 14+15 (preview)", javaVersions = JavaVersion.except(J14__PREVIEW, J15__PREVIEW)) {
|
||||
|
||||
|
||||
inContext(ExpressionParsingCtx) {
|
||||
"obj instanceof Class c" should throwParseException {
|
||||
it.message.shouldContain("Type test patterns in instanceof is a preview feature of JDK 14, you should select your language version accordingly")
|
||||
it.message.shouldContain("Type test patterns in instanceof is a preview feature of JDK")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parserTest("Test simple patterns", javaVersion = J14__PREVIEW) {
|
||||
parserTest("Test simple patterns", javaVersions = listOf(J14__PREVIEW, J15__PREVIEW)) {
|
||||
|
||||
importedTypes += IOException::class.java
|
||||
inContext(ExpressionParsingCtx) {
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBe
|
||||
|
||||
class Java15KotlinTest: ParserTestSpec( {
|
||||
|
||||
// Note: More tests are in ASTLiteralTest.
|
||||
parserTest("textBlocks", javaVersions = JavaVersion.J15..JavaVersion.Latest) {
|
||||
("\"\"\"\n" +
|
||||
" <html> \n" +
|
||||
" <body>\n" +
|
||||
" <p>Hello, world</p> \n" +
|
||||
" </body> \n" +
|
||||
" </html> \n" +
|
||||
" \"\"\"") should matchExpr<ASTExpression> {
|
||||
child<ASTPrimaryExpression> {
|
||||
child<ASTPrimaryPrefix> {
|
||||
child<ASTLiteral> {
|
||||
it::isTextBlock shouldBe true
|
||||
it::getEscapedStringLiteral shouldBe
|
||||
"\"\"\"\n" +
|
||||
" <html> \n" +
|
||||
" <body>\n" +
|
||||
" <p>Hello, world</p> \n" +
|
||||
" </body> \n" +
|
||||
" </html> \n" +
|
||||
" \"\"\""
|
||||
it::getTextBlockContent shouldBe
|
||||
"<html>\n" +
|
||||
" <body>\n" +
|
||||
" <p>Hello, world</p>\n" +
|
||||
" </body>\n" +
|
||||
"</html>\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
@ -28,8 +28,9 @@ import java.beans.PropertyDescriptor
|
||||
enum class JavaVersion : Comparable<JavaVersion> {
|
||||
J1_3, J1_4, J1_5, J1_6, J1_7, J1_8, J9, J10, J11,
|
||||
J12,
|
||||
J13, J13__PREVIEW,
|
||||
J14, J14__PREVIEW;
|
||||
J13,
|
||||
J14, J14__PREVIEW,
|
||||
J15, J15__PREVIEW;
|
||||
|
||||
/** Name suitable for use with e.g. [JavaParsingHelper.parse] */
|
||||
val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').toLowerCase()
|
||||
@ -55,9 +56,10 @@ enum class JavaVersion : Comparable<JavaVersion> {
|
||||
val Latest = values().last()
|
||||
val Earliest = values().first()
|
||||
|
||||
fun except(vararg versions: JavaVersion) = values().toList() - versions
|
||||
fun except(versions: List<JavaVersion>) = values().toList() - versions
|
||||
fun except(v1: JavaVersion, vararg versions: JavaVersion) =
|
||||
values().toList() - v1 - versions
|
||||
|
||||
fun except(versions: List<JavaVersion>) = values().toList() - versions
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
/**
|
||||
* @see <a href="http://openjdk.java.net/jeps/355">JEP 355: Text Blocks (Preview)</a>
|
||||
*/
|
||||
public class TextBlocks {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String html = """
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello, world</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
System.out.println(html);
|
||||
|
||||
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
|
||||
\""";
|
||||
""";
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
public class NonSealedIdentifier {
|
||||
public static void main(String[] args) {
|
||||
int result = 0;
|
||||
int non = 1;
|
||||
// sealed is a valid identifier name in both Java15 and Java15 Preview
|
||||
int sealed = 2;
|
||||
// non-sealed is a valid subtraction expression in both Java15 and Java15 Preview
|
||||
result = non-sealed;
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
+- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = true]
|
||||
+- TypeDeclaration[]
|
||||
+- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "NonSealedIdentifier", @Default = false, @Final = false, @Image = "NonSealedIdentifier", @Interface = false, @Local = false, @Modifiers = 1, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = false, @SimpleName = "NonSealedIdentifier", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.CLASS, @Volatile = false]
|
||||
+- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]
|
||||
+- ClassOrInterfaceBodyDeclaration[@AnonymousInnerClass = false, @EnumChild = false, @Kind = DeclarationKind.METHOD]
|
||||
+- MethodDeclaration[@Abstract = false, @Arity = 1, @Default = false, @Final = false, @InterfaceMember = false, @Kind = MethodLikeKind.METHOD, @MethodName = "main", @Modifiers = 17, @Name = "main", @Native = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Static = true, @Strictfp = false, @Synchronized = false, @SyntacticallyAbstract = false, @SyntacticallyPublic = true, @Transient = false, @Void = true, @Volatile = false]
|
||||
+- ResultType[@Void = true, @returnsArray = false]
|
||||
+- MethodDeclarator[@Image = "main", @ParameterCount = 1]
|
||||
| +- FormalParameters[@ParameterCount = 1, @Size = 1]
|
||||
| +- FormalParameter[@Abstract = false, @Array = true, @ArrayDepth = 1, @Default = false, @ExplicitReceiverParameter = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @Varargs = false, @Volatile = false]
|
||||
| +- Type[@Array = true, @ArrayDepth = 1, @ArrayType = true, @TypeImage = "String"]
|
||||
| | +- ReferenceType[@Array = true, @ArrayDepth = 1]
|
||||
| | +- ClassOrInterfaceType[@AnonymousClass = false, @Array = true, @ArrayDepth = 1, @Image = "String", @ReferenceToClassSameCompilationUnit = false]
|
||||
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = true, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = true, @Image = "args", @LambdaParameter = false, @LocalVariable = false, @Name = "args", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "args"]
|
||||
+- Block[@containsComment = false]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
| +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "result", @Volatile = false]
|
||||
| +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "int"]
|
||||
| | +- PrimitiveType[@Array = false, @ArrayDepth = 0, @Boolean = false, @Image = "int"]
|
||||
| +- VariableDeclarator[@Initializer = true, @Name = "result"]
|
||||
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = false, @Image = "result", @LambdaParameter = false, @LocalVariable = true, @Name = "result", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "result"]
|
||||
| +- VariableInitializer[]
|
||||
| +- Expression[@StandAlonePrimitive = true]
|
||||
| +- PrimaryExpression[]
|
||||
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "0", @FloatLiteral = false, @Image = "0", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "0", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 0, @ValueAsLong = 0]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
| +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "non", @Volatile = false]
|
||||
| +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "int"]
|
||||
| | +- PrimitiveType[@Array = false, @ArrayDepth = 0, @Boolean = false, @Image = "int"]
|
||||
| +- VariableDeclarator[@Initializer = true, @Name = "non"]
|
||||
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = false, @Image = "non", @LambdaParameter = false, @LocalVariable = true, @Name = "non", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "non"]
|
||||
| +- VariableInitializer[]
|
||||
| +- Expression[@StandAlonePrimitive = true]
|
||||
| +- PrimaryExpression[]
|
||||
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "1", @FloatLiteral = false, @Image = "1", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "1", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 1, @ValueAsLong = 1]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
| +- LocalVariableDeclaration[@Abstract = false, @Array = false, @ArrayDepth = 0, @Default = false, @Final = false, @Modifiers = 0, @Native = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeInferred = false, @VariableName = "sealed", @Volatile = false]
|
||||
| +- Type[@Array = false, @ArrayDepth = 0, @ArrayType = false, @TypeImage = "int"]
|
||||
| | +- PrimitiveType[@Array = false, @ArrayDepth = 0, @Boolean = false, @Image = "int"]
|
||||
| +- VariableDeclarator[@Initializer = true, @Name = "sealed"]
|
||||
| +- VariableDeclaratorId[@Array = false, @ArrayDepth = 0, @ArrayType = false, @ExceptionBlockParameter = false, @ExplicitReceiverParameter = false, @Field = false, @Final = false, @FormalParameter = false, @Image = "sealed", @LambdaParameter = false, @LocalVariable = true, @Name = "sealed", @PatternBinding = false, @ResourceDeclaration = false, @TypeInferred = false, @VariableName = "sealed"]
|
||||
| +- VariableInitializer[]
|
||||
| +- Expression[@StandAlonePrimitive = true]
|
||||
| +- PrimaryExpression[]
|
||||
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| +- Literal[@CharLiteral = false, @DoubleLiteral = false, @EscapedStringLiteral = "2", @FloatLiteral = false, @Image = "2", @IntLiteral = true, @LongLiteral = false, @SingleCharacterStringLiteral = false, @StringLiteral = false, @TextBlock = false, @TextBlockContent = "2", @ValueAsDouble = NaN, @ValueAsFloat = NaN, @ValueAsInt = 2, @ValueAsLong = 2]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
| +- Statement[]
|
||||
| +- StatementExpression[]
|
||||
| +- PrimaryExpression[]
|
||||
| | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| | +- Name[@Image = "result"]
|
||||
| +- AssignmentOperator[@Compound = false, @Image = "="]
|
||||
| +- Expression[@StandAlonePrimitive = false]
|
||||
| +- AdditiveExpression[@Image = "-", @Operator = "-"]
|
||||
| +- PrimaryExpression[]
|
||||
| | +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| | +- Name[@Image = "non"]
|
||||
| +- PrimaryExpression[]
|
||||
| +- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| +- Name[@Image = "sealed"]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
+- Statement[]
|
||||
+- StatementExpression[]
|
||||
+- PrimaryExpression[]
|
||||
+- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
| +- Name[@Image = "System.out.println"]
|
||||
+- PrimarySuffix[@ArgumentCount = 1, @Arguments = true, @ArrayDereference = false]
|
||||
+- Arguments[@ArgumentCount = 1, @Size = 1]
|
||||
+- ArgumentList[@Size = 1]
|
||||
+- Expression[@StandAlonePrimitive = false]
|
||||
+- PrimaryExpression[]
|
||||
+- PrimaryPrefix[@SuperModifier = false, @ThisModifier = false]
|
||||
+- Name[@Image = "result"]
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
/**
|
||||
* Text Blocks are a permanent language feature with JDK 15.
|
||||
*
|
||||
* @see <a href="https://openjdk.java.net/jeps/378>JEP 378: Text Blocks</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>"));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user