package com.aptana.rdt.internal.parser.warnings; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.jruby.ast.ClassNode; import org.jruby.ast.DefnNode; import org.jruby.ast.FCallNode; import org.jruby.ast.LocalAsgnNode; import org.jruby.ast.Node; import org.jruby.ast.RootNode; import org.rubypeople.rdt.core.parser.warnings.RubyLintVisitor; import org.rubypeople.rdt.internal.core.util.ASTUtil; import com.aptana.rdt.AptanaRDTPlugin; import com.aptana.rdt.IProblem; public class LocalsMaskingMethodsVisitor extends RubyLintVisitor { private Map<Node, Collection<LocalAsgnNode>> locals; private HashSet<String> methods; private List<Node> scopes; public LocalsMaskingMethodsVisitor(String contents) { super(AptanaRDTPlugin.getDefault().getOptions(), contents); init(); } private void init() { locals = new HashMap<Node, Collection<LocalAsgnNode>>(); methods = new HashSet<String>(); scopes = new ArrayList<Node>(); } @Override public Object visitRootNode(RootNode visited) { init(); enterScope(visited); Object ret = super.visitRootNode(visited); exitScope(); return ret; } private void enterScope(Node scopingNode) { scopes.add(scopingNode); } private void exitScope() { scopes.remove(scopes.size() - 1); } @Override protected String getOptionKey() { return AptanaRDTPlugin.COMPILER_PB_LOCAL_MASKS_METHOD; } @Override protected int getProblemID() { return IProblem.LocalMaskingMethod; } public Object visitClassNode(ClassNode iVisited) { methods.clear(); locals.clear(); enterScope(iVisited); Object ret = super.visitClassNode(iVisited); findMaskingLocals(); exitScope(); return ret; } @Override public Object visitFCallNode(FCallNode iVisited) { String name = iVisited.getName(); if (name.equals("attr_accessor") || name.equals("attr")) { List<String> args = ASTUtil.getArgumentsFromFunctionCall(iVisited); if (name.equals("attr")) { methods.add(args.get(0)); } else methods.addAll(convertSymbolsToStrings(args)); } return super.visitFCallNode(iVisited); } private Collection<? extends String> convertSymbolsToStrings(List<String> args) { Collection<String> converted = new ArrayList<String>(); for (String arg : args) { if (arg.startsWith(":")) { converted.add(arg.substring(1)); } else { converted.add(arg); } } return converted; } public Object visitDefnNode(DefnNode iVisited) { methods.add(iVisited.getName()); enterScope(iVisited); Object ret = super.visitDefnNode(iVisited); exitScope(); return ret; } public Object visitLocalAsgnNode(LocalAsgnNode iVisited) { Collection<LocalAsgnNode> alreadyMarked = locals.get(currentScope()); if (alreadyMarked == null) alreadyMarked = new ArrayList<LocalAsgnNode>(); for (LocalAsgnNode localAsgnNode : alreadyMarked) { if (localAsgnNode.getName().equals(iVisited.getName())) return super.visitLocalAsgnNode(iVisited); } alreadyMarked.add(iVisited); locals.put(currentScope(), alreadyMarked); return super.visitLocalAsgnNode(iVisited); } private Node currentScope() { return scopes.get(scopes.size() - 1); } private void findMaskingLocals() { for (Collection<LocalAsgnNode> localAsgnNodes : locals.values()) { for (LocalAsgnNode local : localAsgnNodes) { if (methods.contains(local.getName())) { createProblem(local.getPosition(), "Local variable hides method"); } } } } }