forked from phoedos/pmd
Changes to antlr impl
* AntlrBaseVisitor does not extend AbstractRule anymore * An AntlrBaseRule *uses* a visitor, it *is* not a visitor
This commit is contained in:
@ -17,9 +17,9 @@ import org.jaxen.BaseXPath;
|
||||
import org.jaxen.JaxenException;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.ast.internal.StreamImpl;
|
||||
import net.sourceforge.pmd.lang.ast.internal.TraversalUtils;
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator;
|
||||
@ -66,7 +66,9 @@ public interface Node {
|
||||
* @deprecated This is JJTree-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
void jjtOpen();
|
||||
default void jjtOpen() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -75,7 +77,10 @@ public interface Node {
|
||||
* @deprecated This is JJTree-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
void jjtClose();
|
||||
default void jjtClose() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the parent of this node.
|
||||
@ -85,7 +90,10 @@ public interface Node {
|
||||
* @deprecated This is JJTree-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
void jjtSetParent(Node parent);
|
||||
default void jjtSetParent(Node parent) {
|
||||
throw new UnsupportedOperationException("JJTree specific");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parent of this node.
|
||||
@ -96,7 +104,10 @@ public interface Node {
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
Node jjtGetParent();
|
||||
default Node jjtGetParent() {
|
||||
return getParent();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method tells the node to add its argument to the node's list of children.
|
||||
@ -107,19 +118,24 @@ public interface Node {
|
||||
* @deprecated This is JJTree-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
void jjtAddChild(Node child, int index);
|
||||
default void jjtAddChild(Node child, int index) {
|
||||
throw new UnsupportedOperationException("JJTree specific");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the index of this node from the perspective of its parent. This
|
||||
* means: this.getParent().getChild(index) == this.
|
||||
*
|
||||
* @param index
|
||||
* the child index
|
||||
* @param index the child index
|
||||
*
|
||||
* @deprecated This is JJTree-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
void jjtSetChildIndex(int index);
|
||||
default void jjtSetChildIndex(int index) {
|
||||
throw new UnsupportedOperationException("JJTree specific");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of this node in the children of its parent.
|
||||
@ -129,20 +145,23 @@ public interface Node {
|
||||
* @deprecated Use {@link #getIndexInParent()}
|
||||
*/
|
||||
@Deprecated
|
||||
int jjtGetChildIndex();
|
||||
default int jjtGetChildIndex() {
|
||||
return getIndexInParent();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method returns a child node. The children are numbered from zero, left to right.
|
||||
*
|
||||
* @param index
|
||||
* the child index. Must be nonnegative and less than
|
||||
* {@link #jjtGetNumChildren}.
|
||||
* @param index the child index. Must be nonnegative and less than
|
||||
* {@link #jjtGetNumChildren}.
|
||||
*
|
||||
* @deprecated Use {@link #getChild(int)}
|
||||
*/
|
||||
@Deprecated
|
||||
Node jjtGetChild(int index);
|
||||
default Node jjtGetChild(int index) {
|
||||
return getChild(index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -151,21 +170,28 @@ public interface Node {
|
||||
* @deprecated Use {@link #getNumChildren()}
|
||||
*/
|
||||
@Deprecated
|
||||
int jjtGetNumChildren();
|
||||
default int jjtGetNumChildren() {
|
||||
return getNumChildren();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated This is JJTree-specific and will be removed from this interface.
|
||||
*/
|
||||
@Deprecated
|
||||
int jjtGetId();
|
||||
default int jjtGetId() {
|
||||
throw new UnsupportedOperationException("JJTree specific");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string token, usually filled-in by the parser, which describes some textual characteristic of this
|
||||
* node. This is usually an identifier, but you should check that using the Designer. On most nodes though, this
|
||||
* method returns {@code null}.
|
||||
*/
|
||||
String getImage();
|
||||
default String getImage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -173,21 +199,30 @@ public interface Node {
|
||||
*/
|
||||
@InternalApi
|
||||
@Deprecated
|
||||
void setImage(String image);
|
||||
default void setImage(String image) {
|
||||
throw new UnsupportedOperationException("setImage");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this node's image is equal to the given string.
|
||||
*
|
||||
* @param image The image to check
|
||||
*/
|
||||
boolean hasImageEqualTo(String image);
|
||||
default boolean hasImageEqualTo(String image) {
|
||||
return getImage() != null && getImage().equals(image);
|
||||
}
|
||||
|
||||
|
||||
int getBeginLine();
|
||||
|
||||
|
||||
int getBeginColumn();
|
||||
|
||||
|
||||
int getEndLine();
|
||||
|
||||
|
||||
// FIXME should not be inclusive
|
||||
int getEndColumn();
|
||||
|
||||
@ -196,14 +231,19 @@ public interface Node {
|
||||
* @deprecated This is Java-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
DataFlowNode getDataFlowNode();
|
||||
default DataFlowNode getDataFlowNode() {
|
||||
throw new UnsupportedOperationException("JJTree specific");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated This is Java-specific and will be removed from this interface
|
||||
*/
|
||||
@Deprecated
|
||||
void setDataFlowNode(DataFlowNode dataFlowNode);
|
||||
default void setDataFlowNode(DataFlowNode dataFlowNode) {
|
||||
throw new UnsupportedOperationException("JJTree specific");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this node is considered a boundary by traversal methods. Traversal methods such as {@link
|
||||
@ -425,6 +465,7 @@ public interface Node {
|
||||
*/
|
||||
void setUserData(Object userData);
|
||||
|
||||
|
||||
/**
|
||||
* Remove the current node from its parent.
|
||||
*
|
||||
@ -432,20 +473,24 @@ public interface Node {
|
||||
*/
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
void remove();
|
||||
default void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method tells the node to remove the child node at the given index from the node's list of children, if any;
|
||||
* if not, no changes are done.
|
||||
*
|
||||
* @param childIndex
|
||||
* The index of the child to be removed
|
||||
* @param childIndex The index of the child to be removed
|
||||
*
|
||||
* @deprecated This is internal API and will be removed from this interface with 7.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
void removeChildAtIndex(int childIndex);
|
||||
default void removeChildAtIndex(int childIndex) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -4,26 +4,11 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
|
||||
import org.antlr.v4.runtime.tree.RuleNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.rule.AbstractRule;
|
||||
|
||||
public abstract class AbstractAntlrVisitor<T> extends AbstractRule implements ParseTreeVisitor<T> {
|
||||
|
||||
protected RuleContext data;
|
||||
|
||||
@Override
|
||||
public void start(RuleContext ctx) {
|
||||
data = ctx;
|
||||
}
|
||||
public abstract class AbstractAntlrVisitor<T> extends AbstractParseTreeVisitor<T> implements ParseTreeVisitor<T> {
|
||||
|
||||
@Override
|
||||
public T visit(ParseTree tree) {
|
||||
@ -33,54 +18,6 @@ public abstract class AbstractAntlrVisitor<T> extends AbstractRule implements Pa
|
||||
return tree.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visitChildren(RuleNode node) {
|
||||
T result = this.defaultResult();
|
||||
final int n = node.getChildCount();
|
||||
|
||||
for (int i = 0; i < n && this.shouldVisitNextChild(node, result); ++i) {
|
||||
final ParseTree c = node.getChild(i);
|
||||
final T childResult = visit(c);
|
||||
result = this.aggregateResult(result, childResult);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visitTerminal(TerminalNode node) {
|
||||
return this.defaultResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visitErrorNode(ErrorNode node) {
|
||||
return this.defaultResult();
|
||||
}
|
||||
|
||||
protected T defaultResult() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected T aggregateResult(T aggregate, T nextResult) {
|
||||
return nextResult;
|
||||
}
|
||||
|
||||
protected boolean shouldVisitNextChild(RuleNode node, T currentResult) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(List<? extends Node> nodes, RuleContext ctx) {
|
||||
visitAll(nodes);
|
||||
}
|
||||
|
||||
protected void visitAll(List<? extends Node> nodes) {
|
||||
for (Node n : nodes) {
|
||||
AntlrBaseNode node = (AntlrBaseNode) n;
|
||||
visit(node);
|
||||
}
|
||||
}
|
||||
|
||||
public T visit(final AntlrBaseNode node) {
|
||||
return node.accept(this);
|
||||
}
|
||||
|
@ -4,20 +4,10 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.dfa.DataFlowNode;
|
||||
public abstract class AntlrBaseNode extends ParserRuleContext implements AntlrNode {
|
||||
|
||||
public class AntlrBaseNode extends ParserRuleContext implements AntlrNode {
|
||||
|
||||
// TODO: what should we do with parent? how do we handle data flows in this scenario? it's ok to ignore
|
||||
// TODO: our parent data flow in case we don't have one?
|
||||
// protected Node parent;
|
||||
|
||||
private DataFlowNode dataFlowNode;
|
||||
private Object userData;
|
||||
|
||||
/**
|
||||
@ -39,10 +29,7 @@ public class AntlrBaseNode extends ParserRuleContext implements AntlrNode {
|
||||
super(parent, invokingStateNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node jjtGetParent() {
|
||||
return (Node) parent; // TODO: review if all parents are Nodes
|
||||
}
|
||||
// FIXME these coordinates are not accurate
|
||||
|
||||
@Override
|
||||
public int getBeginLine() {
|
||||
@ -64,16 +51,6 @@ public class AntlrBaseNode extends ParserRuleContext implements AntlrNode {
|
||||
return stop.getCharPositionInLine(); // This goes from 0 to (n - 1)
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlowNode getDataFlowNode() {
|
||||
return dataFlowNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataFlowNode(final DataFlowNode dataFlowNode) {
|
||||
this.dataFlowNode = dataFlowNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUserData() {
|
||||
return userData;
|
||||
@ -85,26 +62,18 @@ public class AntlrBaseNode extends ParserRuleContext implements AntlrNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node jjtGetChild(final int index) {
|
||||
try {
|
||||
return (Node) childrenStream().skip(index).findFirst().orElse(null);
|
||||
} catch (final ClassCastException e) {
|
||||
throw new IllegalArgumentException("Accessing invalid Antlr node", e);
|
||||
}
|
||||
public AntlrNode getChild(int i) {
|
||||
return (AntlrNode) super.getChild(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImage() {
|
||||
return null;
|
||||
public AntlrBaseNode getParent() {
|
||||
return (AntlrBaseNode) super.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int jjtGetNumChildren() {
|
||||
return (int) childrenStream().count();
|
||||
}
|
||||
|
||||
private Stream<Node> childrenStream() {
|
||||
return children == null ? Stream.empty() : children.stream().filter(e -> e instanceof Node).map(e -> (Node) e);
|
||||
public int getNumChildren() {
|
||||
return getChildCount();
|
||||
}
|
||||
|
||||
// TODO: should we make it abstract due to the comment in AbstractNode ?
|
||||
|
@ -8,7 +8,7 @@ import org.antlr.v4.runtime.ParserRuleContext;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.RootNode;
|
||||
|
||||
public class AntlrBaseRootNode extends AntlrBaseNode implements RootNode {
|
||||
public abstract class AntlrBaseRootNode extends AntlrBaseNode implements RootNode {
|
||||
|
||||
/**
|
||||
* Constructor required by {@link ParserRuleContext}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.rule.AbstractRule;
|
||||
|
||||
/**
|
||||
* Base implementation of an antlr rule.
|
||||
*/
|
||||
public abstract class AntlrBaseRule extends AbstractRule {
|
||||
|
||||
protected AntlrBaseRule() {
|
||||
// inheritance constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(List<? extends Node> nodes, RuleContext ctx) {
|
||||
AbstractAntlrVisitor<Void> visitor = buildVisitor(ctx);
|
||||
assert visitor != null : "Rule should provide a non-null visitor";
|
||||
|
||||
for (Node node : nodes) {
|
||||
assert node instanceof AntlrBaseNode : "Incorrect node type " + node + " passed to " + this;
|
||||
|
||||
((AntlrBaseNode) node).accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rule visitor that can visit nodes for the given rule context.
|
||||
* This visitor should explore the nodes it's interested in and report
|
||||
* violations on the given rule context.
|
||||
*
|
||||
* @param ruleCtx Object that accumulates rule violations
|
||||
*
|
||||
* @return A visitor bound to the given rule context
|
||||
*/
|
||||
public abstract AbstractAntlrVisitor<Void> buildVisitor(RuleContext ruleCtx);
|
||||
|
||||
}
|
@ -4,72 +4,37 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.ast.NodeStream;
|
||||
|
||||
/**
|
||||
* Base interface for all Antlr-based implementation of Node interface.
|
||||
* <p>
|
||||
* Initially all the methods implemented here will be no-op due to scope limitations
|
||||
*/
|
||||
public interface AntlrNode extends Node {
|
||||
public interface AntlrNode extends Node, ParseTree {
|
||||
|
||||
|
||||
@Override
|
||||
default void jjtOpen() {
|
||||
throw new UnsupportedOperationException("Won't be needed on Antlr implementation");
|
||||
AntlrNode getChild(int index);
|
||||
|
||||
|
||||
@Override
|
||||
AntlrNode getParent();
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
default NodeStream<? extends AntlrNode> children() {
|
||||
return (NodeStream<? extends AntlrNode>) Node.super.children();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void jjtClose() {
|
||||
throw new UnsupportedOperationException("Won't be needed on Antlr implementation");
|
||||
}
|
||||
|
||||
@Override
|
||||
default void jjtSetParent(final Node parent) {
|
||||
default int getIndexInParent() {
|
||||
// FIXME
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default void jjtAddChild(final Node child, final int index) {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default void jjtSetChildIndex(final int index) {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default int jjtGetChildIndex() {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default int jjtGetId() {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setImage(final String image) {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default String getImage() {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasImageEqualTo(final String image) {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default void remove() {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
|
||||
@Override
|
||||
default void removeChildAtIndex(final int childIndex) {
|
||||
throw new UnsupportedOperationException("Out of scope for antlr current implementations");
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,12 @@ import net.sourceforge.pmd.lang.rule.AbstractRuleChainVisitor;
|
||||
import net.sourceforge.pmd.lang.rule.XPathRule;
|
||||
|
||||
public class AntlrRuleChainVisitor extends AbstractRuleChainVisitor {
|
||||
|
||||
@Override
|
||||
protected void visit(Rule rule, Node node, RuleContext ctx) {
|
||||
if (rule instanceof AbstractAntlrVisitor) {
|
||||
((AntlrBaseNode) node).accept((AbstractAntlrVisitor) rule);
|
||||
if (rule instanceof AntlrBaseRule) {
|
||||
AntlrBaseRule rule1 = (AntlrBaseRule) rule;
|
||||
((AntlrBaseNode) node).accept(rule1.buildVisitor(ctx));
|
||||
} else {
|
||||
((XPathRule) rule).evaluate(node, ctx);
|
||||
}
|
||||
@ -24,18 +26,15 @@ public class AntlrRuleChainVisitor extends AbstractRuleChainVisitor {
|
||||
|
||||
@Override
|
||||
protected void indexNodes(List<Node> nodes, RuleContext ctx) {
|
||||
final AbstractAntlrVisitor antlrVisitor = new AbstractAntlrVisitor<Object>() {
|
||||
// Perform a visitation of the AST to index nodes which need
|
||||
// visiting by type
|
||||
@Override
|
||||
public Object visit(final AntlrBaseNode node) {
|
||||
indexNode(node);
|
||||
return super.visit(node);
|
||||
}
|
||||
};
|
||||
|
||||
for (final Node node : nodes) {
|
||||
antlrVisitor.visit((AntlrBaseNode) node);
|
||||
indexSubtree(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void indexSubtree(Node node) {
|
||||
indexNode(node);
|
||||
for (Node child : node.children()) {
|
||||
indexSubtree(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
|
||||
|
||||
public class PmdAntlrErrorNode extends PmdAntlrTerminalNode implements AntlrNode, ErrorNode {
|
||||
|
||||
public PmdAntlrErrorNode(Token t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getXPathNodeName() {
|
||||
return "Error";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
return visitor.visitErrorNode(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.TokenStream;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
public abstract class PmdAntlrParserBase extends Parser {
|
||||
|
||||
public PmdAntlrParserBase(TokenStream input) {
|
||||
super(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalNode createTerminalNode(ParserRuleContext parent, Token t) {
|
||||
return new PmdAntlrTerminalNode(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorNode createErrorNode(ParserRuleContext parent, Token t) {
|
||||
return new PmdAntlrErrorNode(t);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.ast.impl.antlr4;
|
||||
|
||||
import org.antlr.v4.runtime.RuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
|
||||
|
||||
/**
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public class PmdAntlrTerminalNode extends TerminalNodeImpl implements AntlrNode {
|
||||
|
||||
private Object userData;
|
||||
|
||||
public PmdAntlrTerminalNode(Token t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AntlrNode getChild(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumChildren() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getXPathNodeName() {
|
||||
return "Token" + getSymbol().getType(); // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public AntlrNode getParent() {
|
||||
return (AntlrNode) super.getParent();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setParent(RuleContext parent) {
|
||||
assert parent instanceof AntlrNode : "Parent should be a parent";
|
||||
super.setParent(parent);
|
||||
}
|
||||
|
||||
|
||||
// FIXME these coordinates are not accurate
|
||||
|
||||
|
||||
@Override
|
||||
public int getBeginLine() {
|
||||
return getSymbol().getLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBeginColumn() {
|
||||
return getSymbol().getCharPositionInLine() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndLine() {
|
||||
return getBeginLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndColumn() {
|
||||
return getBeginColumn() + getSymbol().getText().length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUserData() {
|
||||
return userData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserData(Object userData) {
|
||||
this.userData = userData;
|
||||
}
|
||||
|
||||
}
|
@ -37,8 +37,25 @@ grammar Swift;
|
||||
import net.sourceforge.pmd.lang.ast.impl.antlr4.*;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@classMembers {
|
||||
|
||||
@Override
|
||||
public TerminalNode createTerminalNode(ParserRuleContext parent, Token t) {
|
||||
return new PmdAntlrTerminalNodeImpl(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorNode createErrorNode(ParserRuleContext parent, Token t) {
|
||||
return new PmdAntlrErrorNodeImpl(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
options {
|
||||
contextSuperClass = AntlrBaseNode;
|
||||
superClass = PmdBaseAntlrParser;
|
||||
}
|
||||
|
||||
topLevel : statements? EOF ;
|
||||
|
@ -4,11 +4,16 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.swift;
|
||||
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrBaseRule;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftBaseVisitor;
|
||||
|
||||
public abstract class AbstractSwiftRule<T> extends SwiftBaseVisitor<T> {
|
||||
public AbstractSwiftRule() {
|
||||
super.setLanguage(LanguageRegistry.getLanguage(SwiftLanguageModule.NAME));
|
||||
public abstract class AbstractSwiftRule extends AntlrBaseRule {
|
||||
|
||||
protected AbstractSwiftRule() {
|
||||
// inheritance constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract SwiftBaseVisitor<Void> buildVisitor(RuleContext ruleCtx);
|
||||
}
|
||||
|
@ -6,13 +6,15 @@ package net.sourceforge.pmd.lang.swift.rule.bestpractices;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.swift.AbstractSwiftRule;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftBaseVisitor;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.AttributeContext;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.FunctionHeadContext;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.VariableDeclarationHeadContext;
|
||||
|
||||
public class ProhibitedInterfaceBuilderRule extends AbstractSwiftRule<Void> {
|
||||
public class ProhibitedInterfaceBuilderRule extends AbstractSwiftRule {
|
||||
|
||||
private static final String IBACTION = "@IBAction";
|
||||
private static final String IBOUTLET = "@IBOutlet";
|
||||
@ -24,31 +26,37 @@ public class ProhibitedInterfaceBuilderRule extends AbstractSwiftRule<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFunctionHead(FunctionHeadContext ctx) {
|
||||
if (ctx == null || ctx.attributes() == null) {
|
||||
return null;
|
||||
}
|
||||
public SwiftBaseVisitor<Void> buildVisitor(RuleContext ruleCtx) {
|
||||
return new SwiftBaseVisitor<Void>() {
|
||||
|
||||
return visitDeclarationHead(ctx, ctx.attributes().attribute(), IBACTION);
|
||||
}
|
||||
@Override
|
||||
public Void visitFunctionHead(FunctionHeadContext ctx) {
|
||||
if (ctx == null || ctx.attributes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariableDeclarationHead(final VariableDeclarationHeadContext ctx) {
|
||||
if (ctx == null || ctx.attributes() == null) {
|
||||
return null;
|
||||
}
|
||||
return visitDeclarationHead(ctx, ctx.attributes().attribute(), IBACTION);
|
||||
}
|
||||
|
||||
return visitDeclarationHead(ctx, ctx.attributes().attribute(), IBOUTLET);
|
||||
}
|
||||
@Override
|
||||
public Void visitVariableDeclarationHead(final VariableDeclarationHeadContext ctx) {
|
||||
if (ctx == null || ctx.attributes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Void visitDeclarationHead(final Node node, final List<SwiftParser.AttributeContext> attributes,
|
||||
final String match) {
|
||||
return visitDeclarationHead(ctx, ctx.attributes().attribute(), IBOUTLET);
|
||||
}
|
||||
|
||||
final boolean violate = attributes.stream().anyMatch(atr -> match.equals(atr.getText()));
|
||||
if (violate) {
|
||||
addViolation(data, node);
|
||||
}
|
||||
private Void visitDeclarationHead(final Node node, final List<AttributeContext> attributes,
|
||||
final String match) {
|
||||
|
||||
return null;
|
||||
final boolean violate = attributes.stream().anyMatch(atr -> match.equals(atr.getText()));
|
||||
if (violate) {
|
||||
addViolation(ruleCtx, node);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ package net.sourceforge.pmd.lang.swift.rule.bestpractices;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.lang.swift.AbstractSwiftRule;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftBaseVisitor;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.FunctionDeclarationContext;
|
||||
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.InitializerDeclarationContext;
|
||||
|
||||
public class UnavailableFunctionRule extends AbstractSwiftRule<Void> {
|
||||
public class UnavailableFunctionRule extends AbstractSwiftRule {
|
||||
|
||||
private static final String AVAILABLE_UNAVAILABLE = "@available(*,unavailable)";
|
||||
private static final String FATAL_ERROR = "fatalError";
|
||||
@ -23,48 +25,55 @@ public class UnavailableFunctionRule extends AbstractSwiftRule<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFunctionDeclaration(final FunctionDeclarationContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
public SwiftBaseVisitor<Void> buildVisitor(RuleContext ruleCtx) {
|
||||
return new SwiftBaseVisitor<Void>() {
|
||||
|
||||
if (shouldIncludeUnavailableModifier(ctx.functionBody().codeBlock())) {
|
||||
final SwiftParser.AttributesContext attributes = ctx.functionHead().attributes();
|
||||
if (attributes == null || !hasUnavailableModifier(attributes.attribute())) {
|
||||
addViolation(data, ctx);
|
||||
@Override
|
||||
public Void visitFunctionDeclaration(final FunctionDeclarationContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (shouldIncludeUnavailableModifier(ctx.functionBody().codeBlock())) {
|
||||
final SwiftParser.AttributesContext attributes = ctx.functionHead().attributes();
|
||||
if (attributes == null || !hasUnavailableModifier(attributes.attribute())) {
|
||||
addViolation(ruleCtx, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void visitInitializerDeclaration(final InitializerDeclarationContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitInitializerDeclaration(final InitializerDeclarationContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
if (shouldIncludeUnavailableModifier(ctx.initializerBody().codeBlock())) {
|
||||
final SwiftParser.AttributesContext attributes = ctx.initializerHead().attributes();
|
||||
if (attributes == null || !hasUnavailableModifier(attributes.attribute())) {
|
||||
addViolation(ruleCtx, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldIncludeUnavailableModifier(ctx.initializerBody().codeBlock())) {
|
||||
final SwiftParser.AttributesContext attributes = ctx.initializerHead().attributes();
|
||||
if (attributes == null || !hasUnavailableModifier(attributes.attribute())) {
|
||||
addViolation(data, ctx);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
private boolean shouldIncludeUnavailableModifier(final SwiftParser.CodeBlockContext ctx) {
|
||||
if (ctx == null || ctx.statements() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<SwiftParser.StatementContext> statements = ctx.statements().statement();
|
||||
|
||||
return statements.size() == 1 && FATAL_ERROR.equals(statements.get(0).getStart().getText());
|
||||
}
|
||||
|
||||
private boolean hasUnavailableModifier(final List<SwiftParser.AttributeContext> attributes) {
|
||||
return attributes.stream().anyMatch(atr -> AVAILABLE_UNAVAILABLE.equals(atr.getText()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean shouldIncludeUnavailableModifier(final SwiftParser.CodeBlockContext ctx) {
|
||||
if (ctx == null || ctx.statements() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<SwiftParser.StatementContext> statements = ctx.statements().statement();
|
||||
|
||||
return statements.size() == 1 && FATAL_ERROR.equals(statements.get(0).getStart().getText());
|
||||
}
|
||||
|
||||
private boolean hasUnavailableModifier(final List<SwiftParser.AttributeContext> attributes) {
|
||||
return attributes.stream().anyMatch(atr -> AVAILABLE_UNAVAILABLE.equals(atr.getText()));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user