package com.aptana.rdt.internal.parser.warnings; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jruby.ast.CaseNode; import org.jruby.ast.IfNode; import org.jruby.ast.NewlineNode; import org.jruby.ast.Node; import org.jruby.ast.ReturnNode; import org.jruby.ast.RootNode; import org.jruby.ast.WhenNode; import org.rubypeople.rdt.core.parser.warnings.RubyLintVisitor; import org.rubypeople.rdt.internal.core.parser.InOrderVisitor; import com.aptana.rdt.AptanaRDTPlugin; public class UnecessaryElseVisitor extends RubyLintVisitor { public UnecessaryElseVisitor(String contents) { super(AptanaRDTPlugin.getDefault().getOptions(), contents); } @Override protected String getOptionKey() { return AptanaRDTPlugin.COMPILER_PB_UNNECESSARY_ELSE; } @Override public Object visitIfNode(IfNode iVisited) { String src = getSource(iVisited); Node elseBody; Node thenBody; if (src.startsWith("unless")) { // then we need to "swap" the then and else nodes elseBody = iVisited.getThenBody(); thenBody = iVisited.getElseBody(); } else { elseBody = iVisited.getElseBody(); thenBody = iVisited.getThenBody(); } boolean isUnlessModifier = (iVisited.getThenBody() == null); if (elseBody != null && !isUnlessModifier) { if (alwaysExplicitReturn(thenBody)) { createProblem(elseBody.getPosition(), "Unnecessary else"); // $NON-NLS-1$ } } return super.visitIfNode(iVisited); } private boolean alwaysExplicitReturn(Node body) { if (body == null) return false; ReturnVisitor visitor = new ReturnVisitor(); body.accept(visitor); return visitor.alwaysExplicit(); } private class ReturnVisitor extends InOrderVisitor { private boolean implicit = false; private Set<ReturnVisitor> branches = new HashSet<ReturnVisitor>(); @Override protected Object visitNode(Node iVisited) { if (iVisited != null && !structuralNode(iVisited) && !branchingNode(iVisited) && !(iVisited instanceof ReturnNode)) { implicit = true; } return super.visitNode(iVisited); } private boolean structuralNode(Node visited) { return (visited instanceof RootNode) || (visited instanceof NewlineNode); } private boolean branchingNode(Node visited) { return (visited instanceof IfNode) || (visited instanceof CaseNode); } @Override public Object visitReturnNode(ReturnNode iVisited) { implicit = false; return null; } @Override public Object visitCaseNode(CaseNode iVisited) { List<Node> list = iVisited.getCases().childNodes(); WhenNode whenNode = (WhenNode) list.get(0); while (whenNode != null) { ReturnVisitor visitor = new ReturnVisitor(); whenNode.getBodyNode().accept(visitor); branches.add(visitor); whenNode = (WhenNode) whenNode.getNextCase(); } return null; } @Override public Object visitIfNode(IfNode iVisited) { if (iVisited.getThenBody() != null) { ReturnVisitor visitor = new ReturnVisitor(); iVisited.getThenBody().accept(visitor); branches.add(visitor); } if (iVisited.getElseBody() != null) { ReturnVisitor visitor = new ReturnVisitor(); iVisited.getElseBody().accept(visitor); branches.add(visitor); } else { implicit = true; } return null; } public boolean alwaysExplicit() { for (ReturnVisitor visitor : branches) { if (!visitor.alwaysExplicit()) return false; } return !implicit; } } }