diff --git a/src/libs/cplusplus/CppBindings.cpp b/src/libs/cplusplus/CppBindings.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d8531fd5158583c0c19ab98ce1842a1bec15757 --- /dev/null +++ b/src/libs/cplusplus/CppBindings.cpp @@ -0,0 +1,569 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#include "CppBindings.h" +#include "CppDocument.h" +#include "Overview.h" + +#include <CoreTypes.h> +#include <Symbols.h> +#include <Literals.h> +#include <Names.h> +#include <Scope.h> +#include <Control.h> +#include <SymbolVisitor.h> + +#include <QtDebug> + +using namespace CPlusPlus; + + +//////////////////////////////////////////////////////////////////////////////// +// Location +//////////////////////////////////////////////////////////////////////////////// +Location::Location() + : _fileId(0), + _sourceLocation(0) +{ } + +Location::Location(Symbol *symbol) + : _fileId(symbol->fileId()), + _sourceLocation(symbol->sourceLocation()) +{ } + +Location::Location(StringLiteral *fileId, unsigned sourceLocation) + : _fileId(fileId), _sourceLocation(sourceLocation) +{ } + +//////////////////////////////////////////////////////////////////////////////// +// NamespaceBinding +//////////////////////////////////////////////////////////////////////////////// + +NamespaceBinding::NamespaceBinding(NamespaceBinding *parent) + : parent(parent), + anonymousNamespaceBinding(0) +{ + if (parent) + parent->children.append(this); +} + +NamespaceBinding::~NamespaceBinding() +{ + qDeleteAll(children); + qDeleteAll(classBindings); +} + +NameId *NamespaceBinding::name() const +{ + if (symbols.size()) { + if (Name *name = symbols.at(0)->name()) { + NameId *nameId = name->asNameId(); + Q_ASSERT(nameId != 0); + + return nameId; + } + } + + return 0; +} + +Identifier *NamespaceBinding::identifier() const +{ + if (NameId *nameId = name()) + return nameId->identifier(); + + return 0; +} + +NamespaceBinding *NamespaceBinding::globalNamespaceBinding() +{ + NamespaceBinding *it = this; + + for (; it; it = it->parent) { + if (! it->parent) + break; + } + + return it; +} + +NamespaceBinding *NamespaceBinding::findNamespaceBinding(Name *name) +{ + if (! name) + return anonymousNamespaceBinding; + + else if (NameId *nameId = name->asNameId()) + return findNamespaceBindingForNameId(nameId); + + // invalid binding + return 0; +} + +NamespaceBinding *NamespaceBinding::findNamespaceBindingForNameId(NameId *name) +{ + foreach (NamespaceBinding *binding, children) { + Name *bindingName = binding->name(); + + if (! bindingName) + continue; + + if (NameId *bindingNameId = bindingName->asNameId()) { + if (name->isEqualTo(bindingNameId)) + return binding; + } + } + + return 0; +} + +NamespaceBinding *NamespaceBinding::findOrCreateNamespaceBinding(Namespace *symbol) +{ + if (NamespaceBinding *binding = findNamespaceBinding(symbol->name())) { + int index = 0; + + for (; index < binding->symbols.size(); ++index) { + Namespace *ns = binding->symbols.at(index); + + if (ns == symbol) + break; + } + + if (index == binding->symbols.size()) + binding->symbols.append(symbol); + + return binding; + } + + NamespaceBinding *binding = new NamespaceBinding(this); + binding->symbols.append(symbol); + + if (! symbol->name()) { + Q_ASSERT(! anonymousNamespaceBinding); + + anonymousNamespaceBinding = binding; + } + + return binding; +} + +static void closure(const Location &loc, + NamespaceBinding *binding, Name *name, + QList<NamespaceBinding *> *bindings) +{ + if (bindings->contains(binding)) + return; + + bindings->append(binding); + + Q_ASSERT(name->isNameId()); + + Identifier *id = name->asNameId()->identifier(); + bool ignoreUsingDirectives = false; + + foreach (Namespace *symbol, binding->symbols) { + Scope *scope = symbol->members(); + + for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) { + if (symbol->name() != name || ! symbol->isNamespace()) + continue; + + const Location l(symbol); + + if (l.fileId() == loc.fileId() && l.sourceLocation() < loc.sourceLocation()) { + ignoreUsingDirectives = true; + break; + } + } + } + + if (ignoreUsingDirectives) + return; + + foreach (NamespaceBinding *u, binding->usings) + closure(loc, u, name, bindings); +} + + +NamespaceBinding *NamespaceBinding::resolveNamespace(const Location &loc, + Name *name, + bool lookAtParent) +{ + if (! name) + return 0; + + else if (NameId *nameId = name->asNameId()) { + QList<NamespaceBinding *> bindings; + closure(loc, this, nameId, &bindings); + + QList<NamespaceBinding *> results; + + foreach (NamespaceBinding *binding, bindings) { + if (NamespaceBinding *b = binding->findNamespaceBinding(nameId)) + results.append(b); + } + + if (results.size() == 1) + return results.at(0); + + else if (results.size() > 1) { + // ### FIXME: return 0; + return results.at(0); + } + + else if (parent && lookAtParent) + return parent->resolveNamespace(loc, name); + + } else if (QualifiedNameId *q = name->asQualifiedNameId()) { + if (q->nameCount() == 1) { + Q_ASSERT(q->isGlobal()); + + return globalNamespaceBinding()->resolveNamespace(loc, q->nameAt(0)); + } + + NamespaceBinding *current = this; + if (q->isGlobal()) + current = globalNamespaceBinding(); + + current = current->resolveNamespace(loc, q->nameAt(0)); + for (unsigned i = 1; current && i < q->nameCount(); ++i) + current = current->resolveNamespace(loc, q->nameAt(i), false); + + return current; + } + + return 0; +} + +// ### rewrite me +QByteArray NamespaceBinding::qualifiedId() const +{ + if (! parent) + return "<root>"; + + QByteArray s; + + s.append(parent->qualifiedId()); + s.append("::"); + + if (Identifier *id = identifier()) + s.append(id->chars(), id->size()); + + else + s.append("<anonymous>"); + + return s; +} + +// ### rewrite me +QByteArray ClassBinding::qualifiedId() const +{ + QByteArray s = parent->qualifiedId(); + s += "::"; + + if (Identifier *id = identifier()) + s.append(id->chars(), id->size()); + + else + s.append("<anonymous>"); + + return s; +} + +static int depth; + +void NamespaceBinding::dump() +{ + qDebug() << QByteArray(depth, ' ').constData() << "namespace" << qualifiedId().constData() + << " # " << symbols.size(); + + ++depth; + + foreach (ClassBinding *classBinding, classBindings) { + classBinding->dump(); + } + + foreach (NamespaceBinding *child, children) { + child->dump(); + } + + --depth; +} + +void ClassBinding::dump() +{ + qDebug() << QByteArray(depth, ' ').constData() << "class" << qualifiedId().constData() + << " # " << symbols.size(); + + ++depth; + + foreach (ClassBinding *classBinding, children) { + classBinding->dump(); + } + + --depth; +} + +//////////////////////////////////////////////////////////////////////////////// +// ClassBinding +//////////////////////////////////////////////////////////////////////////////// +ClassBinding::ClassBinding(NamespaceBinding *parent) + : parent(parent) +{ + parent->classBindings.append(this); +} + +ClassBinding::ClassBinding(ClassBinding *parentClass) +{ + parent = parentClass->parent; + parentClass->children.append(this); +} + +ClassBinding::~ClassBinding() +{ qDeleteAll(children); } + +NameId *ClassBinding::name() const +{ + if (symbols.size()) { + if (Name *name = symbols.at(0)->name()) { + NameId *nameId = name->asNameId(); + return nameId; + } + } + + return 0; +} + +Identifier *ClassBinding::identifier() const +{ + if (NameId *nameId = name()) + return nameId->identifier(); + + return 0; +} + +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// Binder +//////////////////////////////////////////////////////////////////////////////// + +class Binder: protected SymbolVisitor +{ +public: + Binder(NamespaceBinding *globals); + virtual ~Binder(); + + NamespaceBinding *operator()(Document::Ptr doc, const Snapshot &snapshot) + { + namespaceBinding = _globals; + const Snapshot previousSnapshot = _snapshot; + + _snapshot = snapshot; + (void) bind(doc); + _snapshot = previousSnapshot; + + return _globals; + } + + Snapshot _snapshot; + +protected: + NamespaceBinding *bind(Document::Ptr doc) + { + QSet<QString> processed; + return bind(doc, &processed); + } + + NamespaceBinding *bind(Document::Ptr doc, QSet<QString> *processed) + { + if (processed->contains(doc->fileName())) + return 0; + + processed->insert(doc->fileName()); + + foreach (const Document::Include &i, doc->includes()) { + if (Document::Ptr includedDoc = _snapshot.value(i.fileName())) { + /*NamepaceBinding *binding = */ bind(includedDoc, processed); + } + } + + Namespace *ns = doc->globalNamespace(); + _globals->symbols.append(ns); + + for (unsigned i = 0; i < ns->memberCount(); ++i) { + (void) bind(ns->memberAt(i), _globals); + } + + return _globals; + } + + NamespaceBinding *bind(Symbol *symbol, NamespaceBinding *binding); + NamespaceBinding *findOrCreateNamespaceBinding(Namespace *symbol); + NamespaceBinding *resolveNamespace(const Location &loc, Name *name); + + NamespaceBinding *switchNamespaceBinding(NamespaceBinding *binding); + + ClassBinding *findOrCreateClassBinding(Class *classSymbol); + ClassBinding *findClassBinding(Name *name); + + ClassBinding *switchClassBinding(ClassBinding *binding); + + using SymbolVisitor::visit; + + virtual bool visit(Namespace *); + virtual bool visit(UsingNamespaceDirective *); + virtual bool visit(Class *); + virtual bool visit(Function *); + virtual bool visit(Block *); + +private: + NamespaceBinding *_globals; + NamespaceBinding *namespaceBinding; + ClassBinding *classBinding; +}; + +Binder::Binder(NamespaceBinding *globals) + : _globals(globals), + namespaceBinding(0), + classBinding(0) +{ } + +Binder::~Binder() +{ } + +NamespaceBinding *Binder::bind(Symbol *symbol, NamespaceBinding *binding) +{ + NamespaceBinding *previousBinding = switchNamespaceBinding(binding); + accept(symbol); + return switchNamespaceBinding(previousBinding); +} + +NamespaceBinding *Binder::findOrCreateNamespaceBinding(Namespace *symbol) +{ return namespaceBinding->findOrCreateNamespaceBinding(symbol); } + +NamespaceBinding *Binder::resolveNamespace(const Location &loc, Name *name) +{ + if (! namespaceBinding) + return 0; + + return namespaceBinding->resolveNamespace(loc, name); +} + +NamespaceBinding *Binder::switchNamespaceBinding(NamespaceBinding *binding) +{ + NamespaceBinding *previousBinding = namespaceBinding; + namespaceBinding = binding; + return previousBinding; +} + +ClassBinding *Binder::findOrCreateClassBinding(Class *classSymbol) +{ + ClassBinding *binding = 0; + + if (classBinding) + binding = new ClassBinding(classBinding); + else + binding = new ClassBinding(namespaceBinding); + + binding->symbols.append(classSymbol); + return binding; +} + +ClassBinding *Binder::findClassBinding(Name *name) +{ + qDebug() << Q_FUNC_INFO; + return 0; +} + +ClassBinding *Binder::switchClassBinding(ClassBinding *binding) +{ + ClassBinding *previousClassBinding = classBinding; + classBinding = binding; + return previousClassBinding; +} + +bool Binder::visit(Namespace *symbol) +{ + NamespaceBinding *binding = findOrCreateNamespaceBinding(symbol); + + for (unsigned i = 0; i < symbol->memberCount(); ++i) { + Symbol *member = symbol->memberAt(i); + + bind(member, binding); + } + + return false; +} + +bool Binder::visit(UsingNamespaceDirective *u) +{ + NamespaceBinding *resolved = resolveNamespace(Location(u), u->name()); + + if (! resolved) + return false; + + namespaceBinding->usings.append(resolved); + + return false; +} + +bool Binder::visit(Class *classSymbol) +{ + Overview oo; + + ClassBinding *binding = findOrCreateClassBinding(classSymbol); + ClassBinding *previousClassBinding = switchClassBinding(binding); + +#if 0 + for (unsigned i = 0; i < classSymbol->baseClassCount(); ++i) { + BaseClass *baseClass = classSymbol->baseClassAt(i); + + ClassBinding *baseClassBinding = findClassBinding(baseClass->name()); + + // ### wrong + binding->baseClassBindings.append(baseClassBinding); + } +#endif + + for (unsigned i = 0; i < classSymbol->memberCount(); ++i) + accept(classSymbol->memberAt(i)); + + (void) switchClassBinding(previousClassBinding); + + return false; +} + +bool Binder::visit(Function *) +{ return false; } + +bool Binder::visit(Block *) +{ return false; } + +} // end of anonymous namespace + diff --git a/src/libs/cplusplus/CppBindings.h b/src/libs/cplusplus/CppBindings.h new file mode 100644 index 0000000000000000000000000000000000000000..57c37f9192875c9df195dbd64ba3ae2b6b2f5702 --- /dev/null +++ b/src/libs/cplusplus/CppBindings.h @@ -0,0 +1,176 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#ifndef CPPBINDINGS_H +#define CPPBINDINGS_H + +#include <CPlusPlusForwardDeclarations.h> + +#include <QtCore/QList> +#include <QtCore/QSharedPointer> +#include <QtCore/QString> +#include <QtCore/QByteArray> + +namespace CPlusPlus { + +class Location; +class Binding; +class NamespaceBinding; +class ClassBinding; + +typedef QSharedPointer<Binding> BindingPtr; +typedef QSharedPointer<ClassBinding> ClassBindingPtr; +typedef QSharedPointer<NamespaceBinding> NamespaceBindingPtr; + +class CPLUSPLUS_EXPORT Location +{ +public: + Location(); + Location(Symbol *symbol); + Location(StringLiteral *fileId, unsigned sourceLocation); + + inline bool isValid() const + { return _fileId != 0; } + + inline operator bool() const + { return _fileId != 0; } + + inline StringLiteral *fileId() const + { return _fileId; } + + inline unsigned sourceLocation() const + { return _sourceLocation; } + +private: + StringLiteral *_fileId; + unsigned _sourceLocation; +}; + +class CPLUSPLUS_EXPORT Binding +{ + Q_DISABLE_COPY(Binding) + +public: + Binding() {} + virtual ~Binding() {} + + virtual NamespaceBinding *asNamespaceBinding() { return 0; } + virtual ClassBinding *asClassBinding() { return 0; } +}; + +class CPLUSPLUS_EXPORT NamespaceBinding: public Binding +{ +public: + /// Constructs a binding with the given parent. + NamespaceBinding(NamespaceBinding *parent = 0); + + /// Destroys the binding. + virtual ~NamespaceBinding(); + + /// Returns this binding's name. + NameId *name() const; + + /// Returns this binding's identifier. + Identifier *identifier() const; + + /// Returns the binding for the global namespace (aka ::). + NamespaceBinding *globalNamespaceBinding(); + + /// Returns the binding for the given namespace symbol. + NamespaceBinding *findNamespaceBinding(Name *name); + + /// Returns the binding associated with the given symbol. + NamespaceBinding *findOrCreateNamespaceBinding(Namespace *symbol); + + NamespaceBinding *resolveNamespace(const Location &loc, + Name *name, + bool lookAtParent = true); + + /// Helpers. + QByteArray qualifiedId() const; + void dump(); + + virtual NamespaceBinding *asNamespaceBinding() { return this; } + +private: + NamespaceBinding *findNamespaceBindingForNameId(NameId *name); + +public: // attributes + /// This binding's parent. + NamespaceBinding *parent; + + /// Binding for anonymous namespace symbols. + NamespaceBinding *anonymousNamespaceBinding; + + /// This binding's connections. + QList<NamespaceBinding *> children; + + /// This binding's list of using namespaces. + QList<NamespaceBinding *> usings; + + /// This binding's namespace symbols. + QList<Namespace *> symbols; + + QList<ClassBinding *> classBindings; +}; + +class CPLUSPLUS_EXPORT ClassBinding: public Binding +{ +public: + ClassBinding(NamespaceBinding *parent); + ClassBinding(ClassBinding *parentClass); + virtual ~ClassBinding(); + + virtual ClassBinding *asClassBinding() { return this; } + + /// Returns this binding's name. + NameId *name() const; + + /// Returns this binding's identifier. + Identifier *identifier() const; + + QByteArray qualifiedId() const; + + void dump(); + +public: // attributes + NamespaceBinding *parent; + + QList<ClassBinding *> children; + + /// This binding's class symbols. + QList<Class *> symbols; + + /// Bindings for the base classes. + QList<ClassBinding *> baseClassBindings; +}; + +} // end of namespace CPlusPlus + +#endif // CPPBINDINGS_H diff --git a/src/libs/cplusplus/cplusplus-lib.pri b/src/libs/cplusplus/cplusplus-lib.pri index 3720d7f6ef1d8630844aa7489c62fa4adeaefd6e..4e29413f0258f24ee0601ed6b5c078af77a0aa0f 100644 --- a/src/libs/cplusplus/cplusplus-lib.pri +++ b/src/libs/cplusplus/cplusplus-lib.pri @@ -26,6 +26,7 @@ HEADERS += \ $$PWD/TypePrettyPrinter.h \ $$PWD/ResolveExpression.h \ $$PWD/LookupContext.h \ + $$PWD/CppBindings.h \ $$PWD/PreprocessorClient.h \ $$PWD/PreprocessorEnvironment.h \ $$PWD/Macro.h \ @@ -44,6 +45,7 @@ SOURCES += \ $$PWD/TypePrettyPrinter.cpp \ $$PWD/ResolveExpression.cpp \ $$PWD/LookupContext.cpp \ + $$PWD/CppBindings.cpp \ $$PWD/PreprocessorClient.cpp \ $$PWD/PreprocessorEnvironment.cpp \ $$PWD/Macro.cpp \