/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.visit;
import polyglot.ast.*;
import polyglot.frontend.*;
import polyglot.main.Reporter;
import polyglot.types.SemanticException;
import polyglot.types.TypeSystem;
import polyglot.util.*;
/**
*/
public class ErrorHandlingVisitor extends NodeVisitor
{
protected boolean error;
protected Job job;
protected TypeSystem ts;
protected NodeFactory nf;
protected Reporter reporter;
public ErrorHandlingVisitor(Job job, TypeSystem ts, NodeFactory nf) {
super();
this.job = job;
this.ts = ts;
this.nf = nf;
this.reporter = ts.extensionInfo().getOptions().reporter;
}
public Job job() {
return job;
}
/**
* Part of the initialization done by begin() in an ErrorHandlingVisitor
* method is initializing the error-handling state.
*/
public NodeVisitor begin() {
this.error = false;
return super.begin();
}
/** Returns the <code>ErrorQueue</code> for the current Job.
*
* @see polyglot.util.ErrorQueue
*/
public ErrorQueue errorQueue() {
return job().compiler().errorQueue();
}
/**
* Returns true if some errors have been reported, even if cleared.
*/
public boolean hasErrors() {
if (goal() != null && ! goal().isReachable())
return true;
return false;
// return errorQueue().hasErrors();
}
/** Returns the <code>NodeFactory</code> that this Visitor is using.
*
* @see polyglot.ast.NodeFactory
*/
public NodeFactory nodeFactory() {
return nf;
}
/** Returns the <code>TypeSystem</code> that this Visitor is using.
*
* @see polyglot.types.TypeSystem
*/
public TypeSystem typeSystem() {
return ts;
}
/** Replaces the functionality of the <code>enter()</code> method; all
* sub-classes should over-ride this method instead of
* <code>enter()</code> if there is any chance of exceptions being
* generated.
*
* This method is the replacement for the <code>enter()</code> method,
* so that all of its subclasses gain the error handling capabilities
* of this visitor without having to rewrite it for the
* <code>enter()</code> for each sub-class.
*
* This method allows for a <code>SemanticException</code> to be
* thrown in the body, while <code>enter()</code> does not.
*
* @see polyglot.visit.NodeVisitor#enter(Node, Node)
* @throws SemanticException
* @param n The root of the subtree to be traversed.
* @return The <code>ErrorHandlingVisitor</code> which should be
* used to visit the children of <code>n</code>.
*/
protected NodeVisitor enterCall(Node parent, Node n)
throws SemanticException {
if (reporter.should_report(Reporter.visit, 3))
reporter.report(3, "enter: " + parent + " -> " + n);
return enterCall(n);
}
protected NodeVisitor enterCall(Node n) throws SemanticException {
return this;
}
/** This method determines what should be returned by <code>enter()</code>
* should its call to <code>enterCall()</code> throw a
* <code>SemanticException</code>.
*
* @param n The root of the subtree that was traversed.
* @return The <code>ErrorHandlingVisitor</code> which should be
* used to visit the childre of <code>n</code>.
*/
protected NodeVisitor enterError(Node n) {
return this;
}
/** Contains all of the functionality that can be done in the <code> leave
* </code> method, but allows <code>SemanticExceptions</code> to be
* thrown.
*
* This method is in addition to the <code>leave</code> method,
* and allows the compiler writer to write code that can throw errors
* and let the polyglot infrastructure handle the exceptions.
*
* @see polyglot.visit.NodeVisitor#leave(Node, Node, NodeVisitor)
* @throws SemanticException
* @param old The original state of root of the current subtree.
* @param n The current state of the root of the current subtree.
* @param v The <code>NodeVisitor</code> object used to visit the children.
* @return The final result of the traversal of the tree rooted at
* <code>n</code>.
*/
protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v)
throws SemanticException {
return leaveCall(old, n, v);
}
protected Node leaveCall(Node old, Node n, NodeVisitor v)
throws SemanticException {
return leaveCall(n);
}
protected Node leaveCall(Node n) throws SemanticException {
return n;
}
/** Return true if we should catch errors thrown when visiting the node. */
protected boolean catchErrors(Node n) {
return n instanceof Stmt
|| n instanceof ClassMember
|| n instanceof ClassDecl
|| n instanceof Import
|| n instanceof SourceFile;
}
/**
* Begin normal traversal of a subtree rooted at <code>n</code>. This gives
* the visitor the option of changing internal state or returning a new
* visitor which will be used to visit the children of <code>n</code>.
*
* This method delegates all responsibility of functionality to the
* <code>enterCall</code> method, and handles and reports any exceptions
* generated by <code>enterCall</code>.
*
* In overriding this method, unless the class explicitly does not
* want to maintain any of the error handling aspects of this class, a call
* <code>super.enter</code> should be embedded within the method at the
* end.
*
* @param n The root of the subtree to be traversed.
* @return The <code>NodeVisitor</code> which should be used to visit the
* children of <code>n</code>.
*/
public NodeVisitor enter(Node parent, Node n) {
if (reporter.should_report(Reporter.visit, 5))
reporter.report(5, "enter(" + n + ")");
if (catchErrors(n)) {
this.error = false;
}
try {
// should copy the visitor
return enterCall(parent, n);
}
catch (SemanticException e) {
if (e.getMessage() != null) {
Position position = e.position();
if (position == null) {
position = n.position();
}
errorQueue().enqueue(ErrorInfo.SEMANTIC_ERROR,
e.getMessage(), position);
}
else {
// silent error; these should be thrown only
// when the error has already been reported
}
// IMPORTANT: Mark the goal as failed, otherwise we may run dependent goals
// that depend on this pass completing successfully.
if (goal() != null)
goal().fail();
if (! catchErrors(n)) {
this.error = true;
}
return enterError(n);
}
}
public Goal goal() {
return job().extensionInfo().scheduler().currentGoal();
}
/**
* This method is called after all of the children of <code>n</code>
* have been visited. In this case, these children were visited by the
* visitor <code>v</code>. This is the last chance for the visitor to
* modify the tree rooted at <code>n</code>. This method will be called
* exactly the same number of times as <code>entry</code> is called.
* That is, for each node that is not overriden, <code>enter</code> and
* <code>leave</code> are each called exactly once.
* <p>
* Note that if <code>old == n</code> then the vistior should make a copy
* of <code>n</code> before modifying it. It should then return the
* modified copy.
*
* This method delegates all responsibility of functionality to the
* <code>leaveCall</code> method, and handles and reports any exceptions
* generated by <code> leaveCall</code>.
*
* In overriding this method, unless the class explicitly does not
* want to maintain any of the error handling aspects of this class, a call
* <code>super.leave</code> should be embedded within the method at the
* end.
*
* @param old The original state of root of the current subtree.
* @param n The current state of the root of the current subtree.
* @param v The <code>NodeVisitor</code> object used to visit the children.
* @return The final result of the traversal of the tree rooted at
* <code>n</code>.
*/
public Node leave(Node parent, Node old, Node n, NodeVisitor v) {
try {
if (v instanceof ErrorHandlingVisitor &&
((ErrorHandlingVisitor) v).error) {
if (reporter.should_report(Reporter.visit, 5))
reporter.report(5, "leave(" + n + "): error below");
if (catchErrors(n)) {
this.error = false;
((ErrorHandlingVisitor) v).error = false;
}
else {
// propagate the error outward
this.error = true;
}
// don't visit the node
return n;
}
if (reporter.should_report(Reporter.visit, 5))
reporter.report(5, "leave(" + n + "): calling leaveCall");
return leaveCall(parent, old, n, v);
}
catch (SemanticException e) {
if (e.getMessage() != null) {
Position position = e.position();
if (position == null) {
position = n.position();
}
errorQueue().enqueue(new CodedErrorInfo(ErrorInfo.SEMANTIC_ERROR, e.getMessage(), position, e.attributes()));
}
else {
// silent error; these should be thrown only
// when the error has already been reported
}
// IMPORTANT: Mark the goal as failed, otherwise we may run dependent goals
// that depend on this pass completing successfully.
if (goal() != null)
goal().fail();
if (catchErrors(n)) {
this.error = false;
((ErrorHandlingVisitor) v).error = false;
}
else {
this.error = true;
((ErrorHandlingVisitor) v).error = true;
}
return n;
}
}
}