diff -r 000000000000 -r 876b1a06bc25 tools/icheck/parser/src/libs/cplusplus/LookupContext.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/icheck/parser/src/libs/cplusplus/LookupContext.cpp Wed Aug 25 15:49:42 2010 +0300 @@ -0,0 +1,783 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "LookupContext.h" +#include "ResolveExpression.h" +#include "Overview.h" +#include "CppBindings.h" + +#include +#include +#include +#include +#include +#include + +#include + +uint CPlusPlus::qHash(const CPlusPlus::LookupItem &key) +{ + const uint h1 = QT_PREPEND_NAMESPACE(qHash)(key.type().type()); + const uint h2 = QT_PREPEND_NAMESPACE(qHash)(key.lastVisibleSymbol()); + return ((h1 << 16) | (h1 >> 16)) ^ h2; +} + +using namespace CPlusPlus; + +///////////////////////////////////////////////////////////////////// +// LookupContext +///////////////////////////////////////////////////////////////////// +LookupContext::LookupContext(Control *control) + : _control(control), + _symbol(0) +{ } + +LookupContext::LookupContext(Symbol *symbol, + Document::Ptr expressionDocument, + Document::Ptr thisDocument, + const Snapshot &snapshot) + : _symbol(symbol), + _expressionDocument(expressionDocument), + _thisDocument(thisDocument), + _snapshot(snapshot) +{ + _control = _expressionDocument->control(); + _visibleScopes = buildVisibleScopes(); +} + +bool LookupContext::isValid() const +{ return _control != 0; } + +Control *LookupContext::control() const +{ return _control; } + +Symbol *LookupContext::symbol() const +{ return _symbol; } + +Document::Ptr LookupContext::expressionDocument() const +{ return _expressionDocument; } + +Document::Ptr LookupContext::thisDocument() const +{ return _thisDocument; } + +Document::Ptr LookupContext::document(const QString &fileName) const +{ return _snapshot.document(fileName); } + +Snapshot LookupContext::snapshot() const +{ return _snapshot; } + +bool LookupContext::maybeValidSymbol(Symbol *symbol, + ResolveMode mode, + const QList &candidates) +{ + if (((mode & ResolveNamespace) && symbol->isNamespace()) || + ((mode & ResolveClass) && symbol->isClass()) || + ((mode & ResolveObjCClass) && symbol->isObjCClass()) || + ((mode & ResolveObjCProtocol) && symbol->isObjCProtocol()) || + (mode & ResolveSymbol)) { + return ! candidates.contains(symbol); + } + + return false; +} + +QList LookupContext::resolveNestedNameSpecifier(const QualifiedNameId *q, + const QList &visibleScopes) const +{ + QList candidates; + QList scopes = visibleScopes; + + for (unsigned i = 0; i < q->nameCount() - 1; ++i) { + const Name *name = q->nameAt(i); + + candidates = resolveClassOrNamespace(name, scopes); + + if (candidates.isEmpty()) + break; + + scopes.clear(); + + foreach (Symbol *candidate, candidates) { + ScopedSymbol *scoped = candidate->asScopedSymbol(); + Scope *members = scoped->members(); + + if (! scopes.contains(members)) + scopes.append(members); + } + } + + return scopes; +} + +QList LookupContext::resolveQualifiedNameId(const QualifiedNameId *q, + const QList &visibleScopes, + ResolveMode mode) const +{ + QList candidates; + + if (true || mode & ResolveClass) { + for (int i = 0; i < visibleScopes.size(); ++i) { + Scope *scope = visibleScopes.at(i); + + for (Symbol *symbol = scope->lookat(q); symbol; symbol = symbol->next()) { + if (! symbol->name()) + continue; + else if (! symbol->isClass()) + continue; + + const QualifiedNameId *qq = symbol->name()->asQualifiedNameId(); + + if (! qq) + continue; + else if (! maybeValidSymbol(symbol, mode, candidates)) + continue; + + if (! q->unqualifiedNameId()->isEqualTo(qq->unqualifiedNameId())) + continue; + + else if (qq->nameCount() == q->nameCount()) { + unsigned j = 0; + + for (; j < q->nameCount(); ++j) { + const Name *classOrNamespaceName1 = q->nameAt(j); + const Name *classOrNamespaceName2 = qq->nameAt(j); + + if (! classOrNamespaceName1->isEqualTo(classOrNamespaceName2)) + break; + } + + if (j == q->nameCount()) + candidates.append(symbol); + } + } + } + } + + QList scopes; + + if (q->nameCount() == 1) + scopes = visibleScopes; // ### handle global scope lookup + else + scopes = resolveNestedNameSpecifier(q, visibleScopes); + + QList expanded; + foreach (Scope *scope, scopes) { + expanded.append(scope); + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *member = scope->symbolAt(i); + + if (ScopedSymbol *scopedSymbol = member->asScopedSymbol()) + expandEnumOrAnonymousSymbol(scopedSymbol, &expanded); + } + } + + candidates += resolve(q->unqualifiedNameId(), expanded, mode); + + return candidates; +} + +QList LookupContext::resolveOperatorNameId(const OperatorNameId *opId, + const QList &visibleScopes, + ResolveMode) const +{ + QList candidates; + + for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) { + Scope *scope = visibleScopes.at(scopeIndex); + + for (Symbol *symbol = scope->lookat(opId->kind()); symbol; symbol = symbol->next()) { + if (! opId->isEqualTo(symbol->name())) + continue; + + if (! candidates.contains(symbol)) + candidates.append(symbol); + } + } + + return candidates; +} + +QList LookupContext::resolve(const Name *name, const QList &visibleScopes, + ResolveMode mode) const +{ + QList candidates; + + if (!name) + return candidates; // nothing to do, the symbol is anonymous. + + else if (const QualifiedNameId *q = name->asQualifiedNameId()) + return resolveQualifiedNameId(q, visibleScopes, mode); + + else if (const OperatorNameId *opId = name->asOperatorNameId()) + return resolveOperatorNameId(opId, visibleScopes, mode); + + else if (const Identifier *id = name->identifier()) { + for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) { + Scope *scope = visibleScopes.at(scopeIndex); + + for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) { + if (! symbol->name()) + continue; // nothing to do, the symbol is anonymous. + + else if (! maybeValidSymbol(symbol, mode, candidates)) + continue; // skip it, we're not looking for this kind of symbols + + else if (const Identifier *symbolId = symbol->identifier()) { + if (! symbolId->isEqualTo(id)) + continue; // skip it, the symbol's id is not compatible with this lookup. + } + + if (const QualifiedNameId *q = symbol->name()->asQualifiedNameId()) { + + if (name->isDestructorNameId() != q->unqualifiedNameId()->isDestructorNameId()) + continue; + + else if (q->nameCount() > 1) { + const Name *classOrNamespaceName = control()->qualifiedNameId(q->names(), + q->nameCount() - 1); + + if (const Identifier *classOrNamespaceNameId = identifier(classOrNamespaceName)) { + if (classOrNamespaceNameId->isEqualTo(id)) + continue; + } + + const QList resolvedClassOrNamespace = + resolveClassOrNamespace(classOrNamespaceName, visibleScopes); + + bool good = false; + foreach (Symbol *classOrNamespace, resolvedClassOrNamespace) { + ScopedSymbol *scoped = classOrNamespace->asScopedSymbol(); + if (visibleScopes.contains(scoped->members())) { + good = true; + break; + } + } + + if (! good) + continue; + } + } else if (symbol->name()->isDestructorNameId() != name->isDestructorNameId()) { + // ### FIXME: this is wrong! + continue; + } + + if (! candidates.contains(symbol)) + candidates.append(symbol); + } + } + } + + return candidates; +} + +const Identifier *LookupContext::identifier(const Name *name) const +{ + if (name) + return name->identifier(); + + return 0; +} + +void LookupContext::buildVisibleScopes_helper(Document::Ptr doc, QList *scopes, + QSet *processed) +{ + if (doc && ! processed->contains(doc->fileName())) { + processed->insert(doc->fileName()); + + if (doc->globalSymbolCount()) + scopes->append(doc->globalSymbols()); + + foreach (const Document::Include &incl, doc->includes()) { + buildVisibleScopes_helper(_snapshot.document(incl.fileName()), + scopes, processed); + } + } +} + +QList LookupContext::buildVisibleScopes() +{ + QList scopes; + + if (_symbol) { + Scope *scope = _symbol->scope(); + + if (Function *fun = _symbol->asFunction()) + scope = fun->members(); // handle ctor initializers. + + for (; scope; scope = scope->enclosingScope()) { + if (scope == _thisDocument->globalSymbols()) + break; + + scopes.append(scope); + } + } + + QSet processed; + buildVisibleScopes_helper(_thisDocument, &scopes, &processed); + + while (true) { + QList expandedScopes; + expand(scopes, &expandedScopes); + + if (expandedScopes.size() == scopes.size()) + return expandedScopes; + + scopes = expandedScopes; + } + + return scopes; +} + +QList LookupContext::visibleScopes(const LookupItem &result) const +{ return visibleScopes(result.lastVisibleSymbol()); } + +QList LookupContext::visibleScopes(Symbol *symbol) const +{ + QList scopes; + if (symbol) { + for (Scope *scope = symbol->scope(); scope; scope = scope->enclosingScope()) + scopes.append(scope); + } + scopes += visibleScopes(); + scopes = expand(scopes); + return scopes; +} + +void LookupContext::expandEnumOrAnonymousSymbol(ScopedSymbol *scopedSymbol, + QList *expandedScopes) const +{ + if (! scopedSymbol || expandedScopes->contains(scopedSymbol->members())) + return; + + Scope *members = scopedSymbol->members(); + + if (scopedSymbol->isEnum()) + expandedScopes->append(members); + else if (! scopedSymbol->name() && (scopedSymbol->isClass() || scopedSymbol->isNamespace())) { + // anonymous class or namespace + + expandedScopes->append(members); + + for (unsigned i = 0; i < members->symbolCount(); ++i) { + Symbol *member = members->symbolAt(i); + + if (ScopedSymbol *nested = member->asScopedSymbol()) { + expandEnumOrAnonymousSymbol(nested, expandedScopes); + } + } + } +} + +QList LookupContext::expand(const QList &scopes) const +{ + QList expanded; + expand(scopes, &expanded); + return expanded; +} + +void LookupContext::expand(const QList &scopes, QList *expandedScopes) const +{ + for (int i = 0; i < scopes.size(); ++i) { + expand(scopes.at(i), scopes, expandedScopes); + } +} + +void LookupContext::expandNamespace(Namespace *ns, + const QList &visibleScopes, + QList *expandedScopes) const +{ + //qDebug() << "*** expand namespace:" << ns->fileName() << ns->line() << ns->column(); + + if (Scope *encl = ns->enclosingNamespaceScope()) + expand(encl, visibleScopes, expandedScopes); + + if (const Name *nsName = ns->name()) { + const QList namespaceList = resolveNamespace(nsName, visibleScopes); + foreach (Symbol *otherNs, namespaceList) { + if (otherNs == ns) + continue; + expand(otherNs->asNamespace()->members(), visibleScopes, expandedScopes); + } + } + + for (unsigned i = 0; i < ns->memberCount(); ++i) { // ### make me fast + Symbol *symbol = ns->memberAt(i); + if (Namespace *otherNs = symbol->asNamespace()) { + if (! otherNs->name()) { + expand(otherNs->members(), visibleScopes, expandedScopes); + } + } else if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) { + const QList candidates = resolveNamespace(u->name(), visibleScopes); + for (int j = 0; j < candidates.size(); ++j) { + expand(candidates.at(j)->asNamespace()->members(), + visibleScopes, expandedScopes); + } + } else if (Enum *e = symbol->asEnum()) { + expand(e->members(), visibleScopes, expandedScopes); + } + } +} + +void LookupContext::expandClass(Class *klass, + const QList &visibleScopes, + QList *expandedScopes) const +{ + for (TemplateParameters *params = klass->templateParameters(); params; params = params->previous()) + expand(params->scope(), visibleScopes, expandedScopes); + + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Symbol *symbol = klass->memberAt(i); + if (Class *nestedClass = symbol->asClass()) { + if (! nestedClass->name()) { + expand(nestedClass->members(), visibleScopes, expandedScopes); + } + } else if (Enum *e = symbol->asEnum()) { + expand(e->members(), visibleScopes, expandedScopes); + } + } + + if (klass->baseClassCount()) { + QList classVisibleScopes = visibleScopes; + for (Scope *scope = klass->scope(); scope; scope = scope->enclosingScope()) { + if (scope->isNamespaceScope()) { + Namespace *enclosingNamespace = scope->owner()->asNamespace(); + if (enclosingNamespace->name()) { + const QList nsList = resolveNamespace(enclosingNamespace->name(), + visibleScopes); + foreach (Symbol *ns, nsList) { + expand(ns->asNamespace()->members(), classVisibleScopes, + &classVisibleScopes); + } + } + } + } + + for (unsigned i = 0; i < klass->baseClassCount(); ++i) { + BaseClass *baseClass = klass->baseClassAt(i); + const Name *baseClassName = baseClass->name(); + const QList baseClassCandidates = resolveClass(baseClassName, + classVisibleScopes); + + for (int j = 0; j < baseClassCandidates.size(); ++j) { + if (Class *baseClassSymbol = baseClassCandidates.at(j)->asClass()) + expand(baseClassSymbol->members(), visibleScopes, expandedScopes); + } + } + } +} + +void LookupContext::expandBlock(Block *blockSymbol, + const QList &visibleScopes, + QList *expandedScopes) const +{ + for (unsigned i = 0; i < blockSymbol->memberCount(); ++i) { + Symbol *symbol = blockSymbol->memberAt(i); + if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) { + const QList candidates = resolveNamespace(u->name(), + visibleScopes); + for (int j = 0; j < candidates.size(); ++j) { + expand(candidates.at(j)->asNamespace()->members(), + visibleScopes, expandedScopes); + } + } + + } +} + +void LookupContext::expandFunction(Function *function, + const QList &visibleScopes, + QList *expandedScopes) const +{ + for (TemplateParameters *params = function->templateParameters(); params; params = params->previous()) + expand(params->scope(), visibleScopes, expandedScopes); + + if (! expandedScopes->contains(function->arguments())) + expandedScopes->append(function->arguments()); + + if (const QualifiedNameId *q = function->name()->asQualifiedNameId()) { + const Name *nestedNameSpec = 0; + if (q->nameCount() == 1) + nestedNameSpec = q->nameAt(0); + else + nestedNameSpec = control()->qualifiedNameId(q->names(), q->nameCount() - 1, + q->isGlobal()); + const QList candidates = resolveClassOrNamespace(nestedNameSpec, visibleScopes); + for (int j = 0; j < candidates.size(); ++j) { + if (ScopedSymbol *scopedSymbol = candidates.at(j)->asScopedSymbol()) + expand(scopedSymbol->members(), visibleScopes, expandedScopes); + } + } +} + +void LookupContext::expandObjCMethod(ObjCMethod *method, + const QList &, + QList *expandedScopes) const +{ + if (! expandedScopes->contains(method->arguments())) + expandedScopes->append(method->arguments()); +} + +void LookupContext::expandObjCClass(ObjCClass *klass, + const QList &visibleScopes, + QList *expandedScopes) const +{ + {// expand other @interfaces, @implementations and categories for this class: + const QList classList = resolveObjCClass(klass->name(), visibleScopes); + foreach (Symbol *otherClass, classList) { + if (otherClass == klass) + continue; + expand(otherClass->asObjCClass()->members(), visibleScopes, expandedScopes); + } + } + + // expand definitions in the currect class: + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Symbol *symbol = klass->memberAt(i); + if (Class *nestedClass = symbol->asClass()) { + if (! nestedClass->name()) { + expand(nestedClass->members(), visibleScopes, expandedScopes); + } + } else if (Enum *e = symbol->asEnum()) { + expand(e->members(), visibleScopes, expandedScopes); + } + } + + // expand the base class: + if (ObjCBaseClass *baseClass = klass->baseClass()) { + const Name *baseClassName = baseClass->name(); + const QList baseClassCandidates = resolveObjCClass(baseClassName, + visibleScopes); + + for (int j = 0; j < baseClassCandidates.size(); ++j) { + if (ObjCClass *baseClassSymbol = baseClassCandidates.at(j)->asObjCClass()) + expand(baseClassSymbol->members(), visibleScopes, expandedScopes); + } + } + + // expand the protocols: + for (unsigned i = 0; i < klass->protocolCount(); ++i) { + const Name *protocolName = klass->protocolAt(i)->name(); + const QList protocolCandidates = resolveObjCProtocol(protocolName, visibleScopes); + for (int j = 0; j < protocolCandidates.size(); ++j) { + if (ObjCProtocol *protocolSymbol = protocolCandidates.at(j)->asObjCProtocol()) + expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes); + } + } +} + +void LookupContext::expandObjCProtocol(ObjCProtocol *protocol, const QList &visibleScopes, QList *expandedScopes) const +{ + // First expand the protocol itself + expand(protocol->members(), visibleScopes, expandedScopes); + + // Then do the same for any incorporated protocol + for (unsigned i = 0; i < protocol->protocolCount(); ++i) { + ObjCBaseProtocol *baseProtocol = protocol->protocolAt(i); + const QList protocolList = resolveObjCProtocol(baseProtocol->name(), visibleScopes); + foreach (Symbol *symbol, protocolList) + if (ObjCProtocol *protocolSymbol = symbol->asObjCProtocol()) + expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes); + } +} + +void LookupContext::expand(Scope *scope, + const QList &visibleScopes, + QList *expandedScopes) const +{ + if (expandedScopes->contains(scope)) + return; + + expandedScopes->append(scope); + + if (Namespace *ns = scope->owner()->asNamespace()) { + expandNamespace(ns, visibleScopes, expandedScopes); + } else if (Class *klass = scope->owner()->asClass()) { + expandClass(klass, visibleScopes, expandedScopes); + } else if (Block *block = scope->owner()->asBlock()) { + expandBlock(block, visibleScopes, expandedScopes); + } else if (Function *fun = scope->owner()->asFunction()) { + expandFunction(fun, visibleScopes, expandedScopes); + } else if (ObjCMethod *meth = scope->owner()->asObjCMethod()) { + expandObjCMethod(meth, visibleScopes, expandedScopes); + } else if (ObjCClass *objcKlass = scope->owner()->asObjCClass()) { + expandObjCClass(objcKlass, visibleScopes, expandedScopes); + } +} + +static void visibleClassBindings_helper(ClassBinding *classBinding, + QList *allClassBindings, + QSet *processed) +{ + if (! classBinding) + return; + + else if (processed->contains(classBinding)) + return; + + processed->insert(classBinding); + + foreach (ClassBinding *baseClassBinding, classBinding->baseClassBindings) + visibleClassBindings_helper(baseClassBinding, allClassBindings, processed); + + allClassBindings->append(classBinding); +} + +static QList visibleClassBindings(Symbol *symbol, NamespaceBinding *globalNamespace) +{ + QList classBindings; + + if (! symbol) + return classBindings; + + else if (Class *klass = symbol->asClass()) { + QSet processed; + + visibleClassBindings_helper(NamespaceBinding::find(klass, globalNamespace), + &classBindings, &processed); + } + + return classBindings; +} + +Symbol *LookupContext::canonicalSymbol(Symbol *symbol, + NamespaceBinding *globalNamespace) +{ + Symbol *canonicalSymbol = LookupContext::canonicalSymbol(symbol); + if (! canonicalSymbol) + return 0; + + if (const Identifier *symbolId = canonicalSymbol->identifier()) { + if (symbolId && canonicalSymbol->type()->isFunctionType()) { + Class *enclosingClass = canonicalSymbol->scope()->owner()->asClass(); + const QList classBindings = visibleClassBindings(enclosingClass, globalNamespace); + + foreach (ClassBinding *baseClassBinding, classBindings) { + if (! baseClassBinding) + continue; + + foreach (Class *baseClass, baseClassBinding->symbols) { + if (! baseClass) + continue; + + for (Symbol *c = baseClass->members()->lookat(symbolId); c; c = c->next()) { + if (! symbolId->isEqualTo(c->identifier())) + continue; + else if (Function *f = c->type()->asFunctionType()) { + if (f->isVirtual()) + return LookupContext::canonicalSymbol(f); + } + } + } + } + } + } + + return canonicalSymbol; +} + +Symbol *LookupContext::canonicalSymbol(const QList &candidates, + NamespaceBinding *globalNamespaceBinding) +{ + if (candidates.isEmpty()) + return 0; + + return canonicalSymbol(candidates.first(), globalNamespaceBinding); +} + +Symbol *LookupContext::canonicalSymbol(const QList &results, + NamespaceBinding *globalNamespaceBinding) +{ + QList candidates; + + foreach (const LookupItem &result, results) + candidates.append(result.lastVisibleSymbol()); // ### not exactly. + + return canonicalSymbol(candidates, globalNamespaceBinding); +} + + +Symbol *LookupContext::canonicalSymbol(Symbol *symbol) +{ + Symbol *canonical = symbol; + Class *canonicalClass = 0; + ObjCClass *canonicalObjCClass = 0; + ObjCProtocol *canonicalObjCProto = 0; + + for (; symbol; symbol = symbol->next()) { + if (symbol->identifier() == canonical->identifier()) { + canonical = symbol; + + if (Class *klass = symbol->asClass()) + canonicalClass = klass; + else if (ObjCClass *clazz = symbol->asObjCClass()) + canonicalObjCClass = clazz; + else if (ObjCProtocol *proto = symbol->asObjCProtocol()) + canonicalObjCProto = proto; + } + } + + if (canonicalClass) { + Q_ASSERT(canonical != 0); + + if (canonical->isForwardClassDeclaration()) + return canonicalClass; // prefer class declarations when available. + } else if (canonicalObjCClass) { + Q_ASSERT(canonical != 0); + + if (canonical->isObjCForwardClassDeclaration()) + return canonicalObjCClass; + } else if (canonicalObjCProto) { + Q_ASSERT(canonical != 0); + + if (canonical->isObjCForwardProtocolDeclaration()) + return canonicalObjCProto; + } + + if (canonical && canonical->scope()->isClassScope()) { + Class *enclosingClass = canonical->scope()->owner()->asClass(); + + if (enclosingClass->identifier() == canonical->identifier()) + return enclosingClass; + } + + return canonical; +}