|
From: Christopher W. <caw...@us...> - 2006-05-17 02:41:13
|
Update of /cvsroot/rubyeclipse/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/parser In directory sc8-pr-cvs9.sourceforge.net:/tmp/cvs-serv24172/src/org/rubypeople/rdt/internal/core/parser Added Files: RubyLintVisitor.java ParserOptions.java InOrderVisitor.java Log Message: Add support for a Ruby "Lint" like AST visitor which will create semantic analysis and warnings ala JDT --- NEW FILE: InOrderVisitor.java --- /* * Author: C.Williams * * Copyright (c) 2004 RubyPeople. * * This file is part of the Ruby Development Tools (RDT) plugin for eclipse. You * can get copy of the GPL along with further information about RubyPeople and * third party software bundled with RDT in the file * org.rubypeople.rdt.core_x.x.x/RDT.license or otherwise at * http://www.rubypeople.org/RDT.license. * * RDT is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * RDT is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. [...1135 lines suppressed...] * (non-Javadoc) * * @see org.jruby.ast.visitor.NodeVisitor#visitZSuperNode(org.jruby.ast.ZSuperNode) */ public Instruction visitZSuperNode(ZSuperNode iVisited) { handleNode(iVisited); return null; } protected Instruction handleNode(Node visited) { return null; } @Override protected Instruction visitNode(Node iVisited) { // TODO Auto-generated method stub return null; } } --- NEW FILE: RubyLintVisitor.java --- package org.rubypeople.rdt.internal.core.parser; import java.util.HashSet; import java.util.Set; import org.jruby.ast.BlockNode; import org.jruby.ast.CallNode; import org.jruby.ast.ConstDeclNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.FCallNode; import org.jruby.ast.FalseNode; import org.jruby.ast.IfNode; import org.jruby.ast.IterNode; import org.jruby.ast.NilNode; import org.jruby.ast.Node; import org.jruby.ast.ScopeNode; import org.jruby.ast.TrueNode; import org.jruby.ast.WhenNode; import org.jruby.evaluator.Instruction; import org.jruby.lexer.yacc.ISourcePosition; import org.rubypeople.rdt.core.IProblemRequestor; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.parser.IProblem; public class RubyLintVisitor extends InOrderVisitor { private IProblemRequestor problemRequestor; private Set assignedConstants; private Set methodsCalled; public RubyLintVisitor(IProblemRequestor problemRequestor) { this.problemRequestor = problemRequestor; assignedConstants = new HashSet(); methodsCalled = new HashSet(); } public Instruction visitFCallNode(FCallNode iVisited) { methodsCalled.add(iVisited.getName()); return super.visitFCallNode(iVisited); } public Instruction visitCallNode(CallNode iVisited) { methodsCalled.add(iVisited.getName()); return super.visitCallNode(iVisited); } public Instruction visitIfNode(IfNode iVisited) { Node condition = iVisited.getCondition(); if (condition instanceof TrueNode) { problemRequestor.acceptProblem(new Warning(iVisited.getPosition(), "Condition is always true")); } else if ((condition instanceof FalseNode) || (condition instanceof NilNode)) { problemRequestor.acceptProblem(new Warning(iVisited.getPosition(), "Condition is always false")); } if (iVisited.getThenBody() == null) { IProblem problem = createProblem( RubyCore.COMPILER_PB_EMPTY_STATEMENT, iVisited .getPosition(), "Empty Conditional Body"); if (problem != null) problemRequestor.acceptProblem(problem); } return super.visitIfNode(iVisited); } public Instruction visitWhenNode(WhenNode iVisited) { if (iVisited.getBodyNode() == null) { IProblem problem = createProblem( RubyCore.COMPILER_PB_EMPTY_STATEMENT, iVisited .getPosition(), "Empty When Body"); if (problem != null) problemRequestor.acceptProblem(problem); } return super.visitWhenNode(iVisited); } public Instruction visitBlockNode(BlockNode iVisited) { return super.visitBlockNode(iVisited); } public Instruction visitIterNode(IterNode iVisited) { if (iVisited.getBodyNode() == null) { IProblem problem = createProblem( RubyCore.COMPILER_PB_EMPTY_STATEMENT, iVisited .getPosition(), "Empty Block"); if (problem != null) problemRequestor.acceptProblem(problem); } return super.visitIterNode(iVisited); } public Instruction visitDefnNode(DefnNode iVisited) { // TODO Analyze method visibility. Create warning for uncalled private // methods ScopeNode scope = iVisited.getBodyNode(); if (scope.getBodyNode() == null) { IProblem problem = createProblem( RubyCore.COMPILER_PB_EMPTY_STATEMENT, iVisited .getPosition(), "Empty Method Definition"); if (problem != null) problemRequestor.acceptProblem(problem); } return super.visitDefnNode(iVisited); } public Instruction visitDefsNode(DefsNode iVisited) { ScopeNode scope = iVisited.getBodyNode(); if (scope.getBodyNode() == null) { IProblem problem = createProblem( RubyCore.COMPILER_PB_EMPTY_STATEMENT, iVisited .getPosition(), "Empty Method Definition"); if (problem != null) problemRequestor.acceptProblem(problem); } return super.visitDefsNode(iVisited); } protected Instruction handleNode(Node visited) { System.out.println(visited.toString() + ", position -> " + visited.getPosition()); return super.handleNode(visited); } public Instruction visitConstDeclNode(ConstDeclNode iVisited) { String name = iVisited.getName(); if (assignedConstants.contains(name)) { problemRequestor.acceptProblem(new Warning(iVisited.getPosition(), "Reassignment of a constant")); } else assignedConstants.add(name); return super.visitConstDeclNode(iVisited); } private IProblem createProblem(String compilerOption, ISourcePosition position, String message) { String value = RubyCore.getOption(compilerOption); if (value.equals(RubyCore.WARNING)) return new Warning(position, message); if (value.equals(RubyCore.ERROR)) return new Error(position, message); return null; } } --- NEW FILE: ParserOptions.java --- package org.rubypeople.rdt.internal.core.parser; import java.util.HashMap; import java.util.Map; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.internal.core.util.CharOperation; public class ParserOptions { public static final long EnsureBlockNotCompleting = 0x1; public static final long UnusedLocalVariable = 0x2; public static final long UnusedPrivateMember = 0x4; public static final long MaskedRescueBlock = 0x8; public static final long UnusedArgument = 0x10; public static final long EmptyStatement = 0x20; public static final long UnnecessaryElse = 0x40; public static final long NullReference = 0x80; public static final long FallthroughCase = 0x100; public static final String ERROR = RubyCore.ERROR; public static final String WARNING = RubyCore.WARNING; public static final String IGNORE = RubyCore.IGNORE; public static final String ENABLED = RubyCore.ENABLED; public static final String DISABLED = RubyCore.DISABLED; // Default severity level for handlers public long errorThreshold = 0; public long warningThreshold = EnsureBlockNotCompleting | UnusedLocalVariable | UnusedPrivateMember /*| NullReference -- keep RubyCore#getDefaultOptions comment in sync */; // source encoding format public String defaultEncoding = null; // will use the platform default encoding // tags used to recognize tasks in comments public char[][] taskTags = null; public char[][] taskPriorites = null; public boolean isTaskCaseSensitive = true; public Map getMap() { Map optionsMap = new HashMap(30); optionsMap.put(RubyCore.COMPILER_PB_HIDDEN_RESCUE_BLOCK, getSeverityString(MaskedRescueBlock)); optionsMap.put(RubyCore.COMPILER_PB_UNUSED_LOCAL, getSeverityString(UnusedLocalVariable)); optionsMap.put(RubyCore.COMPILER_PB_UNUSED_PARAMETER, getSeverityString(UnusedArgument)); optionsMap.put(RubyCore.COMPILER_PB_UNUSED_PRIVATE_MEMBER, getSeverityString(UnusedPrivateMember)); optionsMap.put(RubyCore.COMPILER_PB_EMPTY_STATEMENT, getSeverityString(EmptyStatement)); optionsMap.put(RubyCore.COMPILER_PB_UNNECESSARY_ELSE, getSeverityString(UnnecessaryElse)); optionsMap.put(RubyCore.COMPILER_PB_ENSURE_BLOCK_NOT_COMPLETING, getSeverityString(EnsureBlockNotCompleting)); if (this.defaultEncoding != null) { optionsMap.put(RubyCore.CORE_ENCODING, this.defaultEncoding); } optionsMap.put(RubyCore.COMPILER_TASK_TAGS, this.taskTags == null ? "" : new String(CharOperation.concatWith(this.taskTags,','))); //$NON-NLS-1$ optionsMap.put(RubyCore.COMPILER_TASK_PRIORITIES, this.taskPriorites == null ? "" : new String(CharOperation.concatWith(this.taskPriorites,','))); //$NON-NLS-1$ optionsMap.put(RubyCore.COMPILER_TASK_CASE_SENSITIVE, this.isTaskCaseSensitive ? ENABLED : DISABLED); optionsMap.put(RubyCore.COMPILER_PB_NULL_REFERENCE, getSeverityString(NullReference)); optionsMap.put(RubyCore.COMPILER_PB_FALLTHROUGH_CASE, getSeverityString(FallthroughCase)); return optionsMap; } public String getSeverityString(long irritant) { if((this.warningThreshold & irritant) != 0) return WARNING; if((this.errorThreshold & irritant) != 0) return ERROR; return IGNORE; } } |