package com.aptana.rdt.internal.parser.warnings; import java.util.ArrayList; import java.util.List; import org.jruby.ast.ClassNode; import org.jruby.ast.DefnNode; import org.jruby.ast.Node; import org.jruby.ast.SuperNode; import org.jruby.ast.ZSuperNode; import org.rubypeople.rdt.core.parser.warnings.RubyLintVisitor; import com.aptana.rdt.AptanaRDTPlugin; public class SubclassCallsSuper extends RubyLintVisitor { private boolean inConstructor; private boolean calledSuper; private List<Boolean> subclassStack = new ArrayList<Boolean>(); public SubclassCallsSuper(String contents) { super(AptanaRDTPlugin.getDefault().getOptions(), contents); subclassStack.add(Boolean.FALSE); // top level should be marked as not subclass } @Override protected String getOptionKey() { return AptanaRDTPlugin.COMPILER_PB_SUBCLASS_DOESNT_CALL_SUPER; } @Override public Object visitClassNode(ClassNode iVisited) { Node superNode = iVisited.getSuperNode(); if (superNode != null) { subclassStack.add(Boolean.TRUE); } else { subclassStack.add(Boolean.FALSE); } return super.visitClassNode(iVisited); } @Override public void exitClassNode(ClassNode iVisited) { subclassStack.remove(subclassStack.size() - 1); super.exitClassNode(iVisited); } @Override public Object visitDefnNode(DefnNode iVisited) { if (!isSubClass()) return null; if (!iVisited.getName().equals("initialize")) return null; inConstructor = true; calledSuper = false; return super.visitDefnNode(iVisited); } private boolean isSubClass() { return subclassStack.get(subclassStack.size() - 1); } @Override public Object visitSuperNode(SuperNode iVisited) { if (inConstructor) calledSuper = true; return super.visitSuperNode(iVisited); } @Override public Object visitZSuperNode(ZSuperNode iVisited) { if (inConstructor) calledSuper = true; return super.visitZSuperNode(iVisited); } @Override public void exitDefnNode(DefnNode iVisited) { if (!isSubClass()) return; if (!iVisited.getName().equals("initialize")) return; if (!calledSuper) { createProblem(iVisited.getNameNode().getPosition(), "Subclass does not call super in constructor"); } inConstructor = false; super.exitDefnNode(iVisited); } }