Merge pull request #3375 from adangel:issue-3366-support-jdk-17
[java] Support JDK 17 (LTS) #3375
This commit is contained in:
commit
bf9058aae7
@ -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, 14, 14-preview, 15 (default), 15-preview
|
||||
13, 14, 15, 16, 16-preview, 17 (default), 17-preview
|
||||
* [ecmascript](pmd_rules_ecmascript.html) (JavaScript)
|
||||
* [jsp](pmd_rules_jsp.html)
|
||||
* [modelica](pmd_rules_modelica.html)
|
||||
|
@ -14,6 +14,20 @@ This is a {{ site.pmd.release_type }} release.
|
||||
|
||||
### New and noteworthy
|
||||
|
||||
#### Java 17 Support
|
||||
|
||||
This release of PMD brings support for Java 17. PMD supports [JEP 409: Sealed Classes](https://openjdk.java.net/jeps/409)
|
||||
which has been promoted to be a standard language feature of Java 17.
|
||||
|
||||
PMD also supports [JEP 406: Pattern Matching for switch (Preview)](https://openjdk.java.net/jeps/406) 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 `17-preview`:
|
||||
|
||||
export PMD_JAVA_OPTS=--enable-preview
|
||||
./run.sh pmd -language java -version 17-preview ...
|
||||
|
||||
Note: Support for Java 15 preview language features have been removed. The version "15-preview" is no longer available.
|
||||
|
||||
#### New rules
|
||||
|
||||
This release ships with 3 new Java rules.
|
||||
@ -102,6 +116,14 @@ This release ships with 3 new Java rules.
|
||||
|
||||
### API Changes
|
||||
|
||||
#### Experimental APIs
|
||||
|
||||
* The AST types and APIs around Sealed Classes are not experimental anymore:
|
||||
* {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#isSealed() %},
|
||||
{% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#isNonSealed() %},
|
||||
{% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#getPermittedSubclasses() %}
|
||||
* {% jdoc java::lang.java.ast.ASTPermitsList %}
|
||||
|
||||
#### Internal API
|
||||
|
||||
Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0.
|
||||
|
@ -23,6 +23,9 @@
|
||||
<exclude-pattern>.*/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java10/LocalVariableTypeInference_varAsEnumName.java</exclude-pattern>
|
||||
<exclude-pattern>.*/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java10/LocalVariableTypeInference_varAsTypeIdentifier.java</exclude-pattern>
|
||||
|
||||
<!-- this file contains are parse error explicitly -->
|
||||
<exclude-pattern>.*/net/sourceforge/pmd/lang/java/ast/InfiniteLoopInLookahead.java</exclude-pattern>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml" />
|
||||
<rule ref="category/java/codestyle.xml" />
|
||||
<rule ref="category/java/design.xml" />
|
||||
|
@ -1,4 +1,9 @@
|
||||
/**
|
||||
* Promote "JEP 409: Sealed Classes" as permanent language feature with Java 17.
|
||||
* Support "JEP 406: Pattern Matching for switch (Preview)" for Java 17 Preview.
|
||||
* Remove support for Java 15 preview language features
|
||||
* Andreas Dangel 07/2021
|
||||
*====================================================================
|
||||
* Fix #3117 - infinite loop when parsing invalid code nested in lambdas
|
||||
* Andreas Dangel 03/2021
|
||||
*====================================================================
|
||||
@ -394,12 +399,6 @@ public class JavaParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkforBadInstanceOfPattern() {
|
||||
if (!(jdkVersion == 15 && preview || jdkVersion >= 16)) {
|
||||
throwParseException("Pattern Matching for instanceof is only supported with Java 15 Preview and Java >= 16");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForBadAnonymousDiamondUsage() {
|
||||
if (jdkVersion < 9) {
|
||||
ASTAllocationExpression node = (ASTAllocationExpression)jjtree.peekNode();
|
||||
@ -443,14 +442,14 @@ public class JavaParser {
|
||||
if (jdkVersion >= 14 && "yield".equals(image)) {
|
||||
throwParseException("With JDK 14, 'yield' is a contextual keyword and cannot be used for type declarations!");
|
||||
}
|
||||
if ((jdkVersion == 15 && preview || jdkVersion >= 16) && "record".equals(image)) {
|
||||
throwParseException("With JDK 15 Preview and Java >= 16, 'record' is a contextual keyword and cannot be used for type declarations!");
|
||||
if (jdkVersion >= 16 && "record".equals(image)) {
|
||||
throwParseException("With JDK >= 16, 'record' is a contextual keyword and cannot be used for type declarations!");
|
||||
}
|
||||
if ((jdkVersion == 15 && preview || jdkVersion == 16 && preview) && "sealed".equals(image)) {
|
||||
throwParseException("With JDK 15 Preview and JDK 16 Preview, 'sealed' is a contextual keyword and cannot be used for type declarations!");
|
||||
if ((jdkVersion == 16 && preview || jdkVersion >= 17) && "sealed".equals(image)) {
|
||||
throwParseException("With JDK 16 Preview and JDK >= 17, 'sealed' is a contextual keyword and cannot be used for type declarations!");
|
||||
}
|
||||
if ((jdkVersion == 15 && preview || jdkVersion == 16 && preview) && "permits".equals(image)) {
|
||||
throwParseException("With JDK 15 Preview and JDK 16 Preview, 'permits' is a contextual keyword and cannot be used for type declarations!");
|
||||
if ((jdkVersion == 16 && preview || jdkVersion >= 17) && "permits".equals(image)) {
|
||||
throwParseException("With JDK 16 Preview and JDK >= 17, 'permits' is a contextual keyword and cannot be used for type declarations!");
|
||||
}
|
||||
}
|
||||
private void checkForMultipleCaseLabels() {
|
||||
@ -497,32 +496,66 @@ public class JavaParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkforBadInstanceOfPattern() {
|
||||
if (jdkVersion < 16) {
|
||||
throwParseException("Pattern Matching for instanceof is only supported with JDK >= 16");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRecordTypeSupported() {
|
||||
return jdkVersion >= 16;
|
||||
}
|
||||
|
||||
private void checkForRecordType() {
|
||||
if (!isRecordTypeSupported()) {
|
||||
throwParseException("Records are only supported with Java 15 Preview and Java >= 16");
|
||||
throwParseException("Records are only supported with JDK >= 16");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForLocalInterfaceOrEnumType() {
|
||||
if (!isRecordTypeSupported()) {
|
||||
throwParseException("Local interfaces and enums are only supported with Java 15 Preview and Java >= 16");
|
||||
throwParseException("Local interfaces and enums are only supported with JDK >= 16");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRecordTypeSupported() {
|
||||
return jdkVersion == 15 && preview || jdkVersion >= 16;
|
||||
}
|
||||
|
||||
private boolean isSealedClassSupported() {
|
||||
return jdkVersion == 15 && preview || jdkVersion == 16 && preview;
|
||||
return jdkVersion == 16 && preview || jdkVersion >= 17;
|
||||
}
|
||||
|
||||
private void checkForSealedClassUsage() {
|
||||
if (!isSealedClassSupported()) {
|
||||
throwParseException("Sealed Classes are only supported with Java 15 Preview");
|
||||
throwParseException("Sealed Classes are only supported with JDK 16 Preview and JDK >= 17.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isJEP406Supported() {
|
||||
return jdkVersion == 17 && preview;
|
||||
}
|
||||
|
||||
private void checkForPatternMatchingInSwitch() {
|
||||
if (!isJEP406Supported()) {
|
||||
throwParseException("Pattern Matching in Switch is only supported with JDK 17 Preview.");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForNullCaseLabel() {
|
||||
if (!isJEP406Supported()) {
|
||||
throwParseException("Null case labels in switch are only supported with JDK 17 Preview.");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForDefaultCaseLabel() {
|
||||
if (!isJEP406Supported()) {
|
||||
throwParseException("Default case labels in switch are only supported with JDK 17 Preview.");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForGuardedPatterns() {
|
||||
if (!isJEP406Supported()) {
|
||||
throwParseException("Guarded patterns are only supported with JDK 17 Preview.");
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -586,7 +619,7 @@ public class JavaParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean classModifierLookahead() {
|
||||
private boolean classModifierForLocalTypesLookahead() {
|
||||
Token next = getToken(1);
|
||||
return next.kind == AT
|
||||
|| next.kind == PUBLIC
|
||||
@ -595,9 +628,7 @@ public class JavaParser {
|
||||
|| next.kind == ABSTRACT
|
||||
|| next.kind == STATIC
|
||||
|| next.kind == FINAL
|
||||
|| next.kind == STRICTFP
|
||||
|| isSealedClassSupported() && isKeyword("sealed")
|
||||
|| isSealedClassSupported() && isNonSealedModifier();
|
||||
|| next.kind == STRICTFP;
|
||||
}
|
||||
|
||||
private boolean localTypeDeclLookahead() {
|
||||
@ -1721,6 +1752,25 @@ void EqualityExpression() #EqualityExpression(>1):
|
||||
InstanceOfExpression() ( LOOKAHEAD(2) ( "==" {jjtThis.setImage("==");} | "!=" {jjtThis.setImage("!=");} ) InstanceOfExpression() )*
|
||||
}
|
||||
|
||||
void Pattern() #void:
|
||||
{}
|
||||
{
|
||||
PrimaryPattern() [ GuardedPatternCondition() #GuardedPattern(2) {checkForGuardedPatterns();} ]
|
||||
}
|
||||
|
||||
void GuardedPatternCondition() #void:
|
||||
{}
|
||||
{
|
||||
"&&" ConditionalAndExpression()
|
||||
}
|
||||
|
||||
void PrimaryPattern() #void:
|
||||
{}
|
||||
{
|
||||
TypePattern()
|
||||
| "(" Pattern() ")" { AstImplUtil.bumpParenDepth((ASTPattern) jjtree.peekNode()); }
|
||||
}
|
||||
|
||||
void TypePattern():
|
||||
{}
|
||||
{
|
||||
@ -1735,7 +1785,9 @@ void InstanceOfExpression() #InstanceOfExpression(>1):
|
||||
RelationalExpression()
|
||||
[ "instanceof"
|
||||
(
|
||||
LOOKAHEAD("final" | "@") {checkforBadInstanceOfPattern();} TypePattern()
|
||||
LOOKAHEAD("final" | "@") {checkforBadInstanceOfPattern();} PrimaryPattern()
|
||||
|
|
||||
LOOKAHEAD("(") Pattern() {checkforBadInstanceOfPattern();}
|
||||
|
|
||||
Type()
|
||||
[ {checkforBadInstanceOfPattern();} VariableDeclaratorId() #TypePattern(2) ]
|
||||
@ -2090,7 +2142,7 @@ void BlockStatement():
|
||||
}
|
||||
";"
|
||||
)
|
||||
| LOOKAHEAD({classModifierLookahead() || localTypeDeclLookahead()})
|
||||
| LOOKAHEAD({classModifierForLocalTypesLookahead() || localTypeDeclLookahead()})
|
||||
mods=Modifiers()
|
||||
LocalTypeDecl(mods)
|
||||
| LOOKAHEAD(Type() <IDENTIFIER>)
|
||||
@ -2205,13 +2257,28 @@ void SwitchLabel() :
|
||||
{
|
||||
{ inSwitchLabel = true; }
|
||||
(
|
||||
"case" ( ConditionalExpression() #Expression ) ({checkForMultipleCaseLabels();} "," ( ConditionalExpression() #Expression ) )*
|
||||
"case" CaseLabelElement(jjtThis) ( {checkForMultipleCaseLabels();} "," CaseLabelElement(jjtThis) )*
|
||||
|
|
||||
"default" {jjtThis.setDefault();}
|
||||
)
|
||||
{ inSwitchLabel = false; }
|
||||
}
|
||||
|
||||
void CaseLabelElement(ASTSwitchLabel label) #void:
|
||||
{}
|
||||
{
|
||||
"default" {label.setDefault(); checkForDefaultCaseLabel();}
|
||||
|
|
||||
LOOKAHEAD(Pattern()) Pattern() {checkForPatternMatchingInSwitch();}
|
||||
|
|
||||
ConditionalExpression() #Expression
|
||||
{
|
||||
if ("null".equals(((ASTExpression) jjtree.peekNode()).jjtGetFirstToken().getImage())) {
|
||||
checkForNullCaseLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void YieldStatement() :
|
||||
{ checkForYieldStatement(); }
|
||||
{
|
||||
|
@ -29,9 +29,10 @@ public class JavaLanguageModule extends BaseLanguageModule {
|
||||
addVersion("13", new JavaLanguageHandler(13));
|
||||
addVersion("14", new JavaLanguageHandler(14));
|
||||
addVersion("15", new JavaLanguageHandler(15));
|
||||
addVersion("15-preview", new JavaLanguageHandler(15, true));
|
||||
addDefaultVersion("16", new JavaLanguageHandler(16)); // 16 is the default
|
||||
addVersion("16", new JavaLanguageHandler(16));
|
||||
addVersion("16-preview", new JavaLanguageHandler(16, true));
|
||||
addDefaultVersion("17", new JavaLanguageHandler(17)); // 17 is the default
|
||||
addVersion("17-preview", new JavaLanguageHandler(17, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ 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.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.util.CollectionUtil;
|
||||
@ -142,7 +141,6 @@ public class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclaration {
|
||||
return it == null ? Collections.<ASTClassOrInterfaceType>emptyList() : CollectionUtil.toList(it.iterator());
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public List<ASTClassOrInterfaceType> getPermittedSubclasses() {
|
||||
ASTPermitsList permitted = getFirstChildOfType(ASTPermitsList.class);
|
||||
return permitted == null
|
||||
@ -150,13 +148,11 @@ public class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclaration {
|
||||
: CollectionUtil.toList(permitted.iterator());
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public boolean isSealed() {
|
||||
int modifiers = getModifiers();
|
||||
return (modifiers & AccessNode.SEALED) == AccessNode.SEALED;
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public boolean isNonSealed() {
|
||||
int modifiers = getModifiers();
|
||||
return (modifiers & AccessNode.NON_SEALED) == AccessNode.NON_SEALED;
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 guarded pattern (JDK17 Preview). This can be found
|
||||
* in {@link ASTSwitchLabel}s.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
* GuardedPattern ::= {@linkplain ASTPattern Pattern} "&&" {@linkplain ASTConditionalAndExpression ConditionalAndExpression}
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="https://openjdk.java.net/jeps/406">JEP 406: Pattern Matching for switch (Preview)</a>
|
||||
*/
|
||||
@Experimental
|
||||
public final class ASTGuardedPattern extends AbstractJavaNode implements ASTPattern {
|
||||
|
||||
private int parenDepth;
|
||||
|
||||
ASTGuardedPattern(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
ASTGuardedPattern(JavaParser p, int id) {
|
||||
super(p, id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
|
||||
public ASTPattern getPattern() {
|
||||
return (ASTPattern) getChild(0);
|
||||
}
|
||||
|
||||
public JavaNode getGuard() {
|
||||
return getChild(1);
|
||||
}
|
||||
|
||||
void bumpParenDepth() {
|
||||
parenDepth++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Experimental
|
||||
public int getParenthesisDepth() {
|
||||
return parenDepth;
|
||||
}
|
||||
}
|
@ -4,9 +4,11 @@
|
||||
|
||||
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 16 feature.
|
||||
* A pattern (for pattern matching constructs like {@link ASTInstanceOfExpression InstanceOfExpression}
|
||||
* or within a {@link ASTSwitchLabel}). 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
|
||||
@ -14,7 +16,8 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
* Pattern ::= {@link ASTTypePattern TypePattern}
|
||||
* Pattern ::= {@link ASTTypePattern TypePattern}
|
||||
* | {@link ASTGuardedPattern GuardedPattern}
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
@ -22,4 +25,10 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
*/
|
||||
public interface ASTPattern extends JavaNode {
|
||||
|
||||
/**
|
||||
* Returns the number of parenthesis levels around this pattern.
|
||||
* If this method returns 0, then no parentheses are present.
|
||||
*/
|
||||
@Experimental
|
||||
int getParenthesisDepth();
|
||||
}
|
||||
|
@ -6,15 +6,13 @@ 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 and Java 16 Preview feature.
|
||||
* <p>This is a Java 17 Feature.
|
||||
*
|
||||
* <p>See https://openjdk.java.net/jeps/397
|
||||
* <p>See https://openjdk.java.net/jeps/409
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
@ -22,7 +20,6 @@ import net.sourceforge.pmd.annotation.Experimental;
|
||||
* ( "," ClassOrInterfaceType )*
|
||||
* </pre>
|
||||
*/
|
||||
@Experimental
|
||||
public final class ASTPermitsList extends AbstractJavaNode implements Iterable<ASTClassOrInterfaceType> {
|
||||
|
||||
ASTPermitsList(int id) {
|
||||
|
@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
/**
|
||||
* A type pattern (JDK16). This can be found on
|
||||
* the right-hand side of an {@link ASTInstanceOfExpression InstanceOfExpression}.
|
||||
@ -21,6 +23,7 @@ import java.util.List;
|
||||
public final class ASTTypePattern extends AbstractJavaAnnotatableNode implements ASTPattern {
|
||||
|
||||
private boolean isFinal;
|
||||
private int parenDepth;
|
||||
|
||||
ASTTypePattern(int id) {
|
||||
super(id);
|
||||
@ -60,4 +63,14 @@ public final class ASTTypePattern extends AbstractJavaAnnotatableNode implements
|
||||
boolean isFinal() {
|
||||
return isFinal;
|
||||
}
|
||||
|
||||
void bumpParenDepth() {
|
||||
parenDepth++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Experimental
|
||||
public int getParenthesisDepth() {
|
||||
return parenDepth;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
/**
|
||||
* KEEP PRIVATE
|
||||
*/
|
||||
final class AstImplUtil {
|
||||
|
||||
private AstImplUtil() {
|
||||
}
|
||||
|
||||
static void bumpParenDepth(ASTPattern pattern) {
|
||||
assert pattern instanceof ASTTypePattern || pattern instanceof ASTGuardedPattern
|
||||
: pattern.getClass() + " doesn't have parenDepth attribute!";
|
||||
|
||||
if (pattern instanceof ASTTypePattern) {
|
||||
((ASTTypePattern) pattern).bumpParenDepth();
|
||||
} else if (pattern instanceof ASTGuardedPattern) {
|
||||
((ASTGuardedPattern) pattern).bumpParenDepth();
|
||||
}
|
||||
}
|
||||
}
|
@ -938,9 +938,15 @@ public class JavaParserDecoratedVisitor implements JavaParserVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Experimental
|
||||
public Object visit(ASTPermitsList node, Object data) {
|
||||
visitor.visit(node, data);
|
||||
return visit((JavaNode) node, data);
|
||||
}
|
||||
|
||||
@Experimental
|
||||
@Override
|
||||
public Object visit(ASTGuardedPattern node, Object data) {
|
||||
visitor.visit(node, data);
|
||||
return visit((JavaNode) node, data);
|
||||
}
|
||||
}
|
||||
|
@ -659,8 +659,13 @@ public class JavaParserVisitorAdapter implements JavaParserVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Experimental
|
||||
public Object visit(ASTPermitsList node, Object data) {
|
||||
return visit((JavaNode) node, data);
|
||||
}
|
||||
|
||||
@Experimental
|
||||
@Override
|
||||
public Object visit(ASTGuardedPattern node, Object data) {
|
||||
return visit((JavaNode) node, data);
|
||||
}
|
||||
}
|
||||
|
@ -791,8 +791,13 @@ public class JavaParserVisitorDecorator implements JavaParserControllessVisitor
|
||||
}
|
||||
|
||||
@Override
|
||||
@Experimental
|
||||
public Object visit(ASTPermitsList node, Object data) {
|
||||
return visitor.visit(node, data);
|
||||
}
|
||||
|
||||
@Experimental
|
||||
@Override
|
||||
public Object visit(ASTGuardedPattern node, Object data) {
|
||||
return visitor.visit(node, data);
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTForUpdate;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTGuardedPattern;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
|
||||
@ -868,8 +869,13 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse
|
||||
}
|
||||
|
||||
@Override
|
||||
@Experimental
|
||||
public Object visit(ASTPermitsList node, Object data) {
|
||||
return visit((JavaNode) node, data);
|
||||
}
|
||||
|
||||
@Experimental
|
||||
@Override
|
||||
public Object visit(ASTGuardedPattern node, Object data) {
|
||||
return visit((JavaNode) node, data);
|
||||
}
|
||||
}
|
||||
|
@ -42,24 +42,20 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest {
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "12",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("12"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "12-preview",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("12-preview"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "13",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("13"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "13-preview",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("13-preview"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "14",
|
||||
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"), },
|
||||
{ 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"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "17",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("17"), },
|
||||
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "17-preview",
|
||||
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("17-preview"), },
|
||||
|
||||
// this one won't be found: case sensitive!
|
||||
{ "JAVA", "JAVA", "1.7", null, },
|
||||
|
@ -11,35 +11,51 @@ import static org.junit.Assert.fail;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.contrib.java.lang.system.StandardErrorStreamLog;
|
||||
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
|
||||
import org.junit.contrib.java.lang.system.SystemErrRule;
|
||||
import org.junit.contrib.java.lang.system.SystemOutRule;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
|
||||
|
||||
public class PMDCoverageTest {
|
||||
|
||||
@Rule
|
||||
public StandardOutputStreamLog output = new StandardOutputStreamLog();
|
||||
public SystemOutRule output = new SystemOutRule().muteForSuccessfulTests().enableLog();
|
||||
|
||||
@Rule
|
||||
public StandardErrorStreamLog errorStream = new StandardErrorStreamLog();
|
||||
public SystemErrRule errorStream = new SystemErrRule().muteForSuccessfulTests().enableLog();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
/**
|
||||
* Test some of the PMD command line options
|
||||
*/
|
||||
@Test
|
||||
public void testPmdOptions() {
|
||||
runPmd("-d src/main/java/net/sourceforge/pmd/lang/java/rule/design -f text -R rulesets/internal/all-java.xml -language java -stress -benchmark");
|
||||
runPmd("-d src/main/java/net/sourceforge/pmd/lang/java/rule/design -f text -R rulesets/internal/all-java.xml -stress -benchmark");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void runAllJavaPmdOnSourceTree() {
|
||||
runPmd("-d src/main/java -f text -R rulesets/internal/all-java.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runAllJavaPmdOnTestResourcesWithLatestJavaVersion() {
|
||||
List<LanguageVersion> versions = LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersions();
|
||||
LanguageVersion latest = versions.get(versions.size() - 1);
|
||||
|
||||
runPmd("-d src/test/resources -f text -R rulesets/internal/all-java.xml -language java -version " + latest.getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,8 +65,10 @@ public class PMDCoverageTest {
|
||||
*/
|
||||
private void runPmd(String commandLine) {
|
||||
String[] args = commandLine.split("\\s");
|
||||
String report = "missing report";
|
||||
|
||||
try {
|
||||
|
||||
File f = folder.newFile();
|
||||
args = ArrayUtils.addAll(
|
||||
args,
|
||||
@ -60,27 +78,26 @@ public class PMDCoverageTest {
|
||||
String.valueOf(Runtime.getRuntime().availableProcessors())
|
||||
);
|
||||
|
||||
System.err.println("Running PMD with: " + Arrays.toString(args));
|
||||
PMD.runPmd(args);
|
||||
report = FileUtils.readFileToString(f, StandardCharsets.UTF_8);
|
||||
|
||||
assertEquals("Nothing should be output to stdout", 0, output.getLog().length());
|
||||
|
||||
|
||||
assertEquals("No exceptions expected", 0, StringUtils.countMatches(errorStream.getLog(), "Exception applying rule"));
|
||||
assertFalse("Wrong configuration? Ruleset not found", errorStream.getLog().contains("Ruleset not found"));
|
||||
assertEquals("No usage of deprecated XPath attributes expected", 0, StringUtils.countMatches(errorStream.getLog(), "Use of deprecated attribute"));
|
||||
|
||||
String report = FileUtils.readFileToString(f, StandardCharsets.UTF_8);
|
||||
assertEquals("No processing errors expected", 0, StringUtils.countMatches(report, "Error while processing"));
|
||||
|
||||
// we might have explicit examples of parsing errors, so these are maybe false positives
|
||||
assertEquals("No parsing error expected", 0, StringUtils.countMatches(report, "Error while parsing"));
|
||||
} catch (IOException ioe) {
|
||||
fail("Problem creating temporary file: " + ioe.getLocalizedMessage());
|
||||
} catch (AssertionError ae) {
|
||||
System.out.println("\nReport:\n");
|
||||
System.out.println(report);
|
||||
throw ae;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runAllJavaPmdOnSourceTree() {
|
||||
runPmd("-d src/main/java -f text -R rulesets/internal/all-java.xml -language java");
|
||||
}
|
||||
}
|
||||
|
@ -1,134 +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.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");
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ 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() {
|
||||
@ -31,7 +30,6 @@ public class Java15TreeDumpTest extends BaseTreeDumpTest {
|
||||
@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)
|
||||
@ -47,6 +45,5 @@ public class Java15TreeDumpTest extends BaseTreeDumpTest {
|
||||
@Test
|
||||
public void sealedAndNonSealedIdentifiers() {
|
||||
doTest("NonSealedIdentifier");
|
||||
java15p.parseResource("NonSealedIdentifier.java"); // make sure we can parse it with preview as well
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ 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/");
|
||||
.withResourceContext(Java16TreeDumpTest.class, "jdkversiontests/java16/");
|
||||
private final JavaParsingHelper java16p = java16.withDefaultVersion("16-preview");
|
||||
private final JavaParsingHelper java15 = java16.withDefaultVersion("15");
|
||||
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
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 Java17PreviewTreeDumpTest extends BaseTreeDumpTest {
|
||||
private final JavaParsingHelper java17p =
|
||||
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17-preview")
|
||||
.withResourceContext(Java17PreviewTreeDumpTest.class, "jdkversiontests/java17p/");
|
||||
private final JavaParsingHelper java17 = java17p.withDefaultVersion("17");
|
||||
|
||||
public Java17PreviewTreeDumpTest() {
|
||||
super(new RelevantAttributePrinter(), ".java");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseParsingHelper<?, ?> getParser() {
|
||||
return java17p;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternMatchingForSwitchBeforeJava17Preview() {
|
||||
ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
java17.parseResource("PatternsInSwitchLabels.java");
|
||||
}
|
||||
});
|
||||
Assert.assertTrue("Unexpected message: " + thrown.getMessage(),
|
||||
thrown.getMessage().contains("Pattern Matching in Switch is only supported with JDK 17 Preview."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternMatchingForSwitch() {
|
||||
doTest("PatternsInSwitchLabels");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enhancedTypeCheckingSwitch() {
|
||||
doTest("EnhancedTypeCheckingSwitch");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scopeOfPatternVariableDeclarations() {
|
||||
doTest("ScopeOfPatternVariableDeclarations");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dealingWithNullBeforeJava17Preview() {
|
||||
ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
java17.parseResource("DealingWithNull.java");
|
||||
}
|
||||
});
|
||||
Assert.assertTrue("Unexpected message: " + thrown.getMessage(),
|
||||
thrown.getMessage().contains("Null case labels in switch are only supported with JDK 17 Preview."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dealingWithNull() {
|
||||
doTest("DealingWithNull");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void guardedAndParenthesizedPatternsBeforeJava17Preview() {
|
||||
ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
java17.parseResource("GuardedAndParenthesizedPatterns.java");
|
||||
}
|
||||
});
|
||||
Assert.assertTrue("Unexpected message: " + thrown.getMessage(),
|
||||
thrown.getMessage().contains("Guarded patterns are only supported with JDK 17 Preview."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void guardedAndParenthesizedPatterns() {
|
||||
doTest("GuardedAndParenthesizedPatterns");
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
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 Java17TreeDumpTest extends BaseTreeDumpTest {
|
||||
private final JavaParsingHelper java17 =
|
||||
JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17")
|
||||
.withResourceContext(Java17TreeDumpTest.class, "jdkversiontests/java17/");
|
||||
private final JavaParsingHelper java17p = java17.withDefaultVersion("17-preview");
|
||||
private final JavaParsingHelper java16 = java17.withDefaultVersion("16");
|
||||
|
||||
public Java17TreeDumpTest() {
|
||||
super(new RelevantAttributePrinter(), ".java");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseParsingHelper<?, ?> getParser() {
|
||||
return java17;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sealedClassBeforeJava17() {
|
||||
ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
java16.parseResource("geometry/Shape.java");
|
||||
}
|
||||
});
|
||||
Assert.assertTrue("Unexpected message: " + thrown.getMessage(),
|
||||
thrown.getMessage().contains("Sealed Classes are only supported with JDK 16 Preview and JDK >= 17."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sealedClass() {
|
||||
doTest("geometry/Shape");
|
||||
java17p.parseResource("geometry/Shape.java"); // make sure we can parse it with preview as well
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSealedClass() {
|
||||
doTest("geometry/Square");
|
||||
java17p.parseResource("geometry/Square.java"); // make sure we can parse it with preview as well
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sealedInterfaceBeforeJava17() {
|
||||
ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
java16.parseResource("expression/Expr.java");
|
||||
}
|
||||
});
|
||||
Assert.assertTrue("Unexpected message: " + thrown.getMessage(),
|
||||
thrown.getMessage().contains("Sealed Classes are only supported with JDK 16 Preview and JDK >= 17."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sealedInterface() {
|
||||
doTest("expression/Expr");
|
||||
java17p.parseResource("expression/Expr.java"); // make sure we can parse it with preview as well
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localVars() {
|
||||
doTest("LocalVars");
|
||||
}
|
||||
}
|
@ -186,7 +186,7 @@ public class ParserCornersTest {
|
||||
public void testGitHubBug2767() {
|
||||
// PMD fails to parse an initializer block.
|
||||
// PMD 6.26.0 parses this code just fine.
|
||||
java.withDefaultVersion("15-preview")
|
||||
java.withDefaultVersion("16")
|
||||
.parse("class Foo {\n"
|
||||
+ " {final int I;}\n"
|
||||
+ "}\n");
|
||||
|
@ -12,16 +12,16 @@ import java.io.IOException
|
||||
|
||||
class ASTPatternTest : ParserTestSpec({
|
||||
|
||||
parserTest("Test patterns only available on JDK 15 (preview) and JDK16 and JDK16 (preview)",
|
||||
javaVersions = JavaVersion.values().asList().minus(J15__PREVIEW).minus(J16).minus(J16__PREVIEW)) {
|
||||
parserTest("Test patterns only available on JDK16 and JDK16 (preview) and JDK17 and JDK 17 (preview)",
|
||||
javaVersions = JavaVersion.values().asList().minus(J16).minus(J16__PREVIEW).minus(J17).minus(J17__PREVIEW)) {
|
||||
|
||||
expectParseException("Pattern Matching for instanceof is only supported with Java 15 Preview and Java >= 16") {
|
||||
expectParseException("Pattern Matching for instanceof is only supported with JDK >= 16") {
|
||||
parseAstExpression("obj instanceof Class c")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parserTest("Test simple patterns", javaVersions = listOf(J15__PREVIEW, J16)) {
|
||||
parserTest("Test simple patterns", javaVersions = listOf(J16, J17)) {
|
||||
|
||||
importedTypes += IOException::class.java
|
||||
|
||||
|
@ -21,8 +21,9 @@ enum class JavaVersion : Comparable<JavaVersion> {
|
||||
J12,
|
||||
J13,
|
||||
J14,
|
||||
J15, J15__PREVIEW,
|
||||
J16, J16__PREVIEW;
|
||||
J15,
|
||||
J16, J16__PREVIEW,
|
||||
J17, J17__PREVIEW;
|
||||
|
||||
/** Name suitable for use with e.g. [JavaParsingHelper.parse] */
|
||||
val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').toLowerCase()
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
public class LocalInterfacesAndEnums {
|
||||
|
||||
{
|
||||
class MyLocalClass {}
|
||||
|
||||
// static local classes are not allowed (neither Java15 nor Java15 Preview)
|
||||
//static class MyLocalStaticClass {}
|
||||
|
||||
interface MyLocalInterface {}
|
||||
|
||||
enum MyLocalEnum { A }
|
||||
|
||||
// not supported anymore with Java16
|
||||
//@interface MyLocalAnnotation {}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
+- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = true]
|
||||
+- TypeDeclaration[]
|
||||
+- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "LocalInterfacesAndEnums", @Default = false, @Final = false, @Image = "LocalInterfacesAndEnums", @Interface = false, @Local = false, @Modifiers = 1, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = true, @Sealed = false, @SimpleName = "LocalInterfacesAndEnums", @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.INITIALIZER]
|
||||
+- Initializer[@Static = false]
|
||||
+- Block[@containsComment = true]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
| +- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "LocalInterfacesAndEnums$1MyLocalClass", @Default = false, @Final = false, @Image = "MyLocalClass", @Interface = false, @Local = true, @Modifiers = 0, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = false, @Sealed = false, @SimpleName = "MyLocalClass", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.CLASS, @Volatile = false]
|
||||
| +- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
| +- ClassOrInterfaceDeclaration[@Abstract = false, @BinaryName = "LocalInterfacesAndEnums$1MyLocalInterface", @Default = false, @Final = false, @Image = "MyLocalInterface", @Interface = true, @Local = true, @Modifiers = 0, @Native = false, @Nested = false, @NonSealed = false, @PackagePrivate = false, @Private = false, @Protected = false, @Public = false, @Sealed = false, @SimpleName = "MyLocalInterface", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.INTERFACE, @Volatile = false]
|
||||
| +- ClassOrInterfaceBody[@AnonymousInnerClass = false, @EnumChild = false]
|
||||
+- BlockStatement[@Allocation = false]
|
||||
+- EnumDeclaration[@Abstract = false, @BinaryName = "LocalInterfacesAndEnums$MyLocalEnum", @Default = false, @Final = false, @Image = "MyLocalEnum", @Local = true, @Modifiers = 0, @Native = false, @Nested = false, @PackagePrivate = true, @Private = false, @Protected = false, @Public = false, @SimpleName = "MyLocalEnum", @Static = false, @Strictfp = false, @Synchronized = false, @Transient = false, @TypeKind = TypeKind.ENUM, @Volatile = false]
|
||||
+- EnumBody[]
|
||||
+- EnumConstant[@AnonymousClass = false, @Image = "A"]
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.java.net/jeps/384">JEP 384: Records (Second Preview)</a>
|
||||
*/
|
||||
public class LocalRecords {
|
||||
public interface Merchant {}
|
||||
public static double computeSales(Merchant merchant, int month) {
|
||||
return month;
|
||||
}
|
||||
List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
|
||||
// Local record
|
||||
record MerchantSales(Merchant merchant, double sales) {}
|
||||
|
||||
return merchants.stream()
|
||||
.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
|
||||
.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
|
||||
.map(MerchantSales::merchant)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
void methodWithLocalRecordAndModifiers() {
|
||||
final record MyRecord1(String a) {}
|
||||
final static record MyRecord2(String a) {}
|
||||
@Deprecated record MyRecord3(String a) {}
|
||||
final @Deprecated static record MyRecord4(String a) {}
|
||||
}
|
||||
|
||||
void statementThatStartsWithRecordAsRegularIdent() {
|
||||
// https://github.com/pmd/pmd/issues/3145
|
||||
final Map<String, String> record = new HashMap<>();
|
||||
record.put("key", "value");
|
||||
}
|
||||
|
||||
void methodWithLocalClass() {
|
||||
class MyLocalClass {}
|
||||
}
|
||||
|
||||
void methodWithLocalVarsNamedSealed() {
|
||||
int result = 0;
|
||||
int non = 1;
|
||||
int sealed = 2;
|
||||
result = non-sealed;
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,34 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @see <a href="https://openjdk.java.net/jeps/375">JEP 375: Pattern Matching for instanceof (Second Preview)</a>
|
||||
*/
|
||||
public class PatternMatchingInstanceof {
|
||||
private String s = "other string";
|
||||
|
||||
public void test() {
|
||||
Object obj = "abc";
|
||||
//obj = 1;
|
||||
if (obj instanceof String s) {
|
||||
System.out.println("a) obj == s: " + (obj == s)); // true
|
||||
} else {
|
||||
System.out.println("b) obj == s: " + (obj == s)); // false
|
||||
}
|
||||
|
||||
if (!(obj instanceof String s)) {
|
||||
System.out.println("c) obj == s: " + (obj == s)); // false
|
||||
} else {
|
||||
System.out.println("d) obj == s: " + (obj == s)); // true
|
||||
}
|
||||
|
||||
if (obj instanceof String s && s.length() > 2) {
|
||||
System.out.println("e) obj == s: " + (obj == s)); // true
|
||||
}
|
||||
if (obj instanceof String s || s.length() > 5) {
|
||||
System.out.println("f) obj == s: " + (obj == s)); // false
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new PatternMatchingInstanceof().test();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user