Parse ClassOrInterfaceType recursively
This commit is contained in:
Clément Fournier
committed by
Andreas Dangel
parent
8a588f565f
commit
18581b82ae
@ -214,6 +214,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.sourceforge.pmd.lang.ast.CharStream;
|
import net.sourceforge.pmd.lang.ast.CharStream;
|
||||||
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
||||||
|
import net.sourceforge.pmd.lang.ast.Node;
|
||||||
|
|
||||||
public class JavaParser {
|
public class JavaParser {
|
||||||
|
|
||||||
private int jdkVersion = 0;
|
private int jdkVersion = 0;
|
||||||
@ -1987,18 +1989,51 @@ void ReferenceType() #void:
|
|||||||
| ( ClassOrInterfaceType() [ LOOKAHEAD(2) Dims() ] ) #ArrayType(>1)
|
| ( ClassOrInterfaceType() [ LOOKAHEAD(2) Dims() ] ) #ArrayType(>1)
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassOrInterfaceType():
|
void ClassOrInterfaceType() #void:
|
||||||
{
|
{
|
||||||
StringBuilder s = new StringBuilder();
|
StringBuilder s = new StringBuilder();
|
||||||
Token t;
|
Token t;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
t=<IDENTIFIER> {s.append(t.image);}
|
(
|
||||||
[ LOOKAHEAD(2) TypeArguments() ]
|
(
|
||||||
( LOOKAHEAD(2) "." t=<IDENTIFIER> {s.append('.').append(t.image);} [ LOOKAHEAD(2) TypeArguments() ] )*
|
t=<IDENTIFIER> { s.append(t.getImage()); }
|
||||||
{jjtThis.setImage(s.toString());}
|
// We gobble up all identifiers until we find
|
||||||
|
// either type arguments or annotations, because
|
||||||
|
// it may be a FQCN
|
||||||
|
( LOOKAHEAD("." <IDENTIFIER>)
|
||||||
|
"." t=<IDENTIFIER> { s.append('.').append(t.getImage()); }
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
[ LOOKAHEAD( "<" ) TypeArguments() ]
|
||||||
|
) #ClassOrInterfaceType
|
||||||
|
{
|
||||||
|
// At this point the first ClassOrInterfaceType is on top of the stack
|
||||||
|
jjtree.peekNode().setImage(s.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
( LOOKAHEAD(2)
|
||||||
|
(
|
||||||
|
"."
|
||||||
|
(TypeAnnotation())*
|
||||||
|
t=<IDENTIFIER> { jjtThis.setImage(t.getImage()); }
|
||||||
|
[ LOOKAHEAD( "<" ) TypeArguments() ]
|
||||||
|
) #ClassOrInterfaceType {
|
||||||
|
// At this point the newer ClassOrInterfaceType has been pushed on the stack
|
||||||
|
// We retrieve it
|
||||||
|
AbstractJavaNode lastSegment = (AbstractJavaNode) jjtree.popNode();
|
||||||
|
// All its children (annotations and type arguments) have already been popped,
|
||||||
|
// so the node remaining on top of the stack is the previous segment
|
||||||
|
Node previousSegment = jjtree.popNode();
|
||||||
|
|
||||||
|
lastSegment.insertChild((JavaNode) previousSegment, 0, true);
|
||||||
|
jjtree.pushNode(lastSegment);
|
||||||
|
}
|
||||||
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TypeArguments():
|
void TypeArguments():
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
|
@ -5,15 +5,34 @@
|
|||||||
|
|
||||||
package net.sourceforge.pmd.lang.java.ast;
|
package net.sourceforge.pmd.lang.java.ast;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
/**
|
/**
|
||||||
* Represents a class or interface type, possibly parameterised with type arguments.
|
* Represents a class or interface type, possibly parameterised with type arguments.
|
||||||
|
* This node comes in two productions (see below):
|
||||||
|
*
|
||||||
|
* <p>The first is a left-recursive variant, allowing to parse references to type members
|
||||||
|
* unambiguously. The resulting node's {@linkplain #getLeftHandSide() left-hand-side type}
|
||||||
|
* addresses the type parent of the type. The position of type arguments and annotations are
|
||||||
|
* preserved.
|
||||||
|
*
|
||||||
|
* <p>Parsing types left-recursively has a caveat though: fully-qualified type names. The
|
||||||
|
* parser can't disambiguate between a reference to a type member (e.g. {@code Map.Entry}, where
|
||||||
|
* both segments refer to a type, and which would ideally be parsed left-recursively), and a
|
||||||
|
* qualified type name (e.g. {@code java.util.List}, where the full sequence refers to a unique
|
||||||
|
* type, but individual segments don't).
|
||||||
|
*
|
||||||
|
* <p>We could remove that with a later AST visit, like type resolution though!
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
*
|
*
|
||||||
* ClassOrInterfaceType ::= <IDENTIFIER> {@linkplain ASTTypeArguments TypeArguments}? ( "." <IDENTIFIER> {@linkplain ASTTypeArguments TypeArguments}? )*
|
* ClassOrInterfaceType ::= ClassOrInterfaceType ( "." {@linkplain ASTAnnotation Annotation}* <IDENTIFIER> {@linkplain ASTTypeArguments TypeArguments}? )+
|
||||||
|
* | <IDENTIFIER> ( "." <IDENTIFIER> ) * {@linkplain ASTTypeArguments TypeArguments}?
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
// @formatter:on
|
||||||
public class ASTClassOrInterfaceType extends AbstractJavaTypeNode implements ASTReferenceType {
|
public class ASTClassOrInterfaceType extends AbstractJavaTypeNode implements ASTReferenceType {
|
||||||
public ASTClassOrInterfaceType(String identifier) {
|
public ASTClassOrInterfaceType(String identifier) {
|
||||||
super(JavaParserTreeConstants.JJTCLASSORINTERFACETYPE);
|
super(JavaParserTreeConstants.JJTCLASSORINTERFACETYPE);
|
||||||
@ -31,6 +50,17 @@ public class ASTClassOrInterfaceType extends AbstractJavaTypeNode implements AST
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the left-hand side type of this type. This is a type we know for sure
|
||||||
|
* that this type is a member of.
|
||||||
|
*
|
||||||
|
* @return A type, or null if this is a base type
|
||||||
|
*/
|
||||||
|
public Optional<ASTClassOrInterfaceType> getLeftHandSide() {
|
||||||
|
return Optional.ofNullable(getFirstChildOfType(ASTClassOrInterfaceType.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
|
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
|
||||||
return visitor.visit(this, data);
|
return visitor.visit(this, data);
|
||||||
@ -48,8 +78,8 @@ public class ASTClassOrInterfaceType extends AbstractJavaTypeNode implements AST
|
|||||||
* same compilation unit - either a class/interface or a enum type. You want
|
* same compilation unit - either a class/interface or a enum type. You want
|
||||||
* to check this, if {@link #getType()} is null.
|
* to check this, if {@link #getType()} is null.
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if this node referencing a type in the same
|
* @return {@code true} if this node referencing a type in the same
|
||||||
* compilation unit, <code>false</code> otherwise.
|
* compilation unit, {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isReferenceToClassSameCompilationUnit() {
|
public boolean isReferenceToClassSameCompilationUnit() {
|
||||||
ASTCompilationUnit root = getFirstParentOfType(ASTCompilationUnit.class);
|
ASTCompilationUnit root = getFirstParentOfType(ASTCompilationUnit.class);
|
||||||
|
@ -8,21 +8,25 @@ import net.sourceforge.pmd.lang.ast.AbstractNode;
|
|||||||
import net.sourceforge.pmd.lang.ast.Node;
|
import net.sourceforge.pmd.lang.ast.Node;
|
||||||
import net.sourceforge.pmd.lang.symboltable.Scope;
|
import net.sourceforge.pmd.lang.symboltable.Scope;
|
||||||
|
|
||||||
|
|
||||||
public abstract class AbstractJavaNode extends AbstractNode implements JavaNode {
|
public abstract class AbstractJavaNode extends AbstractNode implements JavaNode {
|
||||||
|
|
||||||
protected JavaParser parser;
|
protected JavaParser parser;
|
||||||
private Scope scope;
|
private Scope scope;
|
||||||
private Comment comment;
|
private Comment comment;
|
||||||
|
|
||||||
|
|
||||||
public AbstractJavaNode(int id) {
|
public AbstractJavaNode(int id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public AbstractJavaNode(JavaParser parser, int id) {
|
public AbstractJavaNode(JavaParser parser, int id) {
|
||||||
super(id);
|
super(id);
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void jjtOpen() {
|
public void jjtOpen() {
|
||||||
if (beginLine == -1 && parser.token.next != null) {
|
if (beginLine == -1 && parser.token.next != null) {
|
||||||
@ -31,6 +35,7 @@ public abstract class AbstractJavaNode extends AbstractNode implements JavaNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void jjtClose() {
|
public void jjtClose() {
|
||||||
if (beginLine == -1 && children.length == 0) {
|
if (beginLine == -1 && children.length == 0) {
|
||||||
@ -43,6 +48,7 @@ public abstract class AbstractJavaNode extends AbstractNode implements JavaNode
|
|||||||
endColumn = parser.token.endColumn;
|
endColumn = parser.token.endColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept the visitor. *
|
* Accept the visitor. *
|
||||||
*/
|
*/
|
||||||
@ -88,20 +94,70 @@ public abstract class AbstractJavaNode extends AbstractNode implements JavaNode
|
|||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setScope(Scope scope) {
|
public void setScope(Scope scope) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void comment(Comment theComment) {
|
public void comment(Comment theComment) {
|
||||||
comment = theComment;
|
comment = theComment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Comment comment() {
|
public Comment comment() {
|
||||||
return comment;
|
return comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// insert a child at a given index, shifting other children if need be
|
||||||
|
// The implementation of jjtAddChild in AbstractNode overwrites nodes
|
||||||
|
// -> probably unexpected and to be changed
|
||||||
|
// parser only
|
||||||
|
void insertChild(JavaNode child, int index, boolean expandTextSpan) {
|
||||||
|
// Allow to insert a child at random index without overwriting
|
||||||
|
// If the child is null, it is replaced. If it is not null, children are shifted
|
||||||
|
if (children != null && index < children.length && children[index] != null) {
|
||||||
|
Node[] newChildren = new Node[children.length + 1];
|
||||||
|
|
||||||
|
// toShift nodes are to the right of the insertion index
|
||||||
|
int toShift = children.length - index;
|
||||||
|
|
||||||
|
// copy the nodes before
|
||||||
|
System.arraycopy(children, 0, newChildren, 0, index);
|
||||||
|
|
||||||
|
// copy the nodes after
|
||||||
|
System.arraycopy(children, index, newChildren, index + 1, toShift);
|
||||||
|
children = newChildren;
|
||||||
|
}
|
||||||
|
super.jjtAddChild(child, index);
|
||||||
|
child.jjtSetParent(this);
|
||||||
|
|
||||||
|
// The text coordinates of this node will be enlarged with those of the child
|
||||||
|
if (expandTextSpan) {
|
||||||
|
AbstractJavaNode childImpl = (AbstractJavaNode) child;
|
||||||
|
|
||||||
|
if (this.beginLine > childImpl.beginLine) {
|
||||||
|
this.beginLine = childImpl.beginLine;
|
||||||
|
this.beginColumn = childImpl.beginColumn;
|
||||||
|
} else if (this.beginLine == childImpl.beginLine
|
||||||
|
&& this.beginColumn > childImpl.beginColumn) {
|
||||||
|
this.beginColumn = childImpl.beginColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.endLine < childImpl.endLine) {
|
||||||
|
this.endLine = childImpl.endLine;
|
||||||
|
this.endColumn = childImpl.endColumn;
|
||||||
|
} else if (this.endLine == childImpl.endLine
|
||||||
|
&& this.endColumn < childImpl.endColumn) {
|
||||||
|
this.endColumn = childImpl.endColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO tokens
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String getXPathNodeName() {
|
public final String getXPathNodeName() {
|
||||||
|
Reference in New Issue
Block a user