Transform antlr visitor to be compatible with #2589

This commit is contained in:
Clément Fournier
2020-06-17 20:25:56 +02:00
parent aa1acb068b
commit 6c053b5edd
16 changed files with 171 additions and 89 deletions

View File

@ -15,29 +15,103 @@
<property name="target-package-dir" value="${antlr4.outputDirectory}/net/sourceforge/pmd/lang/${lang-terse-name}/ast"/> <property name="target-package-dir" value="${antlr4.outputDirectory}/net/sourceforge/pmd/lang/${lang-terse-name}/ast"/>
<property name="lang-ast-package" value="net.sourceforge.pmd.lang.${lang-terse-name}.ast" />
<property name="ast-api-package" value="net.sourceforge.pmd.lang.ast" />
<property name="ast-impl-package" value="${ast-api-package}.impl.antlr4" />
<property name="parser-name" value="${lang-name}Parser"/>
<property name="parser-file" value="${target-package-dir}/${parser-name}.java"/>
<property name="visitor-name" value="${lang-name}Visitor"/>
<property name="visitor-file" value="${target-package-dir}/${visitor-name}.java"/>
<property name="base-visitor-name" value="${lang-name}BaseVisitor"/>
<property name="base-visitor-file" value="${target-package-dir}/${base-visitor-name}.java"/>
<property name="node-itf-name" value="${lang-name}Node"/>
<property name="base-class-name" value="Abstract${lang-name}Node"/>
<target name="adapt-antlr-sources" description="Adapt antlr sources to the PMD codebase"> <target name="adapt-antlr-sources" description="Adapt antlr sources to the PMD codebase">
<replace file="${target-package-dir}/${lang-name}Parser.java" <!-- Adapt parser. -->
token="${root-node-name}Context extends ${lang-name}InnerNode" <replace file="${parser-file}">
<replacefilter token="${root-node-name}Context extends ${lang-name}InnerNode"
value="${root-node-name}Context extends ${lang-name}InnerNode implements net.sourceforge.pmd.lang.ast.RootNode"/> value="${root-node-name}Context extends ${lang-name}InnerNode implements net.sourceforge.pmd.lang.ast.RootNode"/>
<replace file="${target-package-dir}/${lang-name}Parser.java" <replacefilter token="_ctx = _localctx;"
token="_ctx = _localctx;"
value="_ctx = _localctx.asAntlrNode();"/> value="_ctx = _localctx.asAntlrNode();"/>
<replace file="${target-package-dir}/${lang-name}Parser.java" <replacefilter token="public &lt;T> T accept(ParseTreeVisitor&lt;? extends T> visitor)"
token="else return visitor.visitChildren(this);" value="public &lt;P, R> R acceptVisitor(AstVisitor&lt;? super P, ? extends R> visitor, P data)" />
value="
else if (visitor instanceof PmdAntlrVisitor) return ((PmdAntlrVisitor&lt;? extends T>) visitor).visitChildren(this);
else return visitor.visitChildren(asAntlrNode());"/>
<replaceregexp flags="g" file="${target-package-dir}/${lang-name}Parser.java"> <replacefilter token="((${visitor-name}&lt;? extends T>)visitor)"
value="((${visitor-name}&lt;? super P, ? extends R>) visitor)" />
<replacefilter token="return visitor.visitChildren(this);"
value="return visitor.visitNode(this, data);" />
</replace>
<replaceregexp flags="g" file="${parser-file}">
<regexp pattern="\.visit(\w++)\(this\);"/>
<substitution expression=".visit\1(this, data);"/>
</replaceregexp>
<replaceregexp flags="g" file="${parser-file}">
<regexp pattern="_sempred\(\((\w+)\)_localctx,"/> <regexp pattern="_sempred\(\((\w+)\)_localctx,"/>
<substitution expression="_sempred\((\1) asPmdNode(_localctx),"/> <substitution expression="_sempred\((\1) asPmdNode(_localctx),"/>
</replaceregexp> </replaceregexp>
<replace file="${target-package-dir}/${lang-name}Visitor.java" <!-- Transform the visitor to PMD-style. -->
token="extends ParseTreeVisitor" <replace file="${visitor-file}">
value="extends net.sourceforge.pmd.lang.ast.impl.antlr4.PmdAntlrVisitor"/>
<replacefilter token="Visitor&lt;T> extends ParseTreeVisitor&lt;T> {"
value="Visitor&lt;P, R> extends net.sourceforge.pmd.lang.ast.AstVisitor&lt;P, R> {&#10;
&#10;
/**&#10;
* The default visit method for ${lang-name} nodes. Unless overridden,&#10;
* the default implementations of the methods of this interface delegate&#10;
* to this method. The default calls {@link #visitNode(Node, Object)}.&#10;
* &#10;
* @param node Node to visit&#10;
* @param data Parameter of the visit&#10;
* @return Result of the visit&#10;
*/&#10;
default R visit${node-itf-name}(${node-itf-name} node, P data) { return visitNode(node, data); }&#10;&#10;
&#10;
"/>
<replacefilter token="T visit" value="default R visit"/>
<replacefilter token="ctx);" value="node, P data) { return visit${node-itf-name}(node, data); }"/>
</replace>
<replace file="${visitor-file}">
<replacefilter token="Visitor&lt;T> extends ParseTreeVisitor&lt;T> {"
value="Visitor&lt;P, R> extends net.sourceforge.pmd.lang.ast.AstVisitor&lt;P, R> {&#10;
&#10;
/**&#10;
* The default visit method for ${lang-name} nodes. Unless overridden,&#10;
* the default implementations of the methods of this interface delegate&#10;
* to this method. The default calls {@link #visitNode(Node, Object)}.&#10;
* &#10;
* @param node Node to visit&#10;
* @param data Parameter of the visit&#10;
* @return Result of the visit&#10;
*/&#10;
default R visit${node-itf-name}(${node-itf-name} node, P data) { return visitNode(node, data); }&#10;&#10;
&#10;
"/>
<replacefilter token="T visit" value="default R visit"/>
<replacefilter token="ctx);" value="node, P data) { return visit${node-itf-name}(node, data); }"/>
</replace>
<!-- This is in the main sources (not much to do). -->
<delete file="${base-visitor-file}" />
</target> </target>
</project> </project>

View File

@ -0,0 +1,12 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.ast;
// The AstVisitor of #2589
public interface AstVisitor<P, R> {
R visitNode(Node node, P param);
}

View File

@ -0,0 +1,22 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.ast;
// The AstVisitorBase of #2589 (simplified, keep the other instead)
public abstract class AstVisitorBase<P, R> implements AstVisitor<P, R> {
protected R visitChildren(Node node, P data) {
for (Node child : node.children()) {
child.acceptVisitor(this, data);
}
return null;
}
@Override
public R visitNode(Node node, P param) {
return visitChildren(node, param);
}
}

View File

@ -244,6 +244,7 @@ public interface Node {
} }
} }
/** /**
* Returns a data map used to store additional information on this node. * Returns a data map used to store additional information on this node.
* *
@ -251,6 +252,13 @@ public interface Node {
*/ */
DataMap<DataKey<?, ?>> getUserMap(); DataMap<DataKey<?, ?>> getUserMap();
// The acceptVisitor of #2589
default <P, R> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visitNode(this, data);
}
/** /**
* Returns the parent of this node, or null if this is the {@linkplain RootNode root} * Returns the parent of this node, or null if this is the {@linkplain RootNode root}
* of the tree. * of the tree.
@ -259,6 +267,7 @@ public interface Node {
*/ */
Node getParent(); Node getParent();
/** /**
* Returns the child of this node at the given index. * Returns the child of this node at the given index.
* *

View File

@ -6,9 +6,8 @@ package net.sourceforge.pmd.lang.ast.impl.antlr4;
import java.util.List; import java.util.List;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.lang.rule.AbstractRule;
@ -23,13 +22,11 @@ public abstract class AntlrBaseRule extends AbstractRule {
@Override @Override
public void apply(List<? extends Node> nodes, RuleContext ctx) { public void apply(List<? extends Node> nodes, RuleContext ctx) {
ParseTreeVisitor<Void> visitor = buildVisitor(ctx); AstVisitor<RuleContext, ?> visitor = buildVisitor();
assert visitor != null : "Rule should provide a non-null visitor"; assert visitor != null : "Rule should provide a non-null visitor";
for (Node node : nodes) { for (Node node : nodes) {
assert node instanceof BaseAntlrNode : "Incorrect node type " + node + " passed to " + this; node.acceptVisitor(visitor, ctx);
((BaseAntlrNode<?, ?>) node).accept(visitor);
} }
} }
@ -38,10 +35,8 @@ public abstract class AntlrBaseRule extends AbstractRule {
* This visitor should explore the nodes it's interested in and report * This visitor should explore the nodes it's interested in and report
* violations on the given rule context. * violations on the given rule context.
* *
* @param ruleCtx Object that accumulates rule violations
*
* @return A visitor bound to the given rule context * @return A visitor bound to the given rule context
*/ */
public abstract ParseTreeVisitor<Void> buildVisitor(RuleContext ruleCtx); public abstract AstVisitor<RuleContext, ?> buildVisitor();
} }

View File

@ -4,8 +4,6 @@
package net.sourceforge.pmd.lang.ast.impl.antlr4; package net.sourceforge.pmd.lang.ast.impl.antlr4;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import net.sourceforge.pmd.lang.ast.impl.GenericNode; import net.sourceforge.pmd.lang.ast.impl.GenericNode;
/** /**
@ -13,6 +11,4 @@ import net.sourceforge.pmd.lang.ast.impl.GenericNode;
*/ */
public interface AntlrNode<N extends AntlrNode<N>> extends GenericNode<N> { public interface AntlrNode<N extends AntlrNode<N>> extends GenericNode<N> {
<T> T accept(ParseTreeVisitor<? extends T> visitor);
} }

View File

@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.ast.impl.antlr4; package net.sourceforge.pmd.lang.ast.impl.antlr4;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
public abstract class BaseAntlrErrorNode<N extends AntlrNode<N>> extends BaseAntlrTerminalNode<N> { public abstract class BaseAntlrErrorNode<N extends AntlrNode<N>> extends BaseAntlrTerminalNode<N> {
@ -20,12 +19,6 @@ public abstract class BaseAntlrErrorNode<N extends AntlrNode<N>> extends BaseAnt
} }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor.visitErrorNode(asAntlrNode());
}
@Override @Override
public @NonNull String getText() { public @NonNull String getText() {
return getFirstAntlrToken().getText(); return getFirstAntlrToken().getText();

View File

@ -102,12 +102,6 @@ public abstract class BaseAntlrInnerNode<N extends AntlrNode<N>> extends BaseAnt
// default does nothing // default does nothing
} }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor.visitChildren(asAntlrNode());
}
protected static class PmdAsAntlrInnerNode<N extends AntlrNode<N>> extends ParserRuleContext implements RuleNode, AntlrToPmdParseTreeAdapter<N> { protected static class PmdAsAntlrInnerNode<N extends AntlrNode<N>> extends ParserRuleContext implements RuleNode, AntlrToPmdParseTreeAdapter<N> {
private final BaseAntlrInnerNode<N> pmdNode; private final BaseAntlrInnerNode<N> pmdNode;
@ -154,9 +148,7 @@ public abstract class BaseAntlrInnerNode<N extends AntlrNode<N>> extends BaseAnt
@Override @Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) { public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
// note: this delegate to the PMD node, giving throw new UnsupportedOperationException("Cannot visit the underlying antlr nodes");
// control of the visit back to the first-class nodes
return pmdNode.accept(visitor);
} }
} }
} }

View File

@ -62,11 +62,6 @@ public abstract class BaseAntlrTerminalNode<N extends AntlrNode<N>>
return 0; return 0;
} }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor.visitTerminal(asAntlrNode());
}
protected int getTokenKind() { protected int getTokenKind() {
return antlrNode.symbol.getTokenIndex(); return antlrNode.symbol.getTokenIndex();
} }

View File

@ -1,27 +0,0 @@
/*
* 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.tree.ParseTreeVisitor;
import org.antlr.v4.runtime.tree.RuleNode;
/**
* Interface for parse tree visitors that can also visit PMD nodes. The
* antlr rewrite makes the generated visitor interface extend this.
*/
public interface PmdAntlrVisitor<T> extends ParseTreeVisitor<T> {
@Override
T visitChildren(RuleNode node);
/**
* This is added for compatibility.
*/
default T visitChildren(BaseAntlrInnerNode<?> node) {
return visitChildren(node.asAntlrNode());
}
}

View File

@ -35,6 +35,7 @@ grammar Swift;
@header { @header {
import net.sourceforge.pmd.lang.ast.impl.antlr4.*; import net.sourceforge.pmd.lang.ast.impl.antlr4.*;
import net.sourceforge.pmd.lang.ast.AstVisitor;
} }

View File

@ -4,9 +4,8 @@
package net.sourceforge.pmd.lang.swift; package net.sourceforge.pmd.lang.swift;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrBaseRule; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrBaseRule;
public abstract class AbstractSwiftRule extends AntlrBaseRule { public abstract class AbstractSwiftRule extends AntlrBaseRule {
@ -16,5 +15,5 @@ public abstract class AbstractSwiftRule extends AntlrBaseRule {
} }
@Override @Override
public abstract ParseTreeVisitor<Void> buildVisitor(RuleContext ruleCtx); public abstract AstVisitor<RuleContext, ?> buildVisitor();
} }

View File

@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.swift.ast;
import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.ParserRuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrInnerNode; import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrInnerNode;
public abstract class SwiftInnerNode public abstract class SwiftInnerNode
@ -19,6 +20,16 @@ public abstract class SwiftInnerNode
super(parent, invokingStateNumber); super(parent, invokingStateNumber);
} }
@Override
public <P, R> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data) {
if (visitor instanceof SwiftVisitor) {
// some of the generated antlr nodes have no accept method...
return ((SwiftVisitor<? super P, ? extends R>) visitor).visitSwiftNode(this, data);
}
return visitor.visitNode(this, data);
}
@Override // override to make visible in package @Override // override to make visible in package
protected PmdAsAntlrInnerNode<SwiftNode> asAntlrNode() { protected PmdAsAntlrInnerNode<SwiftNode> asAntlrNode() {
return super.asAntlrNode(); return super.asAntlrNode();

View File

@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.swift.ast; package net.sourceforge.pmd.lang.swift.ast;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrTerminalNode; import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrTerminalNode;
@ -28,8 +27,4 @@ public final class SwiftTerminalNode extends BaseAntlrTerminalNode<SwiftNode> im
return SwiftParser.DICO.getXPathNameOfToken(getFirstAntlrToken().getType()); return SwiftParser.DICO.getXPathNameOfToken(getFirstAntlrToken().getType());
} }
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor.visitTerminal(asAntlrNode());
}
} }

View File

@ -0,0 +1,14 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.swift.ast;
import net.sourceforge.pmd.lang.ast.AstVisitorBase;
/**
* Base class for swift visitors.
*/
public abstract class SwiftVisitorBase<P, R> extends AstVisitorBase<P, R> implements SwiftVisitor<P, R> {
}

View File

@ -7,11 +7,12 @@ package net.sourceforge.pmd.lang.swift.rule.bestpractices;
import java.util.List; import java.util.List;
import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.swift.AbstractSwiftRule; 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;
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.FunctionDeclarationContext; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.FunctionDeclarationContext;
import net.sourceforge.pmd.lang.swift.ast.SwiftParser.InitializerDeclarationContext; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.InitializerDeclarationContext;
import net.sourceforge.pmd.lang.swift.ast.SwiftVisitorBase;
public class UnavailableFunctionRule extends AbstractSwiftRule { public class UnavailableFunctionRule extends AbstractSwiftRule {
@ -25,11 +26,11 @@ public class UnavailableFunctionRule extends AbstractSwiftRule {
} }
@Override @Override
public SwiftBaseVisitor<Void> buildVisitor(RuleContext ruleCtx) { public AstVisitor<RuleContext, ?> buildVisitor() {
return new SwiftBaseVisitor<Void>() { return new SwiftVisitorBase<RuleContext, Void>() {
@Override @Override
public Void visitFunctionDeclaration(final FunctionDeclarationContext ctx) { public Void visitFunctionDeclaration(final FunctionDeclarationContext ctx, RuleContext ruleCtx) {
if (ctx == null) { if (ctx == null) {
return null; return null;
} }
@ -45,7 +46,7 @@ public class UnavailableFunctionRule extends AbstractSwiftRule {
} }
@Override @Override
public Void visitInitializerDeclaration(final InitializerDeclarationContext ctx) { public Void visitInitializerDeclaration(final InitializerDeclarationContext ctx, RuleContext ruleCtx) {
if (ctx == null) { if (ctx == null) {
return null; return null;
} }