diff -r 000000000000 -r 1918ee327afb tools/porting/src/semantic.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/porting/src/semantic.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,1227 @@ +/**************************************************************************** +** +** Copyright (C) 2001-2004 Roberto Raggi +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qt3to4 porting application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "smallobject.h" +#include "tokenengine.h" +#include "semantic.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace TokenStreamAdapter; +using namespace TokenEngine; +using namespace CodeModel; + +Semantic::Semantic(CodeModel::NamespaceScope *globalScope, + TokenStreamAdapter::TokenStream *tokenStream, + TypedPool *storage) +{ + m_storage = storage; + m_tokenStream = tokenStream; + + m_currentAccess = CodeModel::Member::Public; + m_inSlots = false; + m_inSignals = false; + m_inStorageSpec = false; + m_inTypedef = false; + + globalScope->setName("::"); + currentScope.push(globalScope); + + //create global UnknownType and UnknownTypeMember + UnknownType *type = Create(m_storage); + type->setName("__UnknownType"); + globalScope->addType(type); + type->setParent(globalScope); + + m_sharedUnknownMember = Create(m_storage); + m_sharedUnknownMember->setNameToken(TokenRef()); + m_sharedUnknownMember->setName("Unknown"); + m_sharedUnknownMember->setType(type); + globalScope->addMember(m_sharedUnknownMember); + m_sharedUnknownMember->setParent(globalScope); + +} + +void Semantic::parseAST(TranslationUnitAST *node) +{ + TreeWalker::parseTranslationUnit(node); +} + + +void Semantic::parseLinkageSpecification(LinkageSpecificationAST *ast) +{ + if(!ast) + return; + int inStorageSpec = m_inStorageSpec; + m_inStorageSpec = true; + TreeWalker::parseLinkageSpecification(ast); + m_inStorageSpec = inStorageSpec; +} + +void Semantic::parseNamespace(NamespaceAST *ast) +{ + CodeModel::NamespaceScope *parent = currentScope.top()->toNamespaceScope(); + if(!parent->toNamespaceScope()) { + emit error("Error in Semantic::parseNamespace: parent scope was not a namespace"); + return; + } + + QByteArray nsName; + if (!ast->namespaceName() || textOf(ast->namespaceName()).isEmpty()){ + nsName = "(__QT_ANON_NAMESPACE)"; + } else { + nsName = textOf(ast->namespaceName()); + } + + CodeModel::NamespaceScope *namespaceScope = 0; + + // Look up namespace scope in case it is already defined. + // (Unlike classes, C++ namespaces are "open" and can be added to.) + CodeModel::Scope *scope = parent->scopes().value(nsName); + if (scope) + namespaceScope = scope->toNamespaceScope(); + + // Create new namespace if not found. + if (!namespaceScope) { + namespaceScope = CodeModel::Create(m_storage); + namespaceScope->setName(nsName); + parent->addScope(namespaceScope); + + NamespaceMember *namespaceMember = Create(m_storage); + namespaceMember->setNameToken(tokenRefFromAST(ast->namespaceName())); + namespaceMember->setName(nsName); + namespaceMember->setNamespaceScope(namespaceScope); + currentScope.top()->addMember(namespaceMember); + namespaceMember->setParent(currentScope.top()); + } + + currentScope.push(namespaceScope); + TreeWalker::parseNamespace(ast); + currentScope.pop(); +} + +void Semantic::parseClassSpecifier(ClassSpecifierAST *ast) +{ + if (!ast->name()){ + return; + } + + QByteArray kind = textOf(ast->classKey()); + if (kind == "class") + m_currentAccess = CodeModel::Member::Private; + else // kind =="struct" + m_currentAccess = CodeModel::Member::Public; + + QByteArray className = textOf(ast->name()->unqualifiedName()); + + //create ClassScope + CodeModel::ClassScope *klass = CodeModel::Create(m_storage); + klass->setName(className); + currentScope.top()->addScope(klass); + + //create ClassType + CodeModel::ClassType *type = CodeModel::Create(m_storage); + type->setScope(klass); + currentScope.top()->addType(type); + type->setParent(currentScope.top()); + + //create TypeMember + CodeModel::TypeMember *typeMember = CodeModel::Create(m_storage); + typeMember->setNameToken(tokenRefFromAST(ast->name()->unqualifiedName())); + typeMember->setName(className); + typeMember->setType(type); + currentScope.top()->addMember(typeMember); + typeMember->setParent(currentScope.top()); + + currentScope.push(klass); + if (ast->baseClause()) + parseBaseClause(ast->baseClause(), klass); + + //TreeWalker::parseClassSpecifier(ast); + parseNode(ast->winDeclSpec()); + parseNode(ast->classKey()); + parseNode(ast->baseClause()); + + // Here's the trick for parsing c++ classes: + // All inline function definitions must be interpreted as if they were + // written after any other declarations in the class. + QList functionDefinitions; + if (ast->declarationList()) + foreach(DeclarationAST *decl, *ast->declarationList()) { + if(decl->nodeType() == NodeType_FunctionDefinition) + functionDefinitions.append(decl); + else + parseNode(decl); + } + foreach(DeclarationAST *decl, functionDefinitions) + parseNode(decl); + + currentScope.pop(); +} +/* + Parse a class, struct or enum forward decalration. +*/ +void Semantic::parseElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *node) +{ + if (!node) + return; + AST *kind = node->kind(); + if (!kind) + return; + + const QByteArray kindText = textOf(kind); + const QByteArray nameText = textOf(node->name()); + + // Don't do anything if the class, struct or enum has already been declared or defined. + if (lookupNameInScope(currentScope.top(), node->name()).count() > 0) + return; + + if (kindText == "class" || kindText == "struct") { + // Create ClassType. + CodeModel::ClassType *type = CodeModel::Create(m_storage); + type->setScope(0); + currentScope.top()->addType(type); + type->setParent(currentScope.top()); + + // Create TypeMember. + CodeModel::TypeMember *typeMember = CodeModel::Create(m_storage); + typeMember->setNameToken(tokenRefFromAST(node->name()->unqualifiedName())); + typeMember->setName(nameText); + typeMember->setType(type); + currentScope.top()->addMember(typeMember); + typeMember->setParent(currentScope.top()); + } else if (kindText == "enum") { + //create a Type + CodeModel::EnumType *enumType = CodeModel::Create(m_storage); + enumType->setName(nameText); + currentScope.top()->addType(enumType); + enumType->setParent(currentScope.top()); + + //create a TypeMember + CodeModel::TypeMember *typeMember = CodeModel::Create(m_storage); + if(node->name()) + typeMember->setNameToken(tokenRefFromAST(node->name()->unqualifiedName())); + typeMember->setName(nameText); + typeMember->setType(enumType); + currentScope.top()->addMember(typeMember); + typeMember->setParent(currentScope.top()); + } +} + +void Semantic::parseSimpleDeclaration(SimpleDeclarationAST *ast) +{ + TypeSpecifierAST *typeSpec = ast->typeSpec(); + InitDeclaratorListAST *declarators = ast->initDeclaratorList(); + + if (typeSpec) + parseTypeSpecifier(typeSpec); + + if (declarators){ + List l = *declarators->initDeclaratorList(); + + foreach (InitDeclaratorAST *current, l) { + parseDeclaration(ast->functionSpecifier(), ast->storageSpecifier(), typeSpec, current); + } + } +} + +void Semantic::parseDeclaration(AST *funSpec, AST *storageSpec, TypeSpecifierAST *typeSpec, InitDeclaratorAST *decl) +{ + if (m_inStorageSpec) + return; + + if(!decl) + return; + + DeclaratorAST *d = decl->declarator(); + if (!d) + return; + + if (!d->subDeclarator() && d->parameterDeclarationClause()) { + parseFunctionDeclaration(funSpec, storageSpec, typeSpec, decl); + return; + } + if(!typeSpec || !typeSpec->name()) + return; + + DeclaratorAST *t = d; + while (t && t->subDeclarator()) + t = t->subDeclarator(); + + QByteArray id; + if (t && t->declaratorId() && t->declaratorId()->unqualifiedName()) + id = textOf(t->declaratorId()->unqualifiedName()); + + if (!t || !t->declaratorId() || !t->declaratorId()->unqualifiedName()) + return; + AST *nameAST = t->declaratorId()->unqualifiedName(); + QByteArray name = textOf(nameAST); + + + if (!scopeOfDeclarator(d, QList()).isEmpty()){ + return; + } + + //Check if this is possibly a function call by searching for '(' and ')' + const QByteArray declText = textOf(decl); + if (declText.contains("(") && declText.contains(")")) { + if (decl->declarator() && decl->declarator()->subDeclarator()) { + + NameAST * name = decl->declarator()->subDeclarator()->declaratorId(); + if (name) + parseNameUse(name); + return; + } + } + + //create VariableMember + CodeModel::VariableMember *variableMember = CodeModel::Create(m_storage); + variableMember->setNameToken(tokenRefFromAST(nameAST)); + variableMember->setName(name); + variableMember->setAccess(m_currentAccess); + variableMember->setParent(currentScope.top()); + currentScope.top()->addMember(variableMember); + + //look up type of variableMember, + + TypeMember *typeMember = typeLookup(currentScope.top(), typeSpec->name()); + if(typeMember) { + variableMember->setType(typeMember->type()); + } else { + QByteArray text = typeOfDeclaration(typeSpec, d); + CodeModel::UnknownType *type = CodeModel::Create(m_storage); + type->setName(text); + variableMember->setType(type); + } + + if (decl) + parseNode(decl->initializer()); + +} + +void Semantic::parseFunctionDeclaration(AST *funSpec, AST *storageSpec, + TypeSpecifierAST * typeSpec, InitDeclaratorAST * initDeclarator) +{ + bool isFriend = false; + bool isVirtual = false; + bool isStatic = false; + bool isInline = false; + bool isPure = initDeclarator->initializer() != 0; + + if (funSpec){ + List l = *funSpec->children(); + foreach (AST *current, l) { + QByteArray text = textOf(current); + if (text == "virtual") isVirtual = true; + else if (text == "inline") isInline = true; + } + } + + if (storageSpec){ + List l = *storageSpec->children(); + foreach (AST *current, l) { + QByteArray text = textOf(current); + if (text == "friend") isFriend = true; + else if (text == "static") isStatic = true; + } + } + DeclaratorAST *declarator = initDeclarator->declarator(); + if(!declarator || !declarator->declaratorId()) + return; + AST *nameAST = declarator->declaratorId()->unqualifiedName(); + QByteArray name = textOf(nameAST); + + CodeModel::FunctionMember *method = CodeModel::Create(m_storage); + method->setNameToken(tokenRefFromAST(nameAST)); + method->setName(name); + method->setAccess(m_currentAccess); + method->setStatic(isStatic); + method->setVirtual(isVirtual); + method->setAbstract(isPure); + + parseFunctionArguments(declarator, method); + + if (m_inSignals) + method->setSignal(true); + + if (m_inSlots) + method->setSlot(true); + + method->setConstant(declarator->constant() != 0); + + QByteArray text = typeOfDeclaration(typeSpec, declarator); + if (!text.isEmpty()) { + CodeModel::UnknownType *type = CodeModel::Create(m_storage); + type->setName(text); + method->setReturnType(type); + } + + method->setParent(currentScope.top()); + currentScope.top()->addMember(method); +} + + +void Semantic::parseBaseClause(BaseClauseAST * baseClause, CodeModel::ClassScope *klass) +{ + if(!baseClause) + return; + if(!klass) + return; + List *l = baseClause->baseSpecifierList(); + if (!l) + return; + foreach (BaseSpecifierAST *baseSpecifier, *l) { + QByteArray baseName; + if (!baseSpecifier->name()) + continue; + + // Look up a class with the correct name. + QList candidates = nameLookup(klass, baseSpecifier->name()); + if (candidates.count() == 1 ) { + Member *member = candidates.at(0); + Q_ASSERT(member); + TypeMember *typeMember = member->toTypeMember(); + if (typeMember) { + Q_ASSERT(typeMember->type()); + ClassType *classType = typeMember->type()->toClassType(); + if (classType) { + klass->addBaseClass(classType); + } + } + } + } +} +void Semantic::parseFunctionArguments(const DeclaratorAST *declarator, CodeModel::FunctionMember *method) +{ + if(!declarator || !method) + return; + + ParameterDeclarationClauseAST *clause = declarator->parameterDeclarationClause(); + + if (clause && clause->parameterDeclarationList()){ + ParameterDeclarationListAST *params = clause->parameterDeclarationList(); + List *l = params->parameterList(); + if (!l) + return; + foreach (ParameterDeclarationAST *param, *l) { + CodeModel::Argument *arg = CodeModel::Create(m_storage); + arg->setParent(method); + + if (param->declarator()){ + QByteArray text = declaratorToString(param->declarator(), QByteArray(), true); + if(param->declarator()->declaratorId()) + arg->setNameToken(tokenRefFromAST(param->declarator()->declaratorId()->unqualifiedName())); + if (!text.isEmpty()) + arg->setName(text); + } + + QByteArray tp = typeOfDeclaration(param->typeSpec(), param->declarator()); + if (!tp.isEmpty()) { + CodeModel::UnknownType *type = CodeModel::Create(m_storage); + type->setName(tp); + arg->setType(type); + } + + method->addArgument(arg); + } + } +} + +// using directive (using namespace A) +void Semantic::parseUsingDirective(UsingDirectiveAST *ast) +{ + QByteArray qualifiedname = textOf(ast->name()); + QByteArray name = textOf(ast->name()->unqualifiedName()); + + //look up target namespace name + QList memberList = nameLookup(currentScope.top(), ast->name()); + + NamespaceScope *targetNamespace = 0; + + // search for namespace in member list. + QList::ConstIterator it = memberList.constBegin(); + while(it != memberList.constEnd()) { + if (NamespaceMember *namespaceMember = (*it)->toNamespaceMember()) { + targetNamespace = namespaceMember->namespaceScope(); + break; + } + ++it; + } + + if (targetNamespace == 0) + return; + + // Find the insertion namespace, which is the first common + // ancesotor namespace for the current scope and the target namespace + + // currentScope might be a block scope, find its first namespace parent + CodeModel::Scope *currentParent = currentScope.top(); + while (currentParent->toNamespaceScope() == 0) { + currentParent = currentParent->parent(); + } + + CodeModel::Scope *namespaceA = currentParent; + while (namespaceA != 0) { + CodeModel::Scope *namespaceB = targetNamespace; + while (namespaceB != 0) { + if (namespaceB == namespaceA) + break; + namespaceB = namespaceB->parent(); + } + if (namespaceB == namespaceA) + break; + namespaceA = namespaceA->parent(); + } + + if (namespaceA == 0 || namespaceA->toNamespaceScope() == 0) + return; + + NamespaceScope *insertionNamespace = namespaceA->toNamespaceScope(); + + // Create using directive link + UsingDirectiveLink *usingDirectiveLink = Create(m_storage); + usingDirectiveLink->setParent(currentScope.top()); + usingDirectiveLink->setTargetNamespace(targetNamespace); + usingDirectiveLink->setInsertionNamespace(insertionNamespace); + + // add it to current namespace + if (NamespaceScope *namespaceScope = currentScope.top()->toNamespaceScope()) + namespaceScope->addUsingDirectiveLink(usingDirectiveLink); + else if (BlockScope *blockScope = currentScope.top()->toBlockScope()) + blockScope->addUsingDirectiveLink(usingDirectiveLink); +} + +void Semantic::parseFunctionDefinition(FunctionDefinitionAST *ast) +{ + AST *funSpec = ast->functionSpecifier(); + AST *storageSpec = ast->storageSpecifier(); + TypeSpecifierAST *typeSpec = ast->typeSpec(); + InitDeclaratorAST *initDeclarator = ast->initDeclarator(); + if (!ast->initDeclarator()) + return; + + DeclaratorAST *d = initDeclarator->declarator(); + + if (!d->declaratorId()) + return; + + parseFunctionDeclaration(funSpec, storageSpec, typeSpec, initDeclarator); + CodeModel::FunctionMember *method = functionLookup(currentScope.top(), d); + + if(!method) { + emit error("Error in Semantic::parseFunctionDefinition: Could not find declaration for function definition"); + return; + } + + CodeModel::Scope *parent = method->parent(); + + if(!ast->functionBody()) { + emit error("Error in Semantic::parseFunctionDefinition: no function body in function definition"); + return; + } + + //create child function scope + QByteArray id = textOf(d->declaratorId()->unqualifiedName()); + CodeModel::BlockScope *functionScope = CodeModel::Create(m_storage); + functionScope->setName(QByteArray("__QT_ANON_BLOCK_SCOPE(Function: ") + id + QByteArray(")")); + functionScope->setParent(parent); + method->setFunctionBodyScope(functionScope); + + //add arguments to child scope + ArgumentCollection arguments = method->arguments(); + ArgumentCollection::ConstIterator it = arguments.constBegin(); + while(it != arguments.constEnd()) { + CodeModel::Argument *argument = *it; + CodeModel::VariableMember *variableMember = CodeModel::Create(m_storage); + variableMember->setNameToken(argument->nameToken()); + variableMember->setType(argument->type()); + variableMember->setName(argument->name()); + variableMember->setParent(functionScope); + functionScope->addMember(variableMember); + ++it; + } + + //push function scope and parse function body + currentScope.push(functionScope); + parseStatementList(ast->functionBody()); + currentScope.pop(); +} + +void Semantic::parseStatementList(StatementListAST *statemenList) +{ + if(!statemenList) + return; + CodeModel::BlockScope *blockScope = CodeModel::Create(m_storage); + blockScope->setName("__QT_ANON_BLOCK_SCOPE"); + blockScope->setParent(currentScope.top()); + currentScope.top()->addScope(blockScope); + + currentScope.push(blockScope); + TreeWalker::parseStatementList(statemenList); + currentScope.pop(); +} + +void Semantic::parseExpression(AbstractExpressionAST* node) +{ + if(!node) + return; + if(node->nodeType() == NodeType_ClassMemberAccess) + parseClassMemberAccess(static_cast(node)); + else + TreeWalker::parseExpression(node); +} + +/* + Pretty hardwired code for handling class member access of the types: + object.member and objectPtr->member. + + This function creates a name use for object to its declaration, and a + name use from member to its declaration in the class. +*/ +void Semantic::parseClassMemberAccess(ClassMemberAccessAST *node) +{ + if(!node) + return; + parseExpression(node->expression()); + // Get a name use for the 'object' name. + NameUse *nameUse = findNameUse(node->expression()); + // Since the NameUse refers to an object, its decalaration must be + // a ClassType. Get the scope of this class type. + if( nameUse + && nameUse->declaration() + && nameUse->declaration()->toVariableMember() + && nameUse->declaration()->toVariableMember()->type() + && nameUse->declaration()->toVariableMember()->type()->toClassType() + && nameUse->declaration()->toVariableMember()->type()->toClassType()->scope()) { + + CodeModel::Scope *scope = nameUse->declaration()->toVariableMember()->type()->toClassType()->scope(); + QList members = lookupNameInScope(scope, node->name()); + if(members.count() != 0) { + createNameUse(members.at(0), node->name()); + return; + } + } + // Create a NameUse that refers to the global shared unknown type. + createNameUse(m_sharedUnknownMember, node->name()); +} + +void Semantic::parseExpressionStatement(ExpressionStatementAST *node) +{ + TreeWalker::parseExpressionStatement(node); +} + +// using declaration (using A::b) +void Semantic::parseUsing(UsingAST *ast) +{ + //CodeModel::Scope *s = lookUpScope(currentScope.top(), ast->name()); + QList members = nameLookup(currentScope.top(), ast->name()); + if(members.isEmpty()) { + emit error("Error in Semantic::parseUsing: could not look up using target"); + return; + } + //TODO: handle multiple members (when nameLookup returns a set of overloded functions) + CodeModel::Member *member = members[0]; + CodeModel::Scope *targetScope = member->parent(); + if(!targetScope) { + emit error("Error in Semantic::parseUsing: target has no parent scope"); + return; + } + + if(!ast->name()) + return; + AST *nameAST = ast->name()->unqualifiedName(); + if(!nameAST) + return; + QByteArray name = textOf(nameAST); +} + +void Semantic::parseEnumSpecifier(EnumSpecifierAST *ast) +{ + if (!ast->name()) + return; + + QByteArray name = textOf(ast->name()); + + //create a Type + CodeModel::EnumType *enumType = CodeModel::Create(m_storage); + enumType->setName(name); + currentScope.top()->addType(enumType); + enumType->setParent(currentScope.top()); + + //create a TypeMember + CodeModel::TypeMember *typeMember = CodeModel::Create(m_storage); + if(ast->name()) + typeMember->setNameToken(tokenRefFromAST(ast->name()->unqualifiedName())); + typeMember->setName(name); + typeMember->setType(enumType); + currentScope.top()->addMember(typeMember); + typeMember->setParent(currentScope.top()); + + //parse the eneumerators + List *list = ast->enumeratorList(); + if (!list) + return; + foreach (EnumeratorAST *current, *list) { + CodeModel::VariableMember *enumerator = CodeModel::Create(m_storage); + enumerator->setNameToken(tokenRefFromAST(current->id())); + enumerator->setName(textOf(current->id())); + enumerator->setAccess(m_currentAccess); + enumerator->setStatic(true); + enumerator->setType(enumType); + currentScope.top()->addMember(enumerator); + enumerator->setParent(currentScope.top()); + } + +} + +void Semantic::parseTypedef(TypedefAST *ast) +{ + TypeSpecifierAST *typeSpec = ast->typeSpec(); + InitDeclaratorListAST *declarators = ast->initDeclaratorList(); + + if (typeSpec && declarators){ + QByteArray typeId; + + if (typeSpec->name()) + typeId = textOf(typeSpec->name()); + + List *l = declarators->initDeclaratorList(); + if (!l) + return; + foreach (InitDeclaratorAST *initDecl, *l) { + QByteArray type, id; + if (initDecl->declarator()){ + type = typeOfDeclaration(typeSpec, initDecl->declarator()); + + DeclaratorAST *d = initDecl->declarator(); + while (d->subDeclarator()){ + d = d->subDeclarator(); + } + + if (d->declaratorId()) + id = textOf(d->declaratorId()); + } + + //create a type + CodeModel::Scope *scope = currentScope.top(); + CodeModel::AliasType *typeAlias = CodeModel::Create(m_storage); + //typeAlias->setName(id); + //typeAlias->setParent(scope); + scope->addType(typeAlias); + + //create a TypeMember + CodeModel::TypeMember *typeMember = CodeModel::Create(m_storage); + if(typeSpec->name()) + typeMember->setNameToken(tokenRefFromAST(typeSpec->name()->unqualifiedName())); + typeMember->setName(id); + typeMember->setType(typeAlias); + currentScope.top()->addMember(typeMember); + typeMember->setParent(currentScope.top()); + + } + + } +} + +void Semantic::parseTypeSpecifier(TypeSpecifierAST *ast) +{ + // If this is a classSpecifier or a EnumSpecifier we skip the name lookup, + // becuase looking up the name "E" in a class definition like + // "class E { ..." makes no sense. (There might be a variable named E + // already declared, but that variable is now shadowed by the class type.) + if( ast->nodeType() != NodeType_EnumSpecifier + && ast->nodeType() != NodeType_ClassSpecifier + && ast->nodeType() != NodeType_ElaboratedTypeSpecifier ) + parseNameUse(ast->name()); + TreeWalker::parseTypeSpecifier(ast); +} + +/* + Parses a name: looks up name, creates name use. +*/ +void Semantic::parseNameUse(NameAST* name) +{ + if(!name) + return; + + // Look up name + QList members = nameLookup(currentScope.top(), name); + if(members.isEmpty()) { + //cout << "no declaration found for " << textOf(name).constData() << endl; + // Create NameUse that refer to a shared UnknownMember + createNameUse(m_sharedUnknownMember, name); + return; + } + + //TODO: handle multiple members (when nameLookup returns a set of overloaded functions) + CodeModel::Member *member = members[0]; + if(!member->parent()) { + emit error("Error in Semantic::parseUsing: target has no parent scope"); + return; + } + + createNameUse(member, name); +} + +/* + looks up name used in basescope. If name->isGlobal() is true or if classOrNamespaceList() + returns a non-emty list, the C++ qualified name lookup rules are used. Otherwise the + unquialified name lookup rules are used. Returns the a list of members that was found, + In most cases this list will contain zero or one element, exept in the case of overloaded functions. + TODO: Argument-dependent name lookup +*/ +QList Semantic::nameLookup(CodeModel::Scope *baseScope, const NameAST* name) +{ + if (name->isGlobal() || (name->classOrNamespaceNameList() + && name->classOrNamespaceNameList()->size()>0 )) { + return qualifiedNameLookup(baseScope, name); + } else { + return unqualifiedNameLookup(baseScope, name); + } +} + +//look up an unqualified name +QList Semantic::unqualifiedNameLookup(CodeModel::Scope *baseScope, const NameAST* name) +{ + QList usingDirectiveLinks; + CodeModel::Scope *currentScope = baseScope; + QList entities; + + while (currentScope != 0) { + // Add any "using namespace" directive links for the current scope to + // usingDirectiveLinks + if (NamespaceScope *namespaceScope = currentScope->toNamespaceScope()) + usingDirectiveLinks += namespaceScope->usingDirectiveLinks(); + if (BlockScope *blockScope = currentScope->toBlockScope()) + usingDirectiveLinks += blockScope->usingDirectiveLinks(); + + // Search usingDirectiveLinks for a link where currentScope is the + // insertion namespace. If found look up name in the target namespace + // for that link. + if (NamespaceScope *namespaceScope = currentScope->toNamespaceScope()) { + QList::ConstIterator it = usingDirectiveLinks.constBegin(); + while (it != usingDirectiveLinks.constEnd()) { + if ((*it)->insertionNamespace() == namespaceScope) + entities = lookupNameInScope((*it)->targetNamespace(), name); + ++it; + } + } + + // Look up names in this scope. + entities += lookupNameInScope(currentScope, name); + if (!entities.isEmpty()) + break; + currentScope = currentScope->parent(); + } + return entities; +} + +//look up a qualified name +QList Semantic::qualifiedNameLookup(CodeModel::Scope *baseScope, const NameAST* name) +{ + QList entities; + CodeModel::Scope *currentScope = baseScope; + + // Check if the global ("::") scope has been specified. + if(name->isGlobal()) { + while (currentScope->parent()) + currentScope = currentScope->parent(); + } + + while (entities.isEmpty() && currentScope != 0) { + CodeModel::Scope *targetScope = scopeLookup(currentScope, name); + entities = lookupNameInScope(targetScope, name); + currentScope = currentScope->parent(); + } + + return entities; +} + +//looks up a name in a scope, includes base classes if scope is a class scope +QList Semantic::lookupNameInScope(CodeModel::Scope *scope, const NameAST* name) +{ + QList entities; + + if(!scope || !name) + return entities; + + QByteArray nameText = textOf(name->unqualifiedName()->name()); + //look up name in members of current scope + const CodeModel::MemberCollection members = scope->members(); + if (members.contains(nameText)) + entities.append(members.value(nameText)); + + // if not found, look up name in base classes (if any) + CodeModel::ClassScope *classScope = scope->toClassScope(); + if (entities.isEmpty() && classScope) { + const TypeCollection baseClasses = classScope->baseClasses(); + TypeCollection::ConstIterator it = baseClasses.constBegin(); + while (it != baseClasses.constEnd()) { + CodeModel::Scope *baseClass = it.value()->toClassType()->scope(); + if (scope != baseClass) + entities += lookupNameInScope(baseClass, name); + ++it; + } + + if (entities.count() > 1) + emit error("Error in Semantic::lookupNameInScope: name " + + nameText + " is ambigous"); + } + return entities; +} + +/* + Resolves the classOrNamespaceNameList part of a NameAST against a base scope. +*/ +CodeModel::Scope *Semantic::scopeLookup(CodeModel::Scope *baseScope, const NameAST* name) +{ + CodeModel::Scope *currentScope = baseScope; + const List *scopeList = name->classOrNamespaceNameList(); + // if there is no scope list, then the scope we are looking for is baseScope + if (!scopeList) + return baseScope; + + // Check if the global ("::") scope has been specified. + if(name->isGlobal()) { + while (currentScope->parent()) + currentScope = currentScope->parent(); + } + + while(currentScope != 0) { + int nestingCounter = 0; + CodeModel::Scope *nestedScope = currentScope; + while (nestingCounter < scopeList->count()) { + const QByteArray nameText = textOf((*scopeList)[nestingCounter]->name()); + nestedScope = nestedScope->scopes().value(nameText); + if (!nestedScope) + break; + ++nestingCounter; + } + if(nestedScope) // found target scope? + return nestedScope; + + currentScope = currentScope->parent(); //look in parent scope + } + + return 0; +} + +TypeMember *Semantic::typeLookup(CodeModel::Scope *baseScope, const NameAST* name) +{ + QList memberList = nameLookup(baseScope, name); + + foreach(Member *member, memberList) { + if(TypeMember *typeMember = member->toTypeMember()) + return typeMember; + } + return 0; +} + +FunctionMember *Semantic::functionLookup(CodeModel::Scope *baseScope, + const DeclaratorAST *functionDeclarator) +{ + + QList candidateList = + nameLookup(baseScope, functionDeclarator->declaratorId()); + return selectFunction(candidateList, functionDeclarator); +} + +/* + This is a simplified function lookup routine, for matching member function + definitions with member function declarations. It does not implement + the general C++ function overload resolution rules. +*/ +FunctionMember *Semantic::selectFunction(QList candidatateList, const DeclaratorAST *functionDeclarator) +{ + // get arguments for funciton we are looking for + FunctionMember testFunction; + parseFunctionArguments(functionDeclarator, &testFunction); + const ArgumentCollection testArgumentCollection = testFunction.arguments(); + + //test againts functions in overload list. + foreach(Member* member, candidatateList) { + FunctionMember *function = member->toFunctionMember(); + if (!function) + continue; + const ArgumentCollection argumentCollection = function->arguments(); + + //test argument types and number of arguments + ArgumentCollection::ConstIterator arg1 = argumentCollection.constBegin(); + ArgumentCollection::ConstIterator arg2 = testArgumentCollection.constBegin(); + bool match = true; + while(arg1 != argumentCollection.constEnd() && arg2 != testArgumentCollection.constEnd()) { + if( arg1.value()->type()->name() != arg2.value()->type()->name() ) { + match = false; + break; + } + ++arg1; + ++arg2; + } + if(match) + return function; + } + return 0; +} + +QByteArray Semantic::typeOfDeclaration(TypeSpecifierAST *typeSpec, DeclaratorAST *declarator) +{ + if (!typeSpec) + return QByteArray(); + + QByteArray text; + + if (typeSpec->cvQualify()) { + List cv = *typeSpec->cvQualify()->children(); + foreach (AST *current, cv) { + text += " " + textOf(current); + } + text += " "; + } + + + text += textOf(typeSpec); + + if (typeSpec->cv2Qualify()) { + List cv = *typeSpec->cv2Qualify()->children(); + foreach (AST *current, cv) { + text += textOf(current) + " "; + } + } + + if (declarator && declarator->ptrOpList()) { + List ptrOpList = *declarator->ptrOpList(); + foreach (AST *current, ptrOpList) { + text += " " + textOf(current); + } + text += " "; + } + + return text.trimmed().simplified(); +} + + + +QList Semantic::scopeOfName(NameAST *id, const QList& startScope) +{ + QList scope = startScope; + if (id && id->classOrNamespaceNameList()){ + if (id->isGlobal()) + scope.clear(); + + List l = *id->classOrNamespaceNameList(); + foreach (ClassOrNamespaceNameAST *current, l) { + if (current->name()) + scope << textOf(current->name()); + } + } + + return scope; +} + +QList Semantic::scopeOfDeclarator(DeclaratorAST *d, const QList& startScope) +{ + if(!d) + return QList(); + return scopeOfName(d->declaratorId(), startScope); +} + +QByteArray Semantic::typeSpecToString(TypeSpecifierAST* typeSpec) +{ + if (!typeSpec) + return QByteArray(); + + QByteArray tp; + if (typeSpec->cvQualify()) { + tp += "const "; + } + + tp += (QString::fromLatin1(textOf(typeSpec)).replace(QRegExp(QLatin1String(" :: ")), QString::fromUtf8("::"))).toLatin1(); + return tp; +} + +QByteArray Semantic::declaratorToString(DeclaratorAST* declarator, const QByteArray& scope, bool skipPtrOp) +{ + if (!declarator) + return QByteArray(); + + QByteArray text; + + if (!skipPtrOp && declarator->ptrOpList()){ + List ptrOpList = *declarator->ptrOpList(); + foreach (AST *current, ptrOpList) { + text += textOf(current); + } + text += QByteArray(" "); + } + + text += scope; + + if (declarator->subDeclarator()) + text += QByteArray("(") + declaratorToString(declarator->subDeclarator()) + QByteArray(")"); + + if (declarator->declaratorId()) + text += textOf(declarator->declaratorId()); + + if (declarator->arrayDimensionList()) { + List arrays = *declarator->arrayDimensionList(); + foreach (AST *current, arrays) { + current=current; //silence unused symbol warning + text += QByteArray("[]"); + } + } + + if (declarator->parameterDeclarationClause()){ + text += QByteArray("("); + + ParameterDeclarationListAST* l = declarator->parameterDeclarationClause()->parameterDeclarationList(); + if (l != 0){ + List params = *l->parameterList(); + foreach (ParameterDeclarationAST *current, params) { + QByteArray type = typeSpecToString(current->typeSpec()); + text += type; + if (!type.isEmpty()) + text += QByteArray(" "); + text += declaratorToString(current->declarator()); + + // ### FIXME if (it.current()) + text += QByteArray(", "); + } + } + + text += QByteArray(")"); + + if (declarator->constant() != 0) + text += QByteArray(" const"); + } + + return QString::fromLatin1(text).replace(QRegExp(QLatin1String(" :: ")), QLatin1String("::")).simplified().toLatin1(); +} + +QByteArray Semantic::textOf(const AST *node) const +{ + if (!node) + return QByteArray(); + QByteArray text; + for (int i = node->startToken(); i < node->endToken(); ++i) { + if (!m_tokenStream->isHidden(i)) { + if (i != node->startToken()) + text += QByteArray(" "); + text += m_tokenStream->tokenText(i); + } + } + return text; +} + +void Semantic::createNameUse(Member *member, NameAST *name) +{ + if (!name) + return; + + AST *unqualifedName = name->unqualifiedName()->name(); + + if(!unqualifedName || !member) + return; + + CodeModel::NameUse *nameUse = CodeModel::Create(m_storage); + nameUse->setParent(currentScope.top()); + nameUse->setNameToken(tokenRefFromAST(unqualifedName)); + nameUse->setName(textOf(unqualifedName)); + nameUse->setDeclaration(member); + + currentScope.top()->addNameUse(nameUse); + addNameUse(unqualifedName, nameUse); +} + +void Semantic::addNameUse(AST *node, NameUse *nameUse) +{ + const int tokenIndex = node->startToken(); + m_nameUses.insert(tokenIndex, nameUse); +} + +/* + Searches a AST node and all its children for a nameUse. The name use is + found by looking up each node's tokens in the m_nameUses map. A depth-first + search is used. +*/ +NameUse *Semantic::findNameUse(AST *node) +{ + if(!node) + return 0; + + List *children = node->children(); + if(children) { + NameUse *nameUse = 0; + foreach(AST* child , *children) { + nameUse = findNameUse(child); + if(nameUse) + break; + } + if (nameUse) + return nameUse; + } + + for (int t = node->startToken(); t < node->endToken(); ++t) { + // cout << t <<" |" <tokenText(t).constData() << "|" << endl; + if (m_nameUses.contains(t)) + return m_nameUses.value(t); + } + return 0; +} + +/* + Gets a TokenRef from an AST node. + Assumes that the node only covers one token, which means that + node->statToken() == node->endToken(). If this is not the case + then the TokenRef will reference the token at startToken. +*/ +TokenEngine::TokenRef Semantic::tokenRefFromAST(AST *node) +{ + const int startTokenIndex = node->startToken(); + const TokenEngine::TokenContainer tokenContainer = m_tokenStream->tokenContainer(startTokenIndex); + const int containerIndex = m_tokenStream->containerIndex(startTokenIndex); + return TokenEngine::TokenRef(tokenContainer, containerIndex); +} + +QT_END_NAMESPACE