/**************************************************************************** ** ** 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