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="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">
<replace file="${target-package-dir}/${lang-name}Parser.java"
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"/>
<!-- Adapt parser. -->
<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"/>
<replace file="${target-package-dir}/${lang-name}Parser.java"
token="_ctx = _localctx;"
value="_ctx = _localctx.asAntlrNode();"/>
<replacefilter token="_ctx = _localctx;"
value="_ctx = _localctx.asAntlrNode();"/>
<replace file="${target-package-dir}/${lang-name}Parser.java"
token="else return visitor.visitChildren(this);"
value="
else if (visitor instanceof PmdAntlrVisitor) return ((PmdAntlrVisitor&lt;? extends T>) visitor).visitChildren(this);
else return visitor.visitChildren(asAntlrNode());"/>
<replacefilter token="public &lt;T> T accept(ParseTreeVisitor&lt;? extends T> visitor)"
value="public &lt;P, R> R acceptVisitor(AstVisitor&lt;? super P, ? extends R> visitor, P data)" />
<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,"/>
<substitution expression="_sempred\((\1) asPmdNode(_localctx),"/>
</replaceregexp>
<replace file="${target-package-dir}/${lang-name}Visitor.java"
token="extends ParseTreeVisitor"
value="extends net.sourceforge.pmd.lang.ast.impl.antlr4.PmdAntlrVisitor"/>
<!-- Transform the visitor to PMD-style. -->
<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>
<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>
</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.
*
@ -251,6 +252,13 @@ public interface Node {
*/
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}
* of the tree.
@ -259,6 +267,7 @@ public interface Node {
*/
Node getParent();
/**
* 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 org.antlr.v4.runtime.tree.ParseTreeVisitor;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.AbstractRule;
@ -23,13 +22,11 @@ public abstract class AntlrBaseRule extends AbstractRule {
@Override
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";
for (Node node : nodes) {
assert node instanceof BaseAntlrNode : "Incorrect node type " + node + " passed to " + this;
((BaseAntlrNode<?, ?>) node).accept(visitor);
node.acceptVisitor(visitor, ctx);
}
}
@ -38,10 +35,8 @@ public abstract class AntlrBaseRule extends AbstractRule {
* 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 ParseTreeVisitor<Void> buildVisitor(RuleContext ruleCtx);
public abstract AstVisitor<RuleContext, ?> buildVisitor();
}

View File

@ -4,8 +4,6 @@
package net.sourceforge.pmd.lang.ast.impl.antlr4;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
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> {
<T> T accept(ParseTreeVisitor<? extends T> visitor);
}

View File

@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.ast.impl.antlr4;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.checkerframework.checker.nullness.qual.NonNull;
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
public @NonNull String getText() {
return getFirstAntlrToken().getText();

View File

@ -102,12 +102,6 @@ public abstract class BaseAntlrInnerNode<N extends AntlrNode<N>> extends BaseAnt
// 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> {
private final BaseAntlrInnerNode<N> pmdNode;
@ -154,9 +148,7 @@ public abstract class BaseAntlrInnerNode<N extends AntlrNode<N>> extends BaseAnt
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
// note: this delegate to the PMD node, giving
// control of the visit back to the first-class nodes
return pmdNode.accept(visitor);
throw new UnsupportedOperationException("Cannot visit the underlying antlr nodes");
}
}
}

View File

@ -62,11 +62,6 @@ public abstract class BaseAntlrTerminalNode<N extends AntlrNode<N>>
return 0;
}
@Override
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor.visitTerminal(asAntlrNode());
}
protected int getTokenKind() {
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 {
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;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrBaseRule;
public abstract class AbstractSwiftRule extends AntlrBaseRule {
@ -16,5 +15,5 @@ public abstract class AbstractSwiftRule extends AntlrBaseRule {
}
@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 net.sourceforge.pmd.lang.ast.AstVisitor;
import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrInnerNode;
public abstract class SwiftInnerNode
@ -19,6 +20,16 @@ public abstract class SwiftInnerNode
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
protected PmdAsAntlrInnerNode<SwiftNode> asAntlrNode() {
return super.asAntlrNode();

View File

@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.swift.ast;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.checkerframework.checker.nullness.qual.NonNull;
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());
}
@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 net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.AstVisitor;
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;
import net.sourceforge.pmd.lang.swift.ast.SwiftVisitorBase;
public class UnavailableFunctionRule extends AbstractSwiftRule {
@ -25,11 +26,11 @@ public class UnavailableFunctionRule extends AbstractSwiftRule {
}
@Override
public SwiftBaseVisitor<Void> buildVisitor(RuleContext ruleCtx) {
return new SwiftBaseVisitor<Void>() {
public AstVisitor<RuleContext, ?> buildVisitor() {
return new SwiftVisitorBase<RuleContext, Void>() {
@Override
public Void visitFunctionDeclaration(final FunctionDeclarationContext ctx) {
public Void visitFunctionDeclaration(final FunctionDeclarationContext ctx, RuleContext ruleCtx) {
if (ctx == null) {
return null;
}
@ -45,7 +46,7 @@ public class UnavailableFunctionRule extends AbstractSwiftRule {
}
@Override
public Void visitInitializerDeclaration(final InitializerDeclarationContext ctx) {
public Void visitInitializerDeclaration(final InitializerDeclarationContext ctx, RuleContext ruleCtx) {
if (ctx == null) {
return null;
}