/****************************************************************************
** $Id: qscheck.cpp  beta1   edited Dec 16 20:55 $
**
** Copyright (C) 2001-2002 Trolltech AS.  All rights reserved.
**
** This file is part of the Qt Script for Applications framework (QSA).
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding a valid QSA Beta Evaluation Version license may use
** this file in accordance with the QSA Beta Evaluation Version License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about QSA Commercial License Agreements.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
*****************************************************************************/

#include "qscheck.h"
#include "qsnodes.h"
#include "qsenv.h"

// #define QSDEBUG_CHECK

#ifdef QSDEBUG_CHECK
#include <typeinfo>
#endif

QSCheckData::QSCheckData( QSEnv *e, QSGlobalClass *g )
    : currenv( e ),
      lattrs( AttributeNone ),
      ltype( 0 ),
      vbCount( 0 ),
      noGlobStatements( FALSE ),
      inswitch( FALSE ),
      directLookup( TRUE )
{
    if ( g )
 	scopeStack.push_front( g );
}

QSCheckData::~QSCheckData()
{
    // sanity checks
    Q_ASSERT( lablist.isEmpty() );
}

void QSCheckData::enterClass( QSClass *c )
{

    scopeStack.push_front( QSScopeResolution( c ) );
}

void QSCheckData::leaveClass()
{
    Q_ASSERT( inClass() );
    scopeStack.pop_front();
    //    clstack.removeLast();
}

void QSCheckData::registerType( QSClass *t )
{
    Q_ASSERT( !t->asClass() );
    QSClass * scope = currentScope();
    // ### Uncertain as to use identifier or name here?
    QSMember member;
    Q_ASSERT( !scope->member( 0, t->identifier(), &member ) );
    QSObject tobj( env()->typeClass() );
    tobj.setValue( t );
    scope->addStaticVariableMember( t->identifier(), tobj,
				    AttributeExecutable );
}

QSClass *QSCheckData::typeInfo( const QString &name ) const
{
    QSMember member;
    QSScopeResolutionList::const_iterator it = scopeStack.begin();
    QSObject tmp;
    tmp.setVal( (QSShared*)0 );
    int offset;
    while( it!=scopeStack.end() ) {
	offset = 0;
	tmp.setType( (*it).cl );
	const QSClass *cl = tmp.resolveMember( name, &member, tmp.objectType(), &offset );
	tmp.invalidate();
	if( cl ) {
	    QSObject o( cl->fetchValue( 0, member ) );
	    if ( o.objectType() == env()->typeClass() )
		return o.classValue();
	    else
		return 0;
	}
	it++;
    }
    return 0;
}


/*!
  Returns a pointer to the innermost class based on the current
  position in the scope. For global scope the global class is returned.
*/
QSClass * QSCheckData::innermostClass() const
{
    QSScopeResolution scope;
    QSScopeResolutionList::const_iterator it = scopeStack.begin();
    while( it!=scopeStack.end() ) {
	scope = *it;
	if( scope.isClassScope() || scope.isGlobalScope() ) {
	    return scope.cl;
	}
	it++;
    }
    Q_ASSERT( 0 /* should never get here */ );
    return 0;
}

/*!
  Returns the current function if the scope stack top is
  a function. 0 otherwise.
*/

QSFunctionScopeClass * QSCheckData::currentFunction() const
{
    if( scopeStack.size()>0 && scopeStack.first().isFunctionScope() ) {
	return (QSFunctionScopeClass*) scopeStack.first().cl;
    }
    return 0;
}

/*!
  Returns the current class if the scope stack top is a class. 0 otherwise
*/
QSClass * QSCheckData::currentClass() const
{
    if( scopeStack.size()>0 && scopeStack.first().isClassScope() ) {
	return (QSClass*) scopeStack.first().cl;
    }
    return 0;
}

/*!
  Returns the class representing the current scope. 0 if there are no
  objects on the scope stack
*/
QSClass * QSCheckData::currentScope() const
{
    Q_ASSERT( !scopeStack.isEmpty() );
    return scopeStack.first().cl;
}

bool QSCheckData::insideClass( const QString &name ) const
{
    QSScopeResolutionList::const_iterator it = scopeStack.begin();
    while( it!=scopeStack.end() ) {
	if( (*it).isClassScope() && (*it).name()==name ) {
	    return TRUE;
	}
	it++;
    }
    return false;
}


/*!
  Resolves the globally qualified name of \name, including
  package / class / function resolution.
*/
QString QSCheckData::globalName( const QString &name ) const
{
    QStringList lst(name);
    QSScopeResolutionList::const_iterator it = scopeStack.begin();
    while( it!=scopeStack.end() ) {
	lst.prepend( (*it).name() );
	it++;
    }
    return lst.join( "." );
}

void QSCheckData::enterFunction( QSFunctionScopeClass * func )
{
//     qDebug( "QSCheckData::enterFunction( %s ) into %s",
// 	    func->identifier().ascii(),
// 	    currentScope()->identifier().ascii() );
    scopeStack.push_front( func );
}

void QSCheckData::leaveFunction()
{
    Q_ASSERT( inFunction() );
    scopeStack.pop_front();
}

/*!
  Can we have a return statement at this point, i.e. are we inside
  a function ?
 */

bool QSCheckData::canReturn() const
{
    QSScopeResolutionList::const_iterator it = scopeStack.begin();
    while ( it != scopeStack.end() ) {
	if ( (*it).isFunctionScope() )
	    return TRUE;
	else if ( !(*it).isBlockScope() )
	    return FALSE;
	else
	    ++it;
    }
    return FALSE;
}

void QSCheckData::enterBlock( QSBlockScopeClass * cl )
{
    scopeStack.push_front( cl );
}

void QSCheckData::leaveBlock()
{
    scopeStack.pop_front();
}

void QSCheckData::setLabel( const QString &label )
{
    lastlab = label;
}

QString QSCheckData::currentLabel() const
{
    return lastlab;
}

bool QSCheckData::seenLabel( const QString &label ) const
{
    return lablist.contains( label );
}

void QSCheckData::clearLabel()
{
    lastlab = "";
}

void QSCheckData::enterLoop( const QString &label )
{
    lablist.append( label );
}

void QSCheckData::leaveLoop()
{
    Q_ASSERT( inLoop() );
    lablist.remove( --lablist.end() );
}

void QSCheckData::enterSwitch()
{
    inswitch = TRUE;
}

void QSCheckData::leaveSwitch()
{
    Q_ASSERT( inswitch );
    inswitch = FALSE;
}


void QSCheckData::enterPackage( const QString &name )
{
    Q_ASSERT( inGlobal() );
    currpack = name;
}

void QSCheckData::leavePackage()
{
    Q_ASSERT( inPackage() );
    currpack = QString::null;
}


bool QSCheckData::inGlobal() const
{
    return scopeStack.size()>0 && scopeStack.first().isGlobalScope();
}

void QSCheckData::addError( const QSNode *node, QSErrorCode code,
			    const QString &msg)
{
    ecodes.append( code );
    elines.append( node->lineNo() );
    emsgs.append( "Error: " + msg );
}

void QSCheckData::addError( const QSNode *node, const QString &msg )
{
    addError( node, QSErrGeneral, msg );
}

void QSCheckData::addWarning( const QSNode *node, QSErrorCode code,
			    const QString &msg)
{
    ecodes.append( code );
    elines.append( node->lineNo() );
    emsgs.append( "Warning: " + msg );
}

void QSCheckData::addWarning( const QSNode *node, const QString &msg )
{
    addWarning( node, QSErrGeneral, msg );
}


/////////////////// QSNode::check() implementations //////////////////////////

void QSNode::check( QSCheckData * )
{
    // ### nothing. should be made pure virtual one day
#ifdef QSDEBUG_CHECK
    qDebug( "check() in %s", typeid(*this).name() );
#endif
}

/*! When parsing libraries or form code we don't want to have anything
  but class, function and variable declarations at global level. This
  function is called by all other statements and will result in an error
  if we currently are in global level.
*/

void QSStatementNode::checkIfGlobalAllowed( QSCheckData *c )
{
    if ( c->globalStatementsForbidden() && c->inGlobal() )
	c->addError( this, QSErrPositionGlobal,
		     "Global statements detected. May have unwanted "
		     "side effects during execution." );
}

void QSProgramNode::check( QSCheckData *c )
{
    if ( source )
	source->check( c );
    QSClass *cl = c->currentScope();
    if( cl->numberOfVariables() < c->varBlockCount() ) {
	cl->setNumberOfVariables( c->varBlockCount() );
    }
    ( (QSInstanceData*) c->env()->currentScope().shVal() )->
	ensureSize( c->currentScope()->numberOfVariables() );
}

void QSFunctionBodyNode::check( QSCheckData *c )
{
    if ( source )
	source->check( c );
}

void QSSourceElementNode::check( QSCheckData *c )
{
    statement->check( c );
}

void QSSourceElementsNode::check( QSCheckData *c )
{
    if ( elements )
	elements->check( c );
    c->clearLabel();
    element->check( c );
}

void QSStatListNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    if ( list )
	list->check( c );
    c->clearLabel();
    if ( c->hasError() )
	return;
    statement->check( c );
}

void QSScopeNode::check( QSCheckData *c )
{
    QSBlockScopeClass * cl = new QSBlockScopeClass( c->env() );

    cl->setNumberOfVariables( c->currentScope()->numberOfVariables() );
    cl->setEnclosingClass( c->currentScope() );
    c->enterBlock( cl );

    checkStatement( c );

    c->leaveBlock();
    scope = cl;

    if( cl->numberOfVariables()>c->varBlockCount() )
	c->setVarBlockCount( cl->numberOfVariables() );
}


void QSBlockNode::checkStatement( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    if ( c->hasError() )
	return;
    if ( statlist )
	statlist->check( c );
}

void QSAssignExprNode::check( QSCheckData *c )
{
    expr->check( c );
}

void QSEmptyStatementNode::check( QSCheckData * )
{
    //    checkIfGlobalAllowed( c );
}

void QSExprStatementNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    expr->check( c );
}

void QSIfNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    expr->check( c );
    statement1->check( c );
    if ( statement2 )
	statement2->check( c );
}

void QSDoWhileNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    expr->check( c );
    c->enterLoop( c->currentLabel() );
    statement->check( c );
    c->leaveLoop();
}

void QSWhileNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    expr->check( c );
    c->enterLoop( c->currentLabel() );
    statement->check( c );
    c->leaveLoop();
}

void QSForNode::checkStatement( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    if ( expr1 )
	expr1->check( c );
    if ( expr2 )
	expr2->check( c );
    if ( expr3 )
	expr3->check( c );
    if ( c->hasError() )
	return;

    c->enterLoop( c->currentLabel() );
    stat->check( c );
    c->leaveLoop();
}

void QSForInNode::checkStatement( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    if ( var )
	var->check( c );
    if ( lexpr )
	lexpr->check( c );
    expr->check( c );
    if ( c->hasError() )
	return;

    c->enterLoop( c->currentLabel() );
    stat->check( c );
    c->leaveLoop();
}

void QSContinueNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    if ( !c->inLoop() )
	c->addError( this, "'continue' can only be used inside of iteration statements." );
    if ( !ident.isEmpty() && !c->seenLabel( ident ) )
	c->addError( this, "Unknown label." );
}

void QSBreakNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    if ( !c->inLoop() && !c->inSwitch() )
	c->addError( this, "'break' can only be used inside of iteration or switch "
		     "statements." );
    // ### true if nested loops & switch ?
    if ( !ident.isEmpty() && !c->seenLabel( ident ) )
	c->addError( this, "Unknown label." );
}

void QSReturnNode::check( QSCheckData *c )
{
    if ( !c->canReturn() )
	c->addError( this, "Can only return from inside a function." );
    if ( value )
	value->check( c );
}

void QSWithNode::checkStatement( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    bool dlenabled = c->directLookupEnabled();
    c->setDirectLookupEnabled( false );
    expr->check( c );
    stat->check( c );
    c->setDirectLookupEnabled( dlenabled );
}

void QSSwitchNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    expr->check( c );

    c->enterSwitch();
    block->check( c );
    c->leaveSwitch();
}

void QSLabelNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );

    c->pushLabel( label );

    if( stat )
	stat->check( c );

    c->popLabel();
}

void QSThrowNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
}

void QSCatchNode::checkStatement( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    QSClass *cl = c->currentScope();
    index = cl->addVariableMember( ident, AttributeNone );
    block->check( c );
}

void QSFinallyNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    block->check( c );
}

void QSTryNode::check( QSCheckData *c )
{
    checkIfGlobalAllowed( c );
    block->check( c );
    if ( _catch )
	_catch->check( c );
    if ( _final )
	_final->check( c );
}

void QSTypedVarNode::check( QSCheckData *c )
{
    if ( type )
	type->check( c );
}

void QSVarBindingNode::check( QSCheckData *c )
{
    var->check( c );
    if ( assign )
	assign->check( c );

    int attrs = c->lastAttributes();
    QSClass * cl = c->currentScope();
    QSMember m;
    if ( cl->member( 0, var->identifier(), &m ) ) {
	if( cl->asClass() ) {
	    c->addError( this, "Variable has already been declared" );
	    return;
	}
	m = QSMember( QSMember::Variable, 0, attrs );
	cl->replaceMember( var->identifier(), &m );
	idx = m.index();
    } else {
	idx = cl->addVariableMember( var->identifier(), attrs );
    }

    // store pointer to init node if this is a class member
    QSClassClass *clcl = cl->asClass();
    if ( clcl ) {
	if( attrs & AttributeStatic )
	    clcl->addStaticInitializer( assign );
	else
	    clcl->addMemberInitializer( assign );
	idx = -1; // Disable the variable binding node.
    }
}

void QSVarBindingListNode::check( QSCheckData *c )
{
    if ( list )
	list->check( c );
    binding->check( c );
}

void QSVarDefNode::check( QSCheckData *c )
{
    if ( attrs )
	attrs->check( c );
    else
	c->setLastAttributes( AttributeNone );

    if( kind==1 ) // const definition
	c->setLastAttributes( c->lastAttributes()|AttributeNonWritable );

    int as = c->lastAttributes();
    QSClass *cl = c->currentClass();
    if ( cl && !c->inFunction() ) {
	// class member
    } else {
	if ( as & AttributeStatic ) {
	    c->addError( this, QSErrAttrStaticContext,
			 "Can't declare static variables outside of "
			 "class context.");
	    return;
	}
    }
    list->check( c );

    // unset attributes
    c->setLastAttributes( AttributeNone );
}

void QSParameterNode::check( QSCheckData * c ) {
    Q_ASSERT( c->inFunction() );
    QSClass * cl = c->currentScope();

    QSMember m;
    if( cl->member( 0, id, &m ) ) {
	c->addError( this, "Redeclaration of function parameter: " + id );
	return;
    }

    cl->addVariableMember( id, AttributeNone );

    if( next )
	next->check( c );

    // create a member from id
    // add member to current scope
    // next.check();
}

void QSFuncDeclNode::check( QSCheckData *c )
{
//     qDebug( "Function noticed: " + c->globalName(ident) );
    if ( attrs )
	attrs->check( c );
    else
	c->setLastAttributes( AttributeNone );
    int as = c->lastAttributes();
    QSClass * cl = c->currentScope();
    Q_ASSERT( cl );

    if ( (as&AttributeStatic) && cl->name()!="Class" ) {
	c->addError( this, QSErrAttrStaticContext,
		     "Can't declare static functions outside of "
		     "class context.");
	return;
    }

    QSMember m;
    m.setPrivate( as&AttributePrivate );

    if ( cl->member( 0, ident, &m ) ) {
	QSMember mem( body, as );
	cl->replaceMember( ident, &mem );
    } else {
	cl->addFunctionMember( ident, body, as );
    }

    int tmpVarBlockCount = c->varBlockCount();
    c->setVarBlockCount( 0 );
    QSFunctionScopeClass * fscope = new QSFunctionScopeClass( c->env()->objectClass(), this );
    fscope->setEnclosingClass( cl );
    body->setScopeDefinition( fscope );
    c->enterFunction( fscope );
    if( param )
	param->check( c );
    body->check( c );
    c->leaveFunction();

    if( c->varBlockCount()>fscope->numberOfVariables() )
	fscope->setNumberOfVariables( c->varBlockCount() );
    c->setVarBlockCount( tmpVarBlockCount );

    // Calculate the number of arguments
    int count = 0;
    QSParameterNode * node = param;
    while( node ) {
	count++;
	node = node->nextParam();
    }
    fscope->setNumberOfArguments( count );

    // unset attributes
    c->setLastAttributes( AttributeNone );
}

void QSFuncExprNode::check( QSCheckData *c )
{
    // ### Check attributes and refactor code with QSFuncDeclNode
    QSClass * cl = c->currentScope();
    QSFunctionScopeClass * fscope = new QSFunctionScopeClass( c->env()->objectClass()  );
    fscope->setEnclosingClass( cl );
    body->setScopeDefinition( fscope );
    c->enterFunction( fscope );
    if( param )
	param->check( c );
    body->check( c );
    c->leaveFunction();

    // Calculate the number of arguments
    int count = 0;
    QSParameterNode * node = param;
    while( node ) {
	count++;
	node = node->nextParam();
    }
    fscope->setNumberOfArguments( count );
}

void QSClassDefNode::check( QSCheckData *c )
{
//     qDebug( "Class noticed: " + c->globalName( ident ) );

    // forward declaration ?
    if ( !body ) {
	c->addWarning( this, "Forward declarations not supported, yet" );
	return;
    }

    if ( attrs )
	attrs->check( c );
    else
	c->setLastAttributes( AttributeNone );
    int as = c->lastAttributes();

    // base class
    QSClass *b = c->env()->objectClass();
    if ( type ) {
	type->check( c );
	if ( c->hasError() )
	    return;
	Q_ASSERT( c->lastType() );
	// try to cast
	b = c->lastType()->asClass();
	if ( !b ) {
	    c->addError( this, QSErrClassBaseInvalid,
			 "Base type has been declared final." );
	    return;
	}
	if ( b->isFinal() ) {
	    c->addError( this, QSErrClassBaseFinal,
			 "Base class has been declared final." );
	    return;
	}
    }

    // create new class description
    QSClass *scope = c->currentScope();
    if ( scope->name()=="Class" ) {
	if ( !(as&AttributeStatic) ) {
	    c->addError( this, QSErrNestedClass,
			 "Nested classes can only be declared static." );
	    return;
	}
    } else if ( as&AttributeStatic ) {
        c->addError( this, QSErrAttributeConflict,
		     "Static classes can only be declared inside a class" );
	return;
    } else if ( scope->name()=="FunctionScope" ) {
	c->addError( this, QSErrNestedClass,
		     "Cannot declare a class inside a function." );
	return;
    }



    QSClassClass *cl = new QSClassClass( b, as, ident );
    cl->setEnclosingClass( scope );

    QSMember member;
    QSObject tobj( c->env()->typeClass() );
    tobj.setValue( cl );
    if( scope->member( 0, cl->identifier(), &member ) ) {
	scope->replaceMember( cl->identifier(), &member, tobj );
    } else {
	scope->addStaticVariableMember( cl->identifier(), tobj,
					AttributeExecutable );
    }

    c->enterClass( cl );
    body->check( c );
    c->leaveClass();

    // if there is a function with the same name it becomes the constructor
    QSMember ctor;
    if ( cl->member( 0, ident, &ctor ) ) {
	if ( ctor.isExecutable() ) {
	    cl->setHasDefaultConstructor( TRUE );
	} else {
	    qWarning( "Constructor is no function" ); // pedantic
	}
    }

    cl->setClassBodyNode( body );
    QSFunctionScopeClass *fs = new QSFunctionScopeClass( c->env()->objectClass() );
    fs->setEnclosingClass( cl );
    body->setScopeDefinition( fs );

    // will be used in execute()
    this->cldef = cl;
}

// has to be in sync with QSAttribute enum (qsglobal.h)
static const char * const attrStr[] = {
    "",
    "abstract",
    "final",
    "private",
    "public",
    "static",
    "true",
    "false"
    "constructor"
};

/*! Map a QSAttribute value (2^n) to index n for above string table
 */
static int attrNo( int abit )
{
    if ( abit == 0 )
	return 0;
    int no = -1;
    while ( abit )
	no++, abit >>= 1;
    return no;
}

void QSAttributeNode::check( QSCheckData *c )
{
    int asum = AttributeNone;
    QValueList<QSAttribute>::ConstIterator it = attrs.begin();
    for ( ; it != attrs.end(); ++it ) {
	if ( asum & *it ) {
	    QString msg = QString( "Duplicate attribute `%1'" )
			  .arg( attrStr[attrNo(*it)] );
	    c->addWarning( this, QSErrAttributeDuplication, msg );
	}
	asum |= *it;
    }
    // check for invalid combinations
    if ( ( asum & AttributePrivate && asum & AttributePublic ) ||
	 ( asum & AttributeTrue && asum & AttributeFalse ) ) {
	// ### more checks, more detailed error message
	c->addError( this, QSErrAttributeConflict, "Conflicting attributes" );
    }
    c->setLastAttributes( asum );
}

void QSTypeNode::check( QSCheckData *c )
{
    QSClass *t = c->typeInfo( ident );
    c->setLastType( t );
    if ( !t ) {
	QString msg = "Unkown type " + ident + ".";
	c->addError( this, QSErrTypeUnknown, msg );
    }
}

void QSPackageNode::check( QSCheckData *c )
{
    if ( c->inPackage() )
	c->addError( this, "Cannot defined nested packages." );
    // possibly too strict
    if ( !c->inGlobal() )
	c->addError( this, "Packages can only be defined at global scope." );
    c->enterPackage( package );
    block->check( c );
    c->leavePackage();

}

void QSImportNode::check( QSCheckData *c )
{
    // possibly too strict
    if ( !c->inGlobal() )
	c->addError( this, "Packages can only be imported at global scope." );
}

void QSAssignNode::check( QSCheckData *c )
{
    expr->check( c );
    left->check( c );
}

void QSGroupNode::check( QSCheckData *c )
{
    if( group )
	group->check( c );
}

void QSElisionNode::check( QSCheckData *c )
{
    if( elision )
	elision->check( c );
}

void QSElementNode::check( QSCheckData *c )
{
    if( list )
	list->check( c );
    if( elision )
	elision->check( c );
    if( node )
	node->check( c );
}

void QSArrayNode::check( QSCheckData *c )
{
    if( element )
	element->check( c );
    if( elision )
	elision->check( c );
}

void QSObjectLiteralNode::check( QSCheckData *c )
{
    if( list )
	list->check( c );
}

void QSPropertyValueNode::check( QSCheckData *c )
{
    if( name )
	name->check( c );
    if( assign )
	assign->check( c );
    if( list )
	list->check( c );
}

void QSAccessorNode1::check( QSCheckData *c )
{
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSAccessorNode2::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSNewExprNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
    if( args )
	args->check( c );
}

void QSFunctionCallNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
    if( args )
	args->check( c );
}

void QSDeleteNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSVoidNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSTypeOfNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSPrefixNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSUnaryPlusNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSBitwiseNotNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSLogicalNotNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSAddNode::check( QSCheckData *c )
{
    if( term1 )
	term1->check( c );
    if( term2 )
	term2->check( c );
}

void QSMultNode::check( QSCheckData *c )
{
    if( term1 )
	term1->check( c );
    if( term2 )
	term2->check( c );
}

void QSShiftNode::check( QSCheckData *c )
{
    if( term1 )
	term1->check( c );
    if( term2 )
	term2->check( c );
}

void QSRelationalNode::check( QSCheckData *c )
{
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSEqualNode::check( QSCheckData *c )
{
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSBitOperNode::check( QSCheckData *c )
{
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSBinaryLogicalNode::check( QSCheckData *c )
{
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSConditionalNode::check( QSCheckData *c )
{
    if( logical )
	logical->check( c );
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSCommaNode::check( QSCheckData *c )
{
    if( expr1 )
	expr1->check( c );
    if( expr2 )
	expr2->check( c );
}

void QSClauseListNode::check( QSCheckData *c )
{
    if( cl )
	cl->check( c );
    if( nx )
	nx->check( c );
}

void QSCaseBlockNode::check( QSCheckData *c )
{
    if( list1 )
	list1->check( c );
    if( def )
	def->check( c );
    if( list2 )
	list2->check( c );
}

void QSArgumentListNode::check( QSCheckData *c )
{
    if( list )
	list->check( c );
    if( expr )
	expr->check( c );
}

void QSArgumentsNode::check( QSCheckData *c )
{
    if( list )
	list->check( c );
}

void QSNegateNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}

void QSCaseClauseNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
    if( list )
	list->check( c );
}

void QSNumberNode::check( QSCheckData * )
{
}

void QSBooleanNode::check( QSCheckData * )
{
}

void QSStringNode::check( QSCheckData * )
{
}

void QSRegExpNode::check( QSCheckData * )
{
}

void QSResolveNode::check( QSCheckData *c )
{
    if ( !c->directLookupEnabled() )
	return;
    QSClass * cl = c->currentScope();
    QSClass *cont = cl;
    int uplvl = 0;
    int blocks = 0;
    QSMember member;
    while ( cont ) {
	QSMember mem;
	if ( cont->member( 0, ident, &mem ) ) {
	    if ( mem.type()==QSMember::Variable && !mem.isStatic() ) {
		member = mem;
		break;
	    }
	}
	uplvl++;
	cont = cont->enclosingClass();
    }
    if( member.isDefined() ) {
	// Due to special handling of block scopes in the global object...
	if( cont==c->env()->globalClass() )
 	    uplvl+=blocks;
 	info = new QSLookupInfo( uplvl, member );
    }
}

void QSNullNode::check( QSCheckData * )
{
}

void QSThisNode::check( QSCheckData * )
{
}

void QSPostfixNode::check( QSCheckData *c )
{
    if( expr )
	expr->check( c );
}


void QSPropertyNode::check( QSCheckData * )
{
}
