package com.aptana.rdt.internal.parser.warnings; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.jruby.ast.CallNode; import org.jruby.ast.ClassNode; import org.jruby.ast.DefnNode; import org.jruby.ast.FCallNode; import org.jruby.ast.Node; import org.jruby.ast.SelfNode; import org.jruby.ast.SymbolNode; import org.jruby.ast.VCallNode; import org.jruby.runtime.Visibility; import org.rubypeople.rdt.core.compiler.IProblem; import org.rubypeople.rdt.core.parser.warnings.RubyLintVisitor; import org.rubypeople.rdt.internal.core.util.ASTUtil; import com.aptana.rdt.AptanaRDTPlugin; public class UnusedPrivateMethodVisitor extends RubyLintVisitor { private Map<String, Node> privateMethods = new HashMap<String, Node>(); private Set<String> usedMethods = new HashSet<String>(); private Visibility visibility; public UnusedPrivateMethodVisitor(String contents) { super(AptanaRDTPlugin.getDefault().getOptions(), contents); visibility = Visibility.PUBLIC; } public Object visitFCallNode(FCallNode iVisited) { usedMethods.add(iVisited.getName()); // we've used the method List<Node> args = ASTUtil.getArgumentNodesFromFunctionCall(iVisited); for (Node node : args) { if (node instanceof SymbolNode) { usedMethods.add(((SymbolNode) node).getName()); } } // FIXME Handle case where we call public/private/protected methods with arguments (so current visibility is not changed, but existing methods' visibility is changed) return null; } public Object visitCallNode(CallNode iVisited) { Node receiver = iVisited.getReceiverNode(); if (receiver instanceof SelfNode) usedMethods.add(iVisited.getName()); // we've used the method return null; } public Object visitVCallNode(VCallNode iVisited) { usedMethods.add(iVisited.getName()); // we've used the method if (iVisited.getName().equals("private")) { visibility = Visibility.PRIVATE; } else if (iVisited.getName().equals("protected")) { visibility = Visibility.PROTECTED; } else if (iVisited.getName().equals("public")) { visibility = Visibility.PUBLIC; } return null; } public Object visitClassNode(ClassNode iVisited) { privateMethods.clear(); usedMethods.clear(); visibility = Visibility.PUBLIC; return null; } public void exitClassNode(ClassNode iVisited) { for (String name : usedMethods) { if (privateMethods.containsKey(name)) { privateMethods.remove(name); } } for (Node method : privateMethods.values()) { createProblem(method.getPosition(), "Unused private method " + ASTUtil.getNameReflectively(method)); } } public Object visitDefnNode(DefnNode iVisited) { if (visibility.isPrivate()) { privateMethods.put(iVisited.getName(), iVisited); } return null; } @Override protected String getOptionKey() { return AptanaRDTPlugin.COMPILER_PB_UNUSED_PRIVATE_MEMBER; } @Override protected int getProblemID() { return IProblem.UnusedPrivateMethod; } }