Remove LateInitNode

This commit is contained in:
Clément Fournier
2019-03-24 19:27:59 +01:00
committed by Andreas Dangel
parent 089b7999d2
commit 3e36c1621b
5 changed files with 131 additions and 132 deletions

View File

@ -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())*
<IDENTIFIER> { 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() <IDENTIFIER> {setLastTokenImage (jjtThis) ;} ArgumentsList()) #MethodCall // { wrapLeft (); }
| LOOKAHEAD(3) ("." {jjtree.extendLeft();} <IDENTIFIER> {setLastTokenImage(jjtThis);} ArgumentsList()) #MethodCall // { wrapLeft (); }
| ("." {jjtree.extendLeft();} <IDENTIFIER> {setLastTokenImage(jjtThis);}) #FieldAccess // { wrapLeft (); }
}
void MethodReference(): // LHS is injected
{checkForBadMethodReferenceUsage();}
{
"::" {jjtree.extendLeft();}
[TypeArguments()] ( "new" | <IDENTIFIER>) {setLastTokenImage(jjtThis);}
}
/*
Expressions that may be present at the start of a primary expression.
@ -2471,7 +2434,7 @@ void PrimaryPrefix() #void :
| LOOKAHEAD( <IDENTIFIER> "->", {!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() <IDENTIFIER> {setLastTokenImage (jjtThis) ;} ArgumentsList()) #MethodCall
| LOOKAHEAD(3) ("." {jjtree.extendLeft();} <IDENTIFIER> {setLastTokenImage(jjtThis);} ArgumentsList()) #MethodCall
| ("." {jjtree.extendLeft();} <IDENTIFIER> {setLastTokenImage(jjtThis);}) #FieldAccess
}
void MethodReference(): // LHS is injected
{checkForBadMethodReferenceUsage();}
{
"::" {jjtree.extendLeft();}
[TypeArguments()] ( "new" | <IDENTIFIER>) {setLastTokenImage(jjtThis);}
}
void LambdaExpression() :
@ -2545,7 +2525,6 @@ void LambdaParameterType() #void :
LOOKAHEAD( { jdkVersion >= 11 && isKeyword("var") } ) <IDENTIFIER>
| Type()
}
// TODO compound expression abstraction
void Literal() #void :
{ Token t;}
@ -2563,9 +2542,9 @@ void NumericLiteral():
Token t;
}
{
( t=<INTEGER_LITERAL> { jjtThis.setIntLiteral();}
| t=<FLOATING_POINT_LITERAL> { jjtThis.setFloatLiteral();}
| t=<HEX_FLOATING_POINT_LITERAL> { checkForBadHexFloatingPointLiteral(); jjtThis.setFloatLiteral();}
( t=<INTEGER_LITERAL> { jjtThis.setIntLiteral();}
| t=<FLOATING_POINT_LITERAL> { jjtThis.setFloatLiteral();}
| t=<HEX_FLOATING_POINT_LITERAL> { 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():
{}
{
<IDENTIFIER>
}
}

View File

@ -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.
*
* <p>This node corresponds simultaneously to the <a href="https://docs.oracle.com/javase/specs/jls/se9/html/jls-6.html#jls-AmbiguousName">AmbiguousName</a>
* and PackageOrTypeName productions of the JLS.
@ -32,9 +32,10 @@ import net.sourceforge.pmd.lang.java.xpath.SemanticAmbiguityChecker;
*
* @implNote <h3>Disambiguation</h3>
*
* <p>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:
* <p>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:
*
* <blockquote>
* 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)*
* </blockquote>
*
* 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.
*
* <p>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.
* <p>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.
*
* <p>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.
* <p>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:
* <ul>
* <li>If the name is a single identifier, then this can be
* reclassified as an {@link ASTVariableReference}
* <li>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.
* </ul>
*
* @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 <T> 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());

View File

@ -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}.

View File

@ -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<Node> nodes;
private java.util.List<Integer> 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) */

View File

@ -192,4 +192,4 @@ class ASTMethodReferenceTest : ParserTestSpec({
}
}
}
})
})