binder.cpp
906 lines
| 27.9 KiB
| text/x-c
|
CppLexer
florianlink
|
r10 | /**************************************************************************** | ||
** | ||||
** 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().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()); | ||||
QStringList baseClasses = class_cc.baseClasses(); TypeInfo info; | ||||
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->setClassType(decode_class_type(node->class_key)); | ||||
_M_current_class->setTemplateParameters(_M_current_template_parameters); | ||||
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 (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; | ||||