package com.aptana.rdt.internal.parser.warnings; import java.util.HashSet; import java.util.Set; import org.jruby.ast.ClassVarAsgnNode; import org.jruby.ast.ConstDeclNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.GlobalAsgnNode; import org.jruby.ast.IfNode; import org.jruby.ast.InstAsgnNode; import org.jruby.ast.ListNode; import org.jruby.ast.LocalAsgnNode; import org.jruby.ast.Node; import org.jruby.ast.WhenNode; import org.jruby.lexer.yacc.IDESourcePosition; import org.jruby.lexer.yacc.ISourcePosition; import org.rubypeople.rdt.core.parser.warnings.RubyLintVisitor; import com.aptana.rdt.AptanaRDTPlugin; import com.aptana.rdt.IProblem; public class AccidentalBooleanAssignmentVisitor extends RubyLintVisitor { private Set<String> locals = new HashSet<String>(); public AccidentalBooleanAssignmentVisitor(String contents) { super(AptanaRDTPlugin.getDefault().getOptions(), contents); } @Override protected String getOptionKey() { return AptanaRDTPlugin.COMPILER_PB_POSSIBLE_ACCIDENTAL_BOOLEAN_ASSIGNMENT; } @Override public Object visitLocalAsgnNode(LocalAsgnNode iVisited) { locals.add(iVisited.getName()); return super.visitLocalAsgnNode(iVisited); } @Override public Object visitDefnNode(DefnNode iVisited) { locals.clear(); return super.visitDefnNode(iVisited); } @Override public Object visitDefsNode(DefsNode iVisited) { locals.clear(); return super.visitDefsNode(iVisited); } public Object visitIfNode(IfNode iVisited) { Node condition = iVisited.getCondition(); checkCondition(condition); return super.visitIfNode(iVisited); } private void checkCondition(Node condition) { if (containsAssignment(condition)) { // Only create a problem if we've seen the local before (new locals assigned in an if are bad, but very // common. If we've seen it before, this is bad news!) if (condition instanceof LocalAsgnNode) { LocalAsgnNode assign = (LocalAsgnNode) condition; if (!locals.contains(assign.getName())) return; } ISourcePosition original = condition.getPosition(); IDESourcePosition position = new IDESourcePosition(original.getFile(), original.getStartLine(), original .getEndLine(), original.getStartOffset(), original.getEndOffset() - 1); createProblem(position, "Possible accidental boolean assignment"); } } private boolean containsAssignment(Node condition) { // FIXME Dive into this node branch recursively and see if it contains any assignment nodes return condition instanceof LocalAsgnNode || condition instanceof GlobalAsgnNode || condition instanceof InstAsgnNode || condition instanceof ClassVarAsgnNode || condition instanceof ConstDeclNode; } @Override public Object visitWhenNode(WhenNode visited) { Node expressions = visited.getExpressionNodes(); if (expressions instanceof ListNode) { ListNode list = (ListNode) expressions; for (Node expression : list.childNodes()) { checkCondition(expression); } } else { checkCondition(expressions); } return super.visitWhenNode(visited); } @Override protected int getProblemID() { return IProblem.PossibleAccidentalBooleanAssignment; } }