diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index be347962b0..7add376142 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -1,4 +1,9 @@ /** + * Added support for explicit receiver parameters. + * Bug #1455 + * + * Andreas Dangel 01/2016 + *==================================================================== * Added capability for Tracking Tokens. * * Amit Kumar Prasad 10/2015 @@ -248,6 +253,11 @@ public class JavaParser { throwParseException("Cannot use type annotations when running in JDK inferior to 1.8 mode!"); } } + private void checkforBadExplicitReceiverParameter() { + if (jdkVersion < 8) { + throwParseException("Cannot use explicit receiver parameters when running in JDK inferior to 1.8 mode!"); + } + } // 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 @@ -1390,14 +1400,20 @@ void VariableDeclarator() : } void VariableDeclaratorId() : -{Token t;} { - t= + Token t; + String image; +} +{ + (LOOKAHEAD(2) t= "." { checkforBadExplicitReceiverParameter(); jjtThis.setExplicitReceiverParameter(); image=t.image + ".this"; } + | t= { checkforBadExplicitReceiverParameter(); jjtThis.setExplicitReceiverParameter(); image = t.image;} + | t= { image = t.image; } + ) ( "[" "]" { jjtThis.bumpArrayDepth(); })* { - checkForBadAssertUsage(t.image, "a variable name"); - checkForBadEnumUsage(t.image, "a variable name"); - jjtThis.setImage( t.image ); + checkForBadAssertUsage(image, "a variable name"); + checkForBadEnumUsage(image, "a variable name"); + jjtThis.setImage( image ); } } @@ -1778,7 +1794,7 @@ void PrimaryPrefix() : {Token t;} { Literal() -| "this" {jjtThis.setUsesThisModifier();} +| LOOKAHEAD(2) "this" {jjtThis.setUsesThisModifier();} | "super" {jjtThis.setUsesSuperModifier();} | LOOKAHEAD( "(" ")" "->" ) LambdaExpression() | LOOKAHEAD( "->" ) LambdaExpression() 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 46702f4d6b..4e4336469b 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 @@ -19,7 +19,11 @@ public class ASTFormalParameter extends AbstractJavaAccessNode implements Dimens public boolean isVarargs() { return isVarargs; } - + + public boolean isExplicitReceiverParameter() { + return getDecl().isExplicitReceiverParameter(); + } + public ASTFormalParameter(int id) { super(id); } 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 37499ebcc1..9ff9c5acdb 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 @@ -31,6 +31,7 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode { private int arrayDepth; private VariableNameDeclaration nameDeclaration; + private boolean explicitReceiverParameter = false; public VariableNameDeclaration getNameDeclaration() { return nameDeclaration; @@ -60,6 +61,13 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode { return jjtGetParent().jjtGetParent() instanceof ASTTryStatement; } + public void setExplicitReceiverParameter() { + explicitReceiverParameter = true; + } + public boolean isExplicitReceiverParameter() { + return explicitReceiverParameter; + } + public Node getTypeNameNode() { if (jjtGetParent() instanceof ASTFormalParameter) { return findTypeNameNode(jjtGetParent()); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java index 49db59f085..bd719e7c09 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java @@ -13,6 +13,7 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.lang.java.ParserTst; import org.apache.commons.io.IOUtils; +import org.junit.Assert; import org.junit.Test; public class ParserCornersTest extends ParserTst { @@ -54,15 +55,25 @@ public class ParserCornersTest extends ParserTst { } @Test - public void testParsersCases() { + public void testParsersCases15() { String test15 = readAsString("/net/sourceforge/pmd/ast/ParserCornerCases.java"); parseJava15(test15); + } + @Test + public void testParsersCases17() { String test17 = readAsString("/net/sourceforge/pmd/ast/ParserCornerCases17.java"); parseJava17(test17); + } + @Test + public void testParsersCases18() throws Exception { String test18 = readAsString("/net/sourceforge/pmd/ast/ParserCornerCases18.java"); - parseJava18(test18); + ASTCompilationUnit cu = parseJava18(test18); + + Assert.assertEquals(13, cu.findChildNodesWithXPath("//FormalParameter").size()); + Assert.assertEquals(4, cu.findChildNodesWithXPath("//FormalParameter[@ExplicitReceiverParameter='true']").size()); + Assert.assertEquals(9, cu.findChildNodesWithXPath("//FormalParameter[@ExplicitReceiverParameter='false']").size()); } /** diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/ast/ParserCornerCases18.java b/pmd-java/src/test/resources/net/sourceforge/pmd/ast/ParserCornerCases18.java index 5078cba600..c500f05fbf 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/ast/ParserCornerCases18.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/ast/ParserCornerCases18.java @@ -153,6 +153,22 @@ public class ParserCornerCases18 { }; } } + + /** + * Explicit receiver Parameters + * see: http://blog.joda.org/2015/12/explicit-receiver-parameters.html + * and: https://sourceforge.net/p/pmd/bugs/1455/ + */ + public void methodWithReceiverParameter(ParserCornerCases18 this) { } + public void methodWithReceiverAndOtherParameters(ParserCornerCases18 this, String other) { } + public void methodWithReceiverParameterWithAnnotation(@AnnotatedUsage ParserCornerCases18 this, String other) { } + + @Target(ElementType.TYPE_USE) + public @interface AnnotatedUsage {} + + class Inner { + Inner(ParserCornerCases18 ParserCornerCases18.this) {} + } } interface DefaultIterator { diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md index 2aa752d45f..7fe6f90e9f 100644 --- a/src/site/markdown/overview/changelog.md +++ b/src/site/markdown/overview/changelog.md @@ -29,6 +29,8 @@ * [#1449](https://sourceforge.net/p/pmd/bugs/1449/): false positive when casting a variable to short * java-design/AccessorClassGeneration: * [#1452](https://sourceforge.net/p/pmd/bugs/1452/): ArrayIndexOutOfBoundsException with Annotations for AccessorClassGenerationRule +* General + * [#1455](https://sourceforge.net/p/pmd/bugs/1455/): PMD doesn't handle Java 8 explicit receiver parameters **API Changes:**