diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 6593c7f7a1..ed713afee8 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -2206,10 +2206,38 @@ void LambdaExpression() : { checkForBadLambdaUsage(); } { VariableDeclaratorId() "->" ( Expression() | Block() ) -| LOOKAHEAD(3) FormalParameters() "->" ( Expression() | Block() ) +| LOOKAHEAD(3) LambdaParameters() "->" ( Expression() | Block() ) | LOOKAHEAD(3) "(" VariableDeclaratorId() ( "," VariableDeclaratorId() )* ")" "->" ( Expression() | Block() ) } +void LambdaParameters() #FormalParameters : +{} +{ + "(" [ LambdaParameterList() ] ")" +} + +void LambdaParameterList() #void : +{} +{ + LambdaParameter() ( "," LambdaParameter() )* +} + +void LambdaParameter() #FormalParameter : +{} +{ + ( "final" {jjtThis.setFinal(true);} | Annotation() )* + LambdaParameterType() + [ "..." {checkForBadVariableArgumentsUsage();} {jjtThis.setVarargs();} ] + VariableDeclaratorId() +} + +void LambdaParameterType() #void : +{} +{ + LOOKAHEAD( { jdkVersion >= 11 && isKeyword("var") } ) + | Type() +} + void PrimarySuffix() : {Token t;} { LOOKAHEAD(2) "." "this" diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java index aa1503ca28..f5fb17220a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java @@ -24,7 +24,8 @@ public class JavaLanguageModule extends BaseLanguageModule { addVersion("1.7", new JavaLanguageHandler(7), false); addVersion("1.8", new JavaLanguageHandler(8), false); addVersion("9", new JavaLanguageHandler(9), false); - addVersion("10", new JavaLanguageHandler(10), true); + addVersion("10", new JavaLanguageHandler(10), false); + addVersion("11", new JavaLanguageHandler(11), true); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java index 681b4268bd..2657d71436 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java @@ -14,6 +14,7 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin * production of {@link ASTMethodDeclarator} to represent a * method's formal parameter. Also used in the {@link ASTCatchStatement} * production to represent the declared exception variable. + * Also used in LambdaExpressions for the LambdaParameters. *
  *      ( "final" | Annotation )* Type ( "|" Type )* [ "..." ] VariableDeclaratorId
  * 
@@ -93,7 +94,9 @@ public class ASTFormalParameter extends AbstractJavaAccessTypeNode implements Di */ @Override public boolean isArray() { - return isVarargs() || getTypeNode().isArray() || getVariableDeclaratorId().isArray(); + return isVarargs() + || getTypeNode() != null && getTypeNode().isArray() + || getVariableDeclaratorId().isArray(); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java index 8322be0b26..71da3d4d44 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java @@ -206,7 +206,7 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements Dim */ public boolean isTypeInferred() { // TODO think about supporting var for lambda parameters - return isLambdaParamWithNoType() || isLocalVariableTypeInferred(); + return isLambdaParamWithNoType() || isLocalVariableTypeInferred() || isLambdaTypeInferred(); } @@ -222,6 +222,11 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements Dim return false; } + private boolean isLambdaTypeInferred() { + return getNthParent(3) instanceof ASTLambdaExpression + && jjtGetParent().getFirstChildOfType(ASTType.class) == null; + } + /** * Returns the first child of the node returned by {@link #getTypeNode()}. * The image of that node can usually be interpreted as the image of the diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 012f8798c0..3c3c71ca03 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -49,6 +49,7 @@ public abstract class Foo { @@ -77,6 +78,7 @@ public class Outer { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java index f8c30b8f57..f2972b1407 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java @@ -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 10 !", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("10"), languageVersion); + assertEquals("LanguageVersion must be Java 11 !", + LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), 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("10"), + assertEquals("Default Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), languageVersionDiscoverer .getDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME))); configuration diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 984a99b706..1adfc7c185 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -34,6 +34,12 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.7"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.8", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.8"), }, + { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "9", + LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("9"), }, + { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "10", + LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("10"), }, + { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "11", + LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), }, // this one won't be found: case sensitive! { "JAVA", "JAVA", "1.7", null, }, }); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java new file mode 100644 index 0000000000..623c3088b7 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java @@ -0,0 +1,89 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ParserTstUtil; + +public class Java11Test { + private static String loadSource(String name) { + try { + return IOUtils.toString(Java10Test.class.getResourceAsStream("jdkversiontests/java11/" + name), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testLocalVariableSyntaxForLambdaParametersWithJava10() { + ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("10", + loadSource("LocalVariableSyntaxForLambdaParameters.java")); + + List lambdas = compilationUnit.findDescendantsOfType(ASTLambdaExpression.class); + Assert.assertEquals(4, lambdas.size()); + + // (var x) -> String.valueOf(x); + List formalParameters = lambdas.get(0).findDescendantsOfType(ASTFormalParameter.class); + Assert.assertEquals(1, formalParameters.size()); + ASTType type = formalParameters.get(0).getFirstChildOfType(ASTType.class); + assertEquals("var", type.getTypeImage()); + assertEquals(1, type.jjtGetNumChildren()); + ASTReferenceType referenceType = type.getFirstChildOfType(ASTReferenceType.class); + assertNotNull(referenceType); + assertEquals(1, referenceType.jjtGetNumChildren()); + ASTClassOrInterfaceType classType = referenceType.getFirstChildOfType(ASTClassOrInterfaceType.class); + assertNotNull(classType); + assertEquals("var", classType.getImage()); + + // (var x, var y) -> x + y; + formalParameters = lambdas.get(1).findDescendantsOfType(ASTFormalParameter.class); + Assert.assertEquals(2, formalParameters.size()); + type = formalParameters.get(0).getFirstChildOfType(ASTType.class); + assertEquals("var", type.getTypeImage()); + assertEquals(1, type.jjtGetNumChildren()); + referenceType = type.getFirstChildOfType(ASTReferenceType.class); + assertNotNull(referenceType); + assertEquals(1, referenceType.jjtGetNumChildren()); + classType = referenceType.getFirstChildOfType(ASTClassOrInterfaceType.class); + assertNotNull(classType); + assertEquals("var", classType.getImage()); + type = formalParameters.get(1).getFirstChildOfType(ASTType.class); + assertEquals("var", type.getTypeImage()); + assertEquals(1, type.jjtGetNumChildren()); + + // (@Nonnull var x) -> String.valueOf(x); + formalParameters = lambdas.get(2).findDescendantsOfType(ASTFormalParameter.class); + Assert.assertEquals(1, formalParameters.size()); + Node firstChild = formalParameters.get(0).jjtGetChild(0); + Assert.assertTrue(firstChild instanceof ASTAnnotation); + } + + @Test + public void testLocalVariableSyntaxForLambdaParametersWithJava11() { + ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("11", + loadSource("LocalVariableSyntaxForLambdaParameters.java")); + + List lambdas = compilationUnit.findDescendantsOfType(ASTLambdaExpression.class); + Assert.assertEquals(4, lambdas.size()); + + // (var x) -> String.valueOf(x); + List formalParameters = lambdas.get(0).findDescendantsOfType(ASTFormalParameter.class); + Assert.assertEquals(1, formalParameters.size()); + assertNull(formalParameters.get(0).getTypeNode()); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java11/LocalVariableSyntaxForLambdaParameters.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java11/LocalVariableSyntaxForLambdaParameters.java new file mode 100644 index 0000000000..7daa4efd82 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java11/LocalVariableSyntaxForLambdaParameters.java @@ -0,0 +1,28 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class LocalVariableSyntaxForLambdaParameters { + + @Target({ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Nonnull { } + + public void createLambdas() { + //var lambda = (var x, var y) -> x.process(y); + + Function lambda1 = (var x) -> String.valueOf(x); + BiFunction lambda2 = (var x, var y) -> x + y; + } + + public void createAnnotatedLambdaParameters() { + //@Nonnull var x = new Foo(); + //(@Nonnull var x, @Nullable var y) -> x.process(y) + + Function lambda1 = (@Nonnull var x) -> String.valueOf(x); + BiFunction lambda2 = (@Nonnull var x, @Nonnull var y) -> x + y; + } +} \ No newline at end of file diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml index 091b027d96..9bd3fd0345 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml @@ -4,9 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd"> - + inner class has private constructor 1 + java 10 + - + inner class has public constructor 0 + java 10 + - + outer class has public constructor 1 + java 10 + - + final inner class 0 + java 10 + - + interface inner class has private constructor 1 + java 10 + - + there's a check for int declaration - not sure right now why 1 + java 10 + #1452 ArrayIndexOutOfBoundsException with Annotations for AccessorClassGenerationRule 0 + java 10 + - + #291 - Private constructor called from anonymous class 1 + java 10 + - + Array initializer is not a class body 0 + java 10 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml index 17b12632f4..bf1b8fd638 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml @@ -4,9 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd"> - + inner class accesses private field from outer class 1 8 + java 10 + - + inner class accesses private field from outer class unqualified 1 8 + java 10 + - + outer class accesses private field from inner class 1 9 + java 10 - - + + + non private fields are ok 0 + java 10 + - + inner class accesses private method of outer class, unqualified 1 4 + java 10 + - + inner class accesses private method of outer class, qualified 1 4 + java 10 + - + outer class accesses private method of inner class 1 8 + java 10 - + inner class accesses non-private methods of outer class 0 + java 10 + - + #274 - Method inside static inner class incorrectly reported as generating accessor methods 0 + java 10 diff --git a/pom.xml b/pom.xml index d754cabb39..feef580d3f 100644 --- a/pom.xml +++ b/pom.xml @@ -764,7 +764,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code org.ow2.asm asm - 6.1.1 + 6.2 net.sourceforge.pmd