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:
Clément Fournier
2020-01-17 12:37:06 +01:00
parent ecef88aa0a
commit db424fc1e6
14 changed files with 395 additions and 258 deletions

View File

@ -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();
}
/**

View File

@ -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);
}

View File

@ -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 ?

View File

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

View File

@ -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);
}

View File

@ -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");
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

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

View File

@ -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 ;

View File

@ -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);
}

View File

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

View File

@ -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()));
}
}