From 3e36c1621b2200c8022c0a1433d99c502d3fbf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 24 Mar 2019 19:27:59 +0100 Subject: [PATCH] Remove LateInitNode --- pmd-java/etc/grammar/Java.jjt | 88 ++++++--------- .../pmd/lang/java/ast/ASTAmbiguousName.java | 101 ++++++++++-------- .../pmd/lang/java/ast/ASTMethodReference.java | 9 +- .../pmd/lang/java/ast/JJTJavaParserState.java | 63 ++++++----- .../lang/java/ast/ASTMethodReferenceTest.kt | 2 +- 5 files changed, 131 insertions(+), 132 deletions(-) diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 762d3ff425..592125cf4a 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -464,22 +464,6 @@ public class JavaParser { token_source.setSuppressMarker(marker); } - /** - * Takes the two nodes at the top of the stack and inject the next-to-last as the first - * child of the last. Basically it transforms eg [A][B] into [[A]B]. Can be used to e.g. - * build a left-recursive expression or type subtree from an iterative parsing. - */ - private void wrapLeft() { - AbstractJavaNode lastSegment = (AbstractJavaNode) jjtree.popNode(); - Node previousSegment = jjtree.popNode(); - - lastSegment.insertChild((AbstractJavaNode) previousSegment, 0); - if (lastSegment instanceof AbstractLateInitNode) { - ((AbstractLateInitNode) lastSegment).onInjectFinished(); - } - jjtree.pushNode(lastSegment); - } - private void setLastTokenImage(JavaNode node) { node.setImage(getToken(0).getImage()); } @@ -1998,7 +1982,6 @@ void AnnotationList() #void: void TypeAnnotationList() #void: {} { - // don't repeat the check (TypeAnnotation())* } @@ -2110,8 +2093,8 @@ void ClassOrInterfaceType() #void: } /* - Now if there are other segments, either the previous type specified type arguments, - or the next has an annotation. + Now if there are other segments, either the previous segment specified type + arguments, or the next has an annotation. Each of the following segments is its own ClassOrInterfaceType which encloses the previous one. The resulting structure appears left-recursive, but the parser just @@ -2123,11 +2106,9 @@ void ClassOrInterfaceType() #void: "." (TypeAnnotation())* { jjtThis.setImage(getToken(0).getImage()); } - {jjtree.extendLeft(1);} + {jjtree.extendLeft(1);} // We'll enclose the previous segment [ LOOKAHEAD( "<" ) TypeArguments() ] ) #ClassOrInterfaceType - // inject the previous segment into the last one -// { wrapLeft(); } )* { Node top = jjtree.peekNode(); @@ -2437,24 +2418,6 @@ void PrimaryExpression() #void : PrimaryPrefix() ( LOOKAHEAD(2) PrimarySuffix() )* {forceExprContext();} } -/** - * A selector like a - */ -void MemberSelector() #void : -{} -{ - // if there are type arguments, this is a method call - LOOKAHEAD(2) ("." {jjtree.extendLeft();} TypeArguments() {setLastTokenImage (jjtThis) ;} ArgumentsList()) #MethodCall // { wrapLeft (); } -| LOOKAHEAD(3) ("." {jjtree.extendLeft();} {setLastTokenImage(jjtThis);} ArgumentsList()) #MethodCall // { wrapLeft (); } -| ("." {jjtree.extendLeft();} {setLastTokenImage(jjtThis);}) #FieldAccess // { wrapLeft (); } -} - -void MethodReference(): // LHS is injected -{checkForBadMethodReferenceUsage();} -{ - "::" {jjtree.extendLeft();} - [TypeArguments()] ( "new" | ) {setLastTokenImage(jjtThis);} -} /* Expressions that may be present at the start of a primary expression. @@ -2471,7 +2434,7 @@ void PrimaryPrefix() #void : | LOOKAHEAD( "->", {!inSwitchLabel} ) LambdaExpression() | LOOKAHEAD( "(" VariableDeclaratorId() ( "," VariableDeclaratorId() )* ")" "->", {!inSwitchLabel} ) LambdaExpression() | LOOKAHEAD( FormalParameters() "->", {!inSwitchLabel} ) LambdaExpression() -| ("(" Expression() ")") #ParenthesizedExpression +| ("(" Expression() ")") #ParenthesizedExpression | UnqualifiedAllocationExpr() | LOOKAHEAD( TypeOrVoid() "." "class" ) (TypeOrVoid() "." "class") #ClassLiteral @@ -2499,15 +2462,32 @@ void PrimaryPrefix() #void : void PrimarySuffix() #void : {} { - MethodReference() // { wrapLeft(); } -| ArgumentsList() #MethodCall(2) // { wrapLeft(); } -| ("[" Expression() "]") #ArrayAccess(2) // { wrapLeft(); } + MethodReference() +| ArgumentsList() #MethodCall(2) +| ("[" Expression() "]") #ArrayAccess(2) // all the following start with a "." -| LOOKAHEAD(2) ("." "this") #ThisExpression(1) -| LOOKAHEAD(2) ("." "super") #SuperExpression(1) // { wrapLeft(); } +| LOOKAHEAD(2) ("." "this") #ThisExpression(1) +| LOOKAHEAD(2) ("." "super") #SuperExpression(1) // lookahead("." "new") -| LOOKAHEAD(2) QualifiedAllocationExpr() //{ wrapLeft(); } -| LOOKAHEAD(1) MemberSelector() // starts with "." +| LOOKAHEAD(2) QualifiedAllocationExpr() +| MemberSelector() // more complex method call patterns +} + + +void MemberSelector() #void : +{} +{ + // if there are type arguments, this is a method call + LOOKAHEAD(2) ("." {jjtree.extendLeft();} TypeArguments() {setLastTokenImage (jjtThis) ;} ArgumentsList()) #MethodCall +| LOOKAHEAD(3) ("." {jjtree.extendLeft();} {setLastTokenImage(jjtThis);} ArgumentsList()) #MethodCall +| ("." {jjtree.extendLeft();} {setLastTokenImage(jjtThis);}) #FieldAccess +} + +void MethodReference(): // LHS is injected +{checkForBadMethodReferenceUsage();} +{ + "::" {jjtree.extendLeft();} + [TypeArguments()] ( "new" | ) {setLastTokenImage(jjtThis);} } void LambdaExpression() : @@ -2545,7 +2525,6 @@ void LambdaParameterType() #void : LOOKAHEAD( { jdkVersion >= 11 && isKeyword("var") } ) | Type() } -// TODO compound expression abstraction void Literal() #void : { Token t;} @@ -2563,9 +2542,9 @@ void NumericLiteral(): Token t; } { - ( t= { jjtThis.setIntLiteral();} - | t= { jjtThis.setFloatLiteral();} - | t= { checkForBadHexFloatingPointLiteral(); jjtThis.setFloatLiteral();} + ( t= { jjtThis.setIntLiteral();} + | t= { jjtThis.setFloatLiteral();} + | t= { checkForBadHexFloatingPointLiteral(); jjtThis.setFloatLiteral();} ) { checkForBadNumericalLiteralslUsage(t); @@ -2687,10 +2666,9 @@ void ArrayDimsAndInits() : LOOKAHEAD(2) ( LOOKAHEAD(2) (TypeAnnotation())* "[" Expression() "]" {jjtThis.bumpArrayDepth();})+ ( LOOKAHEAD(2) "[" "]" {jjtThis.bumpArrayDepth();} )* | - ( "[" "]" {jjtThis.bumpArrayDepth();})+ ArrayInitializer() + Dims() ArrayInitializer() } - /* * Statement syntax follows. */ @@ -3241,4 +3219,4 @@ void VariableReference(): {} { -} \ No newline at end of file +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAmbiguousName.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAmbiguousName.java index 5bfc5436a9..4d68b99c60 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAmbiguousName.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAmbiguousName.java @@ -14,12 +14,12 @@ import net.sourceforge.pmd.lang.java.xpath.SemanticAmbiguityChecker; /** - * An ambiguous name occurring in any context. Without a disambiguation pass that - * taking care of obscuring rules and the current declarations in scope, this node could be - * a type, package, or variable name -we can't know for sure. The node is a placeholder - * for that unknown entity. It implements both {@link ASTType} and {@link ASTPrimaryExpression} - * to be able to be inserted in their hierarchy. E.g. if the LHS of a method call is an ambiguous - * name, + * An ambiguous name occurring in any context. Without a disambiguation + * pass that taking care of obscuring rules and the current declarations + * in scope, this node could be a type, package, or variable name -we + * can't know for sure. The node is a placeholder for that unknown entity. + * It implements both {@link ASTType} and {@link ASTPrimaryExpression} to + * be able to be inserted in their hierarchy. * *

This node corresponds simultaneously to the AmbiguousName * and PackageOrTypeName productions of the JLS. @@ -32,9 +32,10 @@ import net.sourceforge.pmd.lang.java.xpath.SemanticAmbiguityChecker; * * @implNote

Disambiguation

* - *

Some ambiguous names are pushed by the expression parser because we don't want to look too - * far ahead (in primary prefix). But it can happen that the next segment (primary suffix) constrains - * the name to be e.g. a type name or an expression name. E.g. From the JLS: + *

Some ambiguous names are pushed by the expression parser because + * we don't want to look too far ahead (in primary prefix). But it can + * happen that the next segment (primary suffix) constrains the name to + * be e.g. a type name or an expression name. E.g. From the JLS: * *

* A name is syntactically classified as an ExpressionName in these contexts: @@ -42,18 +43,22 @@ import net.sourceforge.pmd.lang.java.xpath.SemanticAmbiguityChecker; * - As the qualifying expression in a qualified class instance creation expression (ยง15.9)* *
* - * We don't know at the moment the name is parsed that it will be followed by "." "new" and a constructor - * call. But as soon as the {@link ASTConstructorCall} is pushed, we know that the LHS must be an - * expression. In that case, the name can be reclassified, and e.g. if it's a simple name be promoted - * to {@link ASTVariableReference}. This type of immediate disambiguation is carried out by {@link AbstractLateInitNode#onInjectFinished()}. + * We don't know at the moment the name is parsed that it will be + * followed by "." "new" and a constructor call. But as soon as the + * {@link ASTConstructorCall} is pushed, we know that the LHS must be an + * expression. In that case, the name can be reclassified, and e.g. if + * it's a simple name be promoted to {@link ASTVariableReference}. This + * type of immediate disambiguation is carried out by the {@link AbstractJavaNode#jjtClose()} + * method of those nodes that can be pushed. * - *

Another mechanism is {@link #forceExprContext()} and {@link #forceTypeContext()}, which are - * called by the parser to promote an ambiguous name to an expression or a type when it's sure they - * must be one, but there's no {@link AbstractLateInitNode} that follows them. + *

Another mechanism is {@link #forceExprContext()} and {@link #forceTypeContext()}, + * which are called by the parser to promote an ambiguous name to an + * expression or a type when it's sure they must be one. * - *

These two mechanisms perform the first classification step, the one that only depends on the - * syntactic context and not on semantic information. A second pass on the AST after building the - * symbol tables would allow us to remove all the remaining ambiguous names. + *

These two mechanisms perform the first classification step, the + * one that only depends on the syntactic context and not on semantic + * information. A second pass on the AST after building the symbol tables + * would allow us to remove all the remaining ambiguous names. */ public final class ASTAmbiguousName extends AbstractJavaTypeNode implements ASTReferenceType, ASTPrimaryExpression { @@ -109,13 +114,15 @@ public final class ASTAmbiguousName extends AbstractJavaTypeNode implements ASTR /** * Called by the parser if this ambiguous name was a full expression. - * Then, since the node was in an expression syntactic context, - * we can do some preliminary reclassification: - * * If the name is a single identifier, then this can be - * reclassified as an {@link ASTVariableReference} - * * If the name is a sequence of identifiers, then the last - * segment can be reclassified as an {@link ASTFieldAccess}, - * and the rest of the sequence (to the left) is left ambiguous. + * Then, since the node was in an expression syntactic context, we + * can do some preliminary reclassification: + *

    + *
  • If the name is a single identifier, then this can be + * reclassified as an {@link ASTVariableReference} + *
  • If the name is a sequence of identifiers, then the last + * segment can be reclassified as an {@link ASTFieldAccess}, + * and the rest of the sequence (to the left) is left ambiguous. + *
* * @return the node which will replace this node in the tree */ @@ -127,9 +134,9 @@ public final class ASTAmbiguousName extends AbstractJavaTypeNode implements ASTR /** - * Called by the parser if this ambiguous name was expected to be a type name. - * Then we simply promote it to an {@link ASTClassOrInterfaceType} with the appropriate - * {@link ASTClassOrInterfaceType#getAmbiguousLhs()}. + * Called by the parser if this ambiguous name was expected to be + * a type name. Then we simply promote it to an {@link ASTClassOrInterfaceType} + * with the appropriate {@link ASTClassOrInterfaceType#getAmbiguousLhs()}. * * @return the node which will replace this node in the tree */ @@ -140,16 +147,20 @@ public final class ASTAmbiguousName extends AbstractJavaTypeNode implements ASTR /** - * Low level method to reclassify this ambiguous name. Basically the name is split - * in two: the part before the last dot, and the part after it. + * Low level method to reclassify this ambiguous name. Basically + * the name is split in two: the part before the last dot, and the + * part after it. * - * @param simpleNameHandler Called with this name as parameter if this ambiguous name - * is a simple name. No resizing of the node is performed. - * @param splitNameConsumer Called with this node as first parameter, and the last name - * segment as second parameter. After the handler is executed, - * the text bounds of this node are shrunk to fit to only the - * left part. The handler may e.g. move the node to another - * parent. + * @param simpleNameHandler Called with this name as parameter if + * this ambiguous name is a simple name. + * No resizing of the node is performed. + * + * @param splitNameConsumer Called with this node as first parameter, + * and the last name segment as second + * parameter. After the handler is executed, + * the text bounds of this node are shrunk + * to fit to only the left part. The handler + * may e.g. move the node to another parent. * @param Result type * * @return The node that will replace this one. @@ -185,17 +196,15 @@ public final class ASTAmbiguousName extends AbstractJavaTypeNode implements ASTR /** - * A specialized version of {@link #shrinkOneSegment(Function, BiFunction)} for nodes - * that carry the unambiguous part as their own image. E.g. when the parser sees an ambiguous - * name as primary prefix, then method arguments immediately after, it pushes an {@link ASTMethodCall}, - * calls wrapLeft(), which calls {@link AbstractLateInitNode#onInjectFinished()}, which calls this method - * to shrink the ambiguous name because it's necessarily the method's name. The last part is set - * on the parent. + * A specialized version of {@link #shrinkOneSegment(Function, BiFunction)} + * for nodes that carry the unambiguous part as their own image. + * Basically the last segment is set as the image of the parent + * node, and no node corresponds to it. */ void shrinkOrDeleteInParentSetImage() { // the params of the lambdas here are this object, - // but if we use them instead of this, we avoid capturing the this reference - // and the lambdas can be optimised to a singleton + // but if we use them instead of this, we avoid capturing the + // this reference and the lambdas can be optimised to a singleton shrinkOneSegment( simpleName -> { simpleName.jjtGetParent().setImage(simpleName.getImage()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodReference.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodReference.java index 22367c67f3..fb528a4318 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodReference.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodReference.java @@ -36,13 +36,14 @@ public class ASTMethodReference extends AbstractJavaTypeNode implements ASTPrima return visitor.visit(this, data); } + @Override - public void jjtOpen() { - super.jjtOpen(); - AbstractJavaNode node = (AbstractJavaNode) parser.jjtree.peekNode(); - enlargeLeft(node); + public void jjtClose() { + super.jjtClose(); + enlargeLeft(); } + /** * Returns true if this is a constructor reference, * e.g. {@code ArrayList::new}. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JJTJavaParserState.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JJTJavaParserState.java index d0b6548ae1..54a430eb0a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JJTJavaParserState.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JJTJavaParserState.java @@ -2,13 +2,11 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -/* Generated By:JavaCC: Do not edit this line. JJTJavaParserState.java Version 5.0 */ - package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.ast.Node; -public class JJTJavaParserState { +class JJTJavaParserState { private java.util.List nodes; private java.util.List marks; @@ -24,15 +22,19 @@ public class JJTJavaParserState { mk = 0; } - /* Determines whether the current node was actually closed and - pushed. This should only be called in the final user action of a - node scope. */ + /** + * Determines whether the current node was actually closed and + * pushed. This should only be called in the final user action of a + * node scope. + */ public boolean nodeCreated() { return node_created; } - /* Call this to reinitialize the node stack. It is called - automatically by the parser's ReInit() method. */ + /** + * Call this to reinitialize the node stack. It is called + * automatically by the parser's ReInit() method. + */ public void reset() { nodes.clear(); marks.clear(); @@ -40,8 +42,10 @@ public class JJTJavaParserState { mk = 0; } - /* Returns the root node of the AST. It only makes sense to call - this after a successful parse. */ + /** + * Returns the root node of the AST. It only makes sense to call + * this after a successful parse. + */ public Node rootNode() { return nodes.get(0); } @@ -57,14 +61,16 @@ public class JJTJavaParserState { } - /* Pushes a node on to the stack. */ + /** Pushes a node on to the stack. */ public void pushNode(Node n) { nodes.add(n); ++sp; } - /* Returns the node on the top of the stack, and remove it from the - stack. */ + /** + * Returns the node on the top of the stack, and remove it from the + * stack. + */ public Node popNode() { if (--sp < mk) { mk = marks.remove(marks.size() - 1); @@ -72,13 +78,15 @@ public class JJTJavaParserState { return nodes.remove(nodes.size() - 1); } - /* Returns the node currently on the top of the stack. */ + /** Returns the node currently on the top of the stack. */ public Node peekNode() { return nodes.get(nodes.size() - 1); } - /* Returns the number of children on the stack in the current node - scope. */ + /** + * Returns the number of children on the stack in the current node + * scope. + */ public int nodeArity() { return sp - mk; } @@ -99,10 +107,12 @@ public class JJTJavaParserState { } - /* A definite node is constructed from a specified number of - children. That number of nodes are popped from the stack and - made the children of the definite node. Then the definite node - is pushed on to the stack. */ + /** + * A definite node is constructed from a specified number of + * children. That number of nodes are popped from the stack and + * made the children of the definite node. Then the definite node + * is pushed on to the stack. + */ public void closeNodeScope(Node n, int num) { mk = marks.remove(marks.size() - 1); while (num-- > 0) { @@ -116,11 +126,13 @@ public class JJTJavaParserState { } - /* A conditional node is constructed if its condition is true. All - the nodes that have been pushed since the node was opened are - made children of the conditional node, which is then pushed - on to the stack. If the condition is false the node is not - constructed and they are left on the stack. */ + /** + * A conditional node is constructed if its condition is true. All + * the nodes that have been pushed since the node was opened are + * made children of the conditional node, which is then pushed + * on to the stack. If the condition is false the node is not + * constructed and they are left on the stack. + */ public void closeNodeScope(Node n, boolean condition) { if (condition) { int a = nodeArity(); @@ -139,4 +151,3 @@ public class JJTJavaParserState { } } } -/* JavaCC - OriginalChecksum=cfc84c2e5756b80b513365816808f071 (do not edit this line) */ diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodReferenceTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodReferenceTest.kt index 2ddfea05df..8f1724aa4a 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodReferenceTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodReferenceTest.kt @@ -192,4 +192,4 @@ class ASTMethodReferenceTest : ParserTestSpec({ } } } -}) \ No newline at end of file +})