text: /****************************************************************************
  **
  ** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
  ** Copyright (C) 2002-2005 Roberto Raggi <roberto@kdevelop.org>
  **
  ** This file is part of the Qt Script Generator project on Trolltech Labs.
  **
  ** This file may be used under the terms of the GNU General Public
  ** License version 2.0 as published by the Free Software Foundation
  ** and appearing in the file LICENSE.GPL included in the packaging of
  ** this file.  Please review the following information to ensure GNU
  ** General Public Licensing requirements will be met:
  ** http://www.trolltech.com/products/qt/opensource.html
  **
  ** If you are unsure which license is appropriate for your use, please
  ** review the following information:
  ** http://www.trolltech.com/products/qt/licensing.html or contact the
  ** sales department at sales@trolltech.com.
  **
  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  **
  ****************************************************************************/
  
  #include "binder.h"
  #include "lexer.h"
  #include "control.h"
  #include "symbol.h"
  #include "codemodel_finder.h"
  #include "class_compiler.h"
  #include "compiler_utils.h"
  #include "tokens.h"
  #include "dumptree.h"
  
  #include <iostream>
  
  #include <qdebug.h>
  
  Binder::Binder(CodeModel *__model, LocationManager &__location, Control *__control)
    : _M_model(__model),
      _M_location(__location),
      _M_token_stream(&_M_location.token_stream),
      _M_control(__control),
      _M_current_function_type(CodeModel::Normal),
      type_cc(this),
      name_cc(this),
      decl_cc(this)
  {
    _M_qualified_types["char"] = QString();
    _M_qualified_types["double"] = QString();
    _M_qualified_types["float"] = QString();
    _M_qualified_types["int"] = QString();
    _M_qualified_types["long"] = QString();
    _M_qualified_types["short"] = QString();
    _M_qualified_types["void"] = QString();
  }
  
  Binder::~Binder()
  {
  }
  
  FileModelItem Binder::run(AST *node)
  {
    FileModelItem old = _M_current_file;
    _M_current_access = CodeModel::Public;
  
    _M_current_file = model()->create<FileModelItem>();
    updateItemPosition (_M_current_file->toItem(), node);
    visit(node);
    FileModelItem result = _M_current_file;
  
    _M_current_file = old; // restore
  
    return result;
  }
  
  ScopeModelItem Binder::currentScope()
  {
    if (_M_current_class)
      return model_static_cast<ScopeModelItem>(_M_current_class);
    else if (_M_current_namespace)
      return model_static_cast<ScopeModelItem>(_M_current_namespace);
  
    return model_static_cast<ScopeModelItem>(_M_current_file);
  }
  
  TemplateParameterList Binder::changeTemplateParameters(TemplateParameterList templateParameters)
  {
    TemplateParameterList old = _M_current_template_parameters;
    _M_current_template_parameters = templateParameters;
    return old;
  }
  
  CodeModel::FunctionType Binder::changeCurrentFunctionType(CodeModel::FunctionType functionType)
  {
    CodeModel::FunctionType old = _M_current_function_type;
    _M_current_function_type = functionType;
    return old;
  }
  
  CodeModel::AccessPolicy Binder::changeCurrentAccess(CodeModel::AccessPolicy accessPolicy)
  {
    CodeModel::AccessPolicy old = _M_current_access;
    _M_current_access = accessPolicy;
    return old;
  }
  
  NamespaceModelItem Binder::changeCurrentNamespace(NamespaceModelItem item)
  {
    NamespaceModelItem old = _M_current_namespace;
    _M_current_namespace = item;
    return old;
  }
  
  ClassModelItem Binder::changeCurrentClass(ClassModelItem item)
  {
    ClassModelItem old = _M_current_class;
    _M_current_class = item;
    return old;
  }
  
  FunctionDefinitionModelItem Binder::changeCurrentFunction(FunctionDefinitionModelItem item)
  {
    FunctionDefinitionModelItem old = _M_current_function;
    _M_current_function = item;
    return old;
  }
  
  int Binder::decode_token(std::size_t index) const
  {
    return _M_token_stream->kind(index);
  }
  
  CodeModel::AccessPolicy Binder::decode_access_policy(std::size_t index) const
  {
    switch (decode_token(index))
      {
        case Token_class:
          return CodeModel::Private;
  
        case Token_struct:
        case Token_union:
          return CodeModel::Public;
  
        default:
          return CodeModel::Public;
      }
  }
  
  CodeModel::ClassType Binder::decode_class_type(std::size_t index) const
  {
    switch (decode_token(index))
      {
        case Token_class:
          return CodeModel::Class;
        case Token_struct:
          return CodeModel::Struct;
        case Token_union:
          return CodeModel::Union;
        default:
          std::cerr << "** WARNING unrecognized class type" << std::endl;
      }
      return CodeModel::Class;
  }
  
  const NameSymbol *Binder::decode_symbol(std::size_t index) const
  {
    return _M_token_stream->symbol(index);
  }
  
  void Binder::visitAccessSpecifier(AccessSpecifierAST *node)
  {
    const ListNode<std::size_t> *it =  node->specs;
    if (it == 0)
      return;
  
    it = it->toFront();
    const ListNode<std::size_t> *end = it;
  
    do
      {
        switch (decode_token(it->element))
          {
            default:
              break;
  
            case Token_public:
              changeCurrentAccess(CodeModel::Public);
              changeCurrentFunctionType(CodeModel::Normal);
              break;
            case Token_protected:
              changeCurrentAccess(CodeModel::Protected);
              changeCurrentFunctionType(CodeModel::Normal);
              break;
            case Token_private:
              changeCurrentAccess(CodeModel::Private);
              changeCurrentFunctionType(CodeModel::Normal);
              break;
            case Token_signals:
              changeCurrentAccess(CodeModel::Protected);
              changeCurrentFunctionType(CodeModel::Signal);
              break;
            case Token_slots:
              changeCurrentFunctionType(CodeModel::Slot);
              break;
          }
        it = it->next;
      }
    while (it != end);
  }
  
  void Binder::visitSimpleDeclaration(SimpleDeclarationAST *node)
  {
    visit(node->type_specifier);
  
    if (const ListNode<InitDeclaratorAST*> *it = node->init_declarators)
      {
        it = it->toFront();
        const ListNode<InitDeclaratorAST*> *end = it;
        do
          {
            InitDeclaratorAST *init_declarator = it->element;
            declare_symbol(node, init_declarator);
            it = it->next;
          }
        while (it != end);
      }
  }
  
  void Binder::declare_symbol(SimpleDeclarationAST *node, InitDeclaratorAST *init_declarator)
  {
    DeclaratorAST *declarator = init_declarator->declarator;
  
    while (declarator && declarator->sub_declarator)
      declarator = declarator->sub_declarator;
  
    NameAST *id = declarator->id;
    if (! declarator->id)
      {
        std::cerr << "** WARNING expected a declarator id" << std::endl;
        return;
      }
  
    CodeModelFinder finder(model(), this);
    ScopeModelItem symbolScope = finder.resolveScope(id, currentScope());
    if (! symbolScope)
      {
        name_cc.run(id);
        std::cerr << "** WARNING scope not found for symbol:"
                  << qPrintable(name_cc.name()) << std::endl;
        return;
      }
  
    decl_cc.run(declarator);
  
    if (decl_cc.isFunction())
      {
        name_cc.run(id->unqualified_name);
  
        FunctionModelItem fun = model()->create<FunctionModelItem>();
        updateItemPosition (fun->toItem(), node);
        fun->setAccessPolicy(_M_current_access);
        fun->setFunctionType(_M_current_function_type);
        fun->setName(name_cc.name());
        fun->setAbstract(init_declarator->initializer != 0);
        fun->setConstant(declarator->fun_cv != 0);
        fun->setTemplateParameters(_M_current_template_parameters);
        applyStorageSpecifiers(node->storage_specifiers, model_static_cast<MemberModelItem>(fun));
        applyFunctionSpecifiers(node->function_specifiers, fun);
  
        // build the type
        TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier,
                                                           declarator,
                                                           this);
  
        fun->setType(qualifyType(typeInfo, symbolScope->qualifiedName()));
  
  
        fun->setVariadics (decl_cc.isVariadics ());
  
        // ... and the signature
        foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters())
          {
            ArgumentModelItem arg = model()->create<ArgumentModelItem>();
            arg->setType(qualifyType(p.type, _M_context));
            arg->setName(p.name);
            arg->setDefaultValue(p.defaultValue);
            if (p.defaultValue)
                arg->setDefaultValueExpression(p.defaultValueExpression);
            fun->addArgument(arg);
          }
  
        fun->setScope(symbolScope->qualifiedName());
        symbolScope->addFunction(fun);
      }
    else
      {
        VariableModelItem var = model()->create<VariableModelItem>();
        updateItemPosition (var->toItem(), node);
        var->setTemplateParameters(_M_current_template_parameters);
        var->setAccessPolicy(_M_current_access);
        name_cc.run(id->unqualified_name);
        var->setName(name_cc.name());
        TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier,
                                                           declarator,
                                                           this);
        if (declarator != init_declarator->declarator
              && init_declarator->declarator->parameter_declaration_clause != 0)
          {
            typeInfo.setFunctionPointer (true);
            decl_cc.run (init_declarator->declarator);
            foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters())
              typeInfo.addArgument(p.type);
          }
  
        var->setType(qualifyType(typeInfo, _M_context));
        applyStorageSpecifiers(node->storage_specifiers, model_static_cast<MemberModelItem>(var));
  
        var->setScope(symbolScope->qualifiedName());
        symbolScope->addVariable(var);
      }
  }
  
  void Binder::visitFunctionDefinition(FunctionDefinitionAST *node)
  {
    Q_ASSERT(node->init_declarator != 0);
  
    ScopeModelItem scope = currentScope();
  
    InitDeclaratorAST *init_declarator = node->init_declarator;
    DeclaratorAST *declarator = init_declarator->declarator;
  
    // in the case of "void (func)()" or "void ((func))()" we need to
    // skip to the inner most.  This is in line with how the declarator
    // node is generated in 'parser.cpp'
    while (declarator && declarator->sub_declarator)
        declarator = declarator->sub_declarator;
    Q_ASSERT(declarator->id);
  
    CodeModelFinder finder(model(), this);
  
    ScopeModelItem functionScope = finder.resolveScope(declarator->id, scope);
    if (! functionScope)
      {
        name_cc.run(declarator->id);
        std::cerr << "** WARNING scope not found for function definition:"
                  << qPrintable(name_cc.name()) << std::endl
                  << "\tdefinition *ignored*"
                  << std::endl;
        return;
      }
  
    decl_cc.run(declarator);
  
    Q_ASSERT(! decl_cc.id().isEmpty());
  
    FunctionDefinitionModelItem
      old = changeCurrentFunction(_M_model->create<FunctionDefinitionModelItem>());
    _M_current_function->setScope(functionScope->qualifiedName());
    updateItemPosition (_M_current_function->toItem(), node);
  
    Q_ASSERT(declarator->id->unqualified_name != 0);
    name_cc.run(declarator->id->unqualified_name);
    QString unqualified_name = name_cc.name();
  
    _M_current_function->setName(unqualified_name);
    TypeInfo tmp_type = CompilerUtils::typeDescription(node->type_specifier,
                                                       declarator, this);
  
    _M_current_function->setType(qualifyType(tmp_type, _M_context));
    _M_current_function->setAccessPolicy(_M_current_access);
    _M_current_function->setFunctionType(_M_current_function_type);
    _M_current_function->setConstant(declarator->fun_cv != 0);
    _M_current_function->setTemplateParameters(_M_current_template_parameters);
  
    applyStorageSpecifiers(node->storage_specifiers,
                            model_static_cast<MemberModelItem>(_M_current_function));
    applyFunctionSpecifiers(node->function_specifiers,
                            model_static_cast<FunctionModelItem>(_M_current_function));
  
    _M_current_function->setVariadics (decl_cc.isVariadics ());
  
    foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters())
      {
        ArgumentModelItem arg = model()->create<ArgumentModelItem>();
        arg->setType(qualifyType(p.type, functionScope->qualifiedName()));
        arg->setName(p.name);
        arg->setDefaultValue(p.defaultValue);
        if (p.defaultValue)
          arg->setDefaultValueExpression(p.defaultValueExpression);
        _M_current_function->addArgument(arg);
      }
  
    functionScope->addFunctionDefinition(_M_current_function);
  
    FunctionModelItem prototype = model_static_cast<FunctionModelItem>(_M_current_function);
    FunctionModelItem declared = functionScope->declaredFunction(prototype);
  
    // try to find a function declaration for this definition..
    if (! declared)
      {
        functionScope->addFunction(prototype);
      }
    else
      {
        applyFunctionSpecifiers(node->function_specifiers, declared);
  
        // fix the function type and the access policy
        _M_current_function->setAccessPolicy(declared->accessPolicy());
        _M_current_function->setFunctionType(declared->functionType());
      }
  
    changeCurrentFunction(old);
  }
  
  void Binder::visitTemplateDeclaration(TemplateDeclarationAST *node)
  {
      const ListNode<TemplateParameterAST*> *it = node->template_parameters;
      if (it == 0) {
          // QtScript: we want to visit the declaration still, so that
          // e.g. QMetaTypeId<Foo> is added to the code model
          visit(node->declaration);
          return;
      }
  
      TemplateParameterList savedTemplateParameters = changeTemplateParameters(TemplateParameterList());
  
      it = it->toFront();
      const ListNode<TemplateParameterAST*> *end = it;
  
      TemplateParameterList templateParameters;
      do {
          TemplateParameterAST *parameter = it->element;
          TypeParameterAST *type_parameter = parameter->type_parameter;
  
          NameAST *name;
          if (!type_parameter) {
              // A hacky hack to work around missing support for parameter declarations in
              // templates. We just need the to get the name of the variable, since we
              // aren't actually compiling these anyway. We are still not supporting much
              // more, but we are refusing to fail for a few more declarations
              if (parameter->parameter_declaration == 0 ||
                  parameter->parameter_declaration->declarator == 0 ||
                  parameter->parameter_declaration->declarator->id == 0) {
  
                      /*std::cerr << "** WARNING template declaration not supported ``";
                      Token const &tk = _M_token_stream->token ((int) node->start_token);
                      Token const &end_tk = _M_token_stream->token ((int) node->declaration->start_token);
  
                      std::cerr << std::string (&tk.text[tk.position], (end_tk.position) - tk.position) << "''"
                          << std::endl << std::endl;*/
  
                      changeTemplateParameters(savedTemplateParameters);
                      return;
  
              }
  
              name = parameter->parameter_declaration->declarator->id;
          } else {
              int tk = decode_token(type_parameter->type);
              if (tk != Token_typename && tk != Token_class)
              {
                  /*std::cerr << "** WARNING template declaration not supported ``";
                  Token const &tk = _M_token_stream->token ((int) node->start_token);
                  Token const &end_tk = _M_token_stream->token ((int) node->declaration->start_token);
  
                  std::cerr << std::string (&tk.text[tk.position], (end_tk.position) - tk.position) << "''"
                  << std::endl << std::endl;*/
  
                  changeTemplateParameters(savedTemplateParameters);
                  return;
              }
              assert(tk == Token_typename || tk == Token_class);
  
              name = type_parameter->name;
          }
  
          TemplateParameterModelItem p = model()->create<TemplateParameterModelItem>();
          name_cc.run(name);
          p->setName(name_cc.name());
  
          _M_current_template_parameters.append(p);
          it = it->next;
      } while (it != end);
  
      visit(node->declaration);
  
      changeTemplateParameters(savedTemplateParameters);
  }
  
  void Binder::visitTypedef(TypedefAST *node)
  {
    const ListNode<InitDeclaratorAST*> *it = node->init_declarators;
    if (it == 0)
      return;
  
    it = it->toFront();
    const ListNode<InitDeclaratorAST*> *end = it;
  
    do
      {
        InitDeclaratorAST *init_declarator = it->element;
        it = it->next;
  
        Q_ASSERT(init_declarator->declarator != 0);
  
        // the name
        decl_cc.run (init_declarator->declarator);
        QString alias_name = decl_cc.id ();
  
        if (alias_name.isEmpty ())
          {
            std::cerr << "** WARNING anonymous typedef not supported! ``";
            Token const &tk = _M_token_stream->token ((int) node->start_token);
            Token const &end_tk = _M_token_stream->token ((int) node->end_token);
  
            std::cerr << std::string (&tk.text[tk.position], end_tk.position - tk.position) << "''"
                      << std::endl << std::endl;
            continue;
          }
  
        // build the type
        TypeInfo typeInfo = CompilerUtils::typeDescription (node->type_specifier,
                                                            init_declarator->declarator,
                                                            this);
        DeclaratorAST *decl = init_declarator->declarator;
        while (decl && decl->sub_declarator)
          decl = decl->sub_declarator;
  
        if (decl != init_declarator->declarator
              && init_declarator->declarator->parameter_declaration_clause != 0)
          {
            typeInfo.setFunctionPointer (true);
            decl_cc.run (init_declarator->declarator);
            foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters())
              typeInfo.addArgument(p.type);
          }
  
        ScopeModelItem scope = currentScope();
        DeclaratorAST *declarator = init_declarator->declarator;
        CodeModelFinder finder(model(), this);
        ScopeModelItem typedefScope = finder.resolveScope(declarator->id, scope);
  
        TypeAliasModelItem typeAlias = model ()->create<TypeAliasModelItem> ();
        updateItemPosition (typeAlias->toItem (), node);
        typeAlias->setName (alias_name);
        typeAlias->setType (qualifyType (typeInfo, currentScope ()->qualifiedName ()));
        typeAlias->setScope (typedefScope->qualifiedName());
        _M_qualified_types[typeAlias->qualifiedName().join(".")] = QString();
        currentScope ()->addTypeAlias (typeAlias);
      }
    while (it != end);
  }
  
  void Binder::visitNamespace(NamespaceAST *node)
  {
    bool anonymous = (node->namespace_name == 0);
  
    ScopeModelItem scope = currentScope();
  
    NamespaceModelItem old;
    if (! anonymous)
      {
        QString name = decode_symbol(node->namespace_name)->as_string();
  
        QStringList qualified_name = scope->qualifiedName();
        qualified_name += name;
        NamespaceModelItem ns =
          model_safe_cast<NamespaceModelItem>(_M_model->findItem(qualified_name,
                                                                    _M_current_file->toItem()));
        if (!ns)
          {
            ns = _M_model->create<NamespaceModelItem>();
            updateItemPosition (ns->toItem(), node);
            ns->setName(name);
            ns->setScope(scope->qualifiedName());
          }
        old = changeCurrentNamespace(ns);
  
        _M_context.append(name);
      }
  
    DefaultVisitor::visitNamespace(node);
  
    if (! anonymous)
      {
        Q_ASSERT(scope->kind() == _CodeModelItem::Kind_Namespace
                 || scope->kind() == _CodeModelItem::Kind_File);
  
        _M_context.removeLast();
  
        if (NamespaceModelItem ns = model_static_cast<NamespaceModelItem>(scope))
          {
            ns->addNamespace(_M_current_namespace);
          }
  
        changeCurrentNamespace(old);
      }
  }
  
  void Binder::visitForwardDeclarationSpecifier(ForwardDeclarationSpecifierAST *node)
  {
      name_cc.run(node->name);
      if (name_cc.name().isEmpty())
          return;
  
      ScopeModelItem scope = currentScope();
      _M_qualified_types[(scope->qualifiedName() + name_cc.qualifiedName()).join(".") ] = QString();
  }
  
  void Binder::visitClassSpecifier(ClassSpecifierAST *node)
  {
    ClassCompiler class_cc(this);
    class_cc.run(node);
  
    if (class_cc.name().endsWith('>') && !class_cc.name().contains('<'))
      {
        // TODO parser/lexer bug in case of template<> Class<8>
        return;
      }
  
    if (class_cc.name().endsWith('>') && !class_cc.name().contains('<')) {
  	  // TODO parser bug in case of template<> Class<8>
  	  return;
    }
  
    if (class_cc.name().isEmpty())
      {
        // anonymous not supported
        return;
      }
  
    Q_ASSERT(node->name != 0 && node->name->unqualified_name != 0);
  
    ScopeModelItem scope = currentScope();
  
    ClassModelItem old = changeCurrentClass(_M_model->create<ClassModelItem>());
    updateItemPosition (_M_current_class->toItem(), node);
    _M_current_class->setName(class_cc.name());
  
    TypeInfo info;
    QStringList baseClasses = class_cc.baseClasses();
    QStringList baseModifiers = class_cc.baseModifiers();
    for (int i=0; i<baseClasses.size(); ++i)
      {
          info.setQualifiedName(baseClasses.at(i).split("::"));
          baseClasses[i] = qualifyType(info, scope->qualifiedName()).qualifiedName().join("::");
      }
  
    _M_current_class->setBaseClasses(baseClasses);
    _M_current_class->setBaseModifiers(baseModifiers);
    _M_current_class->setClassType(decode_class_type(node->class_key));
    _M_current_class->setTemplateParameters(_M_current_template_parameters);
    _M_current_class->setAccessPolicy(_M_current_access);
  
    if (! _M_current_template_parameters.isEmpty())
      {
        QString name = _M_current_class->name();
        name += "<";
        for (int i = 0; i<_M_current_template_parameters.size(); ++i)
          {
            if (i != 0)
              name += ",";
  
            name += _M_current_template_parameters.at(i)->name();
          }
  
        name += ">";
        _M_current_class->setName(name);
      }
  
    CodeModel::AccessPolicy oldAccessPolicy = changeCurrentAccess(decode_access_policy(node->class_key));
    CodeModel::FunctionType oldFunctionType = changeCurrentFunctionType(CodeModel::Normal);
  
    _M_current_class->setScope(scope->qualifiedName());
    _M_qualified_types[_M_current_class->qualifiedName().join(".")] = QString();
  
    scope->addClass(_M_current_class);
  
    name_cc.run(node->name->unqualified_name);
    _M_context.append(name_cc.name());
    visitNodes(this, node->member_specs);
    _M_context.removeLast();
  
    changeCurrentClass(old);
    changeCurrentAccess(oldAccessPolicy);
    changeCurrentFunctionType(oldFunctionType);
  }
  
  void Binder::visitLinkageSpecification(LinkageSpecificationAST *node)
  {
    DefaultVisitor::visitLinkageSpecification(node);
  }
  
  void Binder::visitUsing(UsingAST *node)
  {
    DefaultVisitor::visitUsing(node);
  }
  
  void Binder::visitEnumSpecifier(EnumSpecifierAST *node)
  {
    CodeModelFinder finder(model(), this);
    ScopeModelItem scope = currentScope();
    ScopeModelItem enumScope = finder.resolveScope(node->name, scope);
  
    name_cc.run(node->name);
    QString name = name_cc.name();
  
    if (name.isEmpty())
      {
        // anonymous enum
        QString key = _M_context.join("::");
        int current = ++_M_anonymous_enums[key];
        name += QLatin1String("enum_");
        name += QString::number(current);
      }
  
    _M_current_enum = model()->create<EnumModelItem>();
    _M_current_enum->setAccessPolicy(_M_current_access);
    updateItemPosition (_M_current_enum->toItem(), node);
    _M_current_enum->setName(name);
    _M_current_enum->setScope(enumScope->qualifiedName());
  
    _M_qualified_types[_M_current_enum->qualifiedName().join(".")] = QString();
  
    enumScope->addEnum(_M_current_enum);
  
    DefaultVisitor::visitEnumSpecifier(node);
  
    _M_current_enum = 0;
  }
  
  static QString strip_preprocessor_lines(const QString &name)
  {
      QStringList lst = name.split("\n");
      QString s;
      for (int i=0; i<lst.size(); ++i) {
          if (!lst.at(i).startsWith('#'))
              s += lst.at(i);
      }
      return s.trimmed();
  }
  
  void Binder::visitEnumerator(EnumeratorAST *node)
  {
    Q_ASSERT(_M_current_enum != 0);
    EnumeratorModelItem e = model()->create<EnumeratorModelItem>();
    updateItemPosition (e->toItem(), node);
    e->setName(decode_symbol(node->id)->as_string());
  
    if (node->owner->class_enum)
      e->setClassName(_M_current_enum->name());
  
    if (ExpressionAST *expr = node->expression)
      {
        const Token &start_token = _M_token_stream->token((int) expr->start_token);
        const Token &end_token = _M_token_stream->token((int) expr->end_token);
  
        e->setValue(strip_preprocessor_lines(QString::fromUtf8(&start_token.text[start_token.position],
                                      (int) (end_token.position - start_token.position)).trimmed()).remove(' '));
      }
  
    _M_current_enum->addEnumerator(e);
  }
  
  void Binder::visitUsingDirective(UsingDirectiveAST *node)
  {
    DefaultVisitor::visitUsingDirective(node);
  }
  
  void Binder::visitQEnums(QEnumsAST *node)
  {
    const Token &start = _M_token_stream->token((int) node->start_token);
    const Token &end = _M_token_stream->token((int) node->end_token);
    QStringList enum_list = QString::fromLatin1(start.text + start.position,
                                                end.position - start.position).split(' ');
  
    ScopeModelItem scope = currentScope();
    for (int i=0; i<enum_list.size(); ++i)
      scope->addEnumsDeclaration(enum_list.at(i));
  }
  
  void Binder::visitQProperty(QPropertyAST *node)
  {
      const Token &start = _M_token_stream->token((int) node->start_token);
      const Token &end = _M_token_stream->token((int) node->end_token);
      QString property = QString::fromLatin1(start.text + start.position,
                                             end.position - start.position);
      _M_current_class->addPropertyDeclaration(property);
  }
  
  void Binder::applyStorageSpecifiers(const ListNode<std::size_t> *it, MemberModelItem item)
  {
    if (it == 0)
      return;
  
    it = it->toFront();
    const ListNode<std::size_t> *end = it;
  
    do
      {
        switch (decode_token(it->element))
          {
            default:
              break;
  
            case Token_friend:
              item->setFriend(true);
              break;
            case Token_auto:
              item->setAuto(true);
              break;
            case Token_register:
              item->setRegister(true);
              break;
            case Token_static:
              item->setStatic(true);
              break;
            case Token_extern:
              item->setExtern(true);
              break;
            case Token_mutable:
              item->setMutable(true);
              break;
          }
        it = it->next;
      }
    while (it != end);
  }
  
  void Binder::applyFunctionSpecifiers(const ListNode<std::size_t> *it, FunctionModelItem item)
  {
    if (it == 0)
      return;
  
    it = it->toFront();
    const ListNode<std::size_t> *end = it;
  
    do
      {
        switch (decode_token(it->element))
          {
            default:
              break;
  
            case Token_inline:
              item->setInline(true);
              break;
  
            case Token_virtual:
              item->setVirtual(true);
              break;
  
            case Token_explicit:
              item->setExplicit(true);
              break;
  
            case Token_Q_INVOKABLE:
              item->setInvokable(true);
              break;
          }
        it = it->next;
      }
    while (it != end);
  }
  
  TypeInfo Binder::qualifyType(const TypeInfo &type, const QStringList &context) const
  {
    // ### Potentially improve to use string list in the name table to
    if (context.size() == 0)
      {
        // ### We can assume that this means global namespace for now...
        return type;
      }
    else if (_M_qualified_types.contains(type.qualifiedName().join(".")))
      {
        return type;
      }
    else
      {
        QStringList expanded = context;
        expanded << type.qualifiedName();
        if (_M_qualified_types.contains(expanded.join(".")))
          {
            TypeInfo modified_type = type;
            modified_type.setQualifiedName(expanded);
            return modified_type;
          }
        else
          {
            CodeModelItem scope = model ()->findItem (context, _M_current_file->toItem ());
  
            if (ClassModelItem klass = model_dynamic_cast<ClassModelItem> (scope))
              {
                foreach (QString base, klass->baseClasses ())
                  {
                    QStringList ctx = context;
                    ctx.removeLast();
                    ctx.append (base);
  
                    TypeInfo qualified = qualifyType (type, ctx);
                    if (qualified != type)
                      return qualified;
                  }
              }
  
            QStringList copy = context;
            copy.removeLast();
            return qualifyType(type, copy);
          }
      }
  }
  
  void Binder::updateItemPosition(CodeModelItem item, AST *node)
  {
    QString filename;
    int line, column;
  
    assert (node != 0);
    _M_location.positionAt (_M_token_stream->position(node->start_token), &line, &column, &filename);
    item->setFileName (filename);
  }
  
  // kate: space-indent on; indent-width 2; replace-tabs on;
  