blob: 1efdbf8e8e0cbf156f3bb1566ead0b35ef6eb1e4 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2001-2004 Roberto Raggi
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.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 the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "smallobject.h"
#include "tokenengine.h"
#include "semantic.h"
#include <QtDebug>
#include <QString>
#include <QRegExp>
QT_BEGIN_NAMESPACE
using namespace TokenStreamAdapter;
using namespace TokenEngine;
using namespace CodeModel;
Semantic::Semantic(CodeModel::NamespaceScope *globalScope,
TokenStreamAdapter::TokenStream *tokenStream,
TypedPool<CodeModel::Item> *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<UnknownType>(m_storage);
type->setName("__UnknownType");
globalScope->addType(type);
type->setParent(globalScope);
m_sharedUnknownMember = Create<TypeMember>(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<CodeModel::NamespaceScope>(m_storage);
namespaceScope->setName(nsName);
parent->addScope(namespaceScope);
NamespaceMember *namespaceMember = Create<NamespaceMember>(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<CodeModel::ClassScope>(m_storage);
klass->setName(className);
currentScope.top()->addScope(klass);
//create ClassType
CodeModel::ClassType *type = CodeModel::Create<CodeModel::ClassType>(m_storage);
type->setScope(klass);
currentScope.top()->addType(type);
type->setParent(currentScope.top());
//create TypeMember
CodeModel::TypeMember *typeMember = CodeModel::Create<CodeModel::TypeMember>(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<DeclarationAST *> 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<CodeModel::ClassType>(m_storage);
type->setScope(0);
currentScope.top()->addType(type);
type->setParent(currentScope.top());
// Create TypeMember.
CodeModel::TypeMember *typeMember = CodeModel::Create<CodeModel::TypeMember>(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<CodeModel::EnumType>(m_storage);
enumType->setName(nameText);
currentScope.top()->addType(enumType);
enumType->setParent(currentScope.top());
//create a TypeMember
CodeModel::TypeMember *typeMember = CodeModel::Create<CodeModel::TypeMember>(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<InitDeclaratorAST*> 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<QByteArray>()).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<CodeModel::VariableMember>(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<CodeModel::UnknownType>(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<AST*> 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<AST*> 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<CodeModel::FunctionMember>(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<CodeModel::UnknownType>(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<BaseSpecifierAST*> *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<Member *> 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<ParameterDeclarationAST*> *l = params->parameterList();
if (!l)
return;
foreach (ParameterDeclarationAST *param, *l) {
CodeModel::Argument *arg = CodeModel::Create<CodeModel::Argument>(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<CodeModel::UnknownType>(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<Member *> memberList = nameLookup(currentScope.top(), ast->name());
NamespaceScope *targetNamespace = 0;
// search for namespace in member list.
QList<Member *>::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<UsingDirectiveLink>(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<CodeModel::BlockScope>(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<CodeModel::VariableMember>(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<CodeModel::BlockScope>(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<ClassMemberAccessAST *>(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<CodeModel::Member *> 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<CodeModel::Member *> 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<CodeModel::EnumType>(m_storage);
enumType->setName(name);
currentScope.top()->addType(enumType);
enumType->setParent(currentScope.top());
//create a TypeMember
CodeModel::TypeMember *typeMember = CodeModel::Create<CodeModel::TypeMember>(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<EnumeratorAST*> *list = ast->enumeratorList();
if (!list)
return;
foreach (EnumeratorAST *current, *list) {
CodeModel::VariableMember *enumerator = CodeModel::Create<CodeModel::VariableMember>(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<InitDeclaratorAST*> *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<CodeModel::AliasType>(m_storage);
//typeAlias->setName(id);
//typeAlias->setParent(scope);
scope->addType(typeAlias);
//create a TypeMember
CodeModel::TypeMember *typeMember = CodeModel::Create<CodeModel::TypeMember>(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,
// because 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<CodeModel::Member *> 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-empty 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, except in the case of overloaded functions.
TODO: Argument-dependent name lookup
*/
QList<CodeModel::Member *> 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<CodeModel::Member *> Semantic::unqualifiedNameLookup(CodeModel::Scope *baseScope, const NameAST* name)
{
QList<UsingDirectiveLink *> usingDirectiveLinks;
CodeModel::Scope *currentScope = baseScope;
QList<CodeModel::Member *> 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<UsingDirectiveLink *>::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<CodeModel::Member *> Semantic::qualifiedNameLookup(CodeModel::Scope *baseScope, const NameAST* name)
{
QList<CodeModel::Member *> 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<CodeModel::Member *> Semantic::lookupNameInScope(CodeModel::Scope *scope, const NameAST* name)
{
QList<CodeModel::Member *> 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<ClassOrNamespaceNameAST *> *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<CodeModel::Member *> 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<CodeModel::Member*> 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<CodeModel::Member*> candidatateList, const DeclaratorAST *functionDeclarator)
{
// get arguments for function 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<AST*> cv = *typeSpec->cvQualify()->children();
foreach (AST *current, cv) {
text += " " + textOf(current);
}
text += " ";
}
text += textOf(typeSpec);
if (typeSpec->cv2Qualify()) {
List<AST*> cv = *typeSpec->cv2Qualify()->children();
foreach (AST *current, cv) {
text += textOf(current) + " ";
}
}
if (declarator && declarator->ptrOpList()) {
List<AST*> ptrOpList = *declarator->ptrOpList();
foreach (AST *current, ptrOpList) {
text += " " + textOf(current);
}
text += " ";
}
return text.trimmed().simplified();
}
QList<QByteArray> Semantic::scopeOfName(NameAST *id, const QList<QByteArray>& startScope)
{
QList<QByteArray> scope = startScope;
if (id && id->classOrNamespaceNameList()){
if (id->isGlobal())
scope.clear();
List<ClassOrNamespaceNameAST*> l = *id->classOrNamespaceNameList();
foreach (ClassOrNamespaceNameAST *current, l) {
if (current->name())
scope << textOf(current->name());
}
}
return scope;
}
QList<QByteArray> Semantic::scopeOfDeclarator(DeclaratorAST *d, const QList<QByteArray>& startScope)
{
if(!d)
return QList<QByteArray>();
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<AST*> 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<AST*> 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<ParameterDeclarationAST*> 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<CodeModel::NameUse>(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<AST*> *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 <<" |" <<m_tokenStream->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