diff --git a/languages/cpp/cppduchain/cppducontext.h b/languages/cpp/cppduchain/cppducontext.h index 81d0f5b044..d87b8832d1 100644 --- a/languages/cpp/cppduchain/cppducontext.h +++ b/languages/cpp/cppduchain/cppducontext.h @@ -1,757 +1,759 @@ /* This file is part of KDevelop Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Some mindmapping about how the template-system works: While construction: - Simplify: Template-parameters are types - Within template-contexts, do not resolve any types. Instead create "virtual types" that will resolve the types when template-parameters are given. (DelayedType) - ready Later: - Searching instantiated template-class: - return virtual declaration - return virtual context (Change template-parameter-context to given template-arguments) - Searching IN instantiated template-class: - When searching local declarations: - Check whether they are already in the instantiated context, if yes return them - If not, Search in non-instantiated context(only for local declarations), then: - Copy & Change returned objects: - Resolve virtual types (DelayedType) - Change parent-context to virtual context - Change internal context, (create virtual, move set parent) - How template-parameters are resolved: - The DUContext's with type DUContext::Template get their template-parameter declarations instantiated and added locally. Then they will be found when resolving virtual types. - */ #ifndef CPPDUCONTEXT_H #define CPPDUCONTEXT_H #include #include #include #include #include #include #include #include #include #include "typeutils.h" #include "templatedeclaration.h" #include "expressionevaluationresult.h" #include "cppduchain.h" #include "cpptypes.h" #include "templatedeclaration.h" #include "cppdebughelper.h" using namespace KDevelop; namespace Cpp { extern QMutex cppDuContextInstantiationsMutex; ///This class breaks up the logic of searching a declaration in C++, so QualifiedIdentifiers as well as AST-based lookup mechanisms can be used for searching class FindDeclaration { public: FindDeclaration( const DUContext* ctx, const TopDUContext* source, DUContext::SearchFlags flags, const CursorInRevision& position, AbstractType::Ptr dataType = AbstractType::Ptr() ) : m_context(ctx), m_source(source), m_flags(flags), m_position(position), m_dataType(dataType) { Q_ASSERT(m_source); } void openQualifiedIdentifier( bool isExplicitlyGlobal ) { StatePtr s(new State); s->identifier.setExplicitlyGlobal( isExplicitlyGlobal ); m_states << s; } ///Can be used to just append a result that was computed outside. closeQualifiedIdentifier(...) must still be called. void openQualifiedIdentifier( const ExpressionEvaluationResult& result ) { StatePtr s(new State); s->expressionResult = result; s->result.clear(); foreach(const DeclarationId& decl, result.allDeclarations) s->result << DeclarationPointer( decl.getDeclaration(const_cast(topContext())) ); m_states << s; } /** * After this was called, lastDeclarations(..) can be used to retrieve declarations of the qualified identifier. * The DUChain needs to be locked when this is called. * */ void closeQualifiedIdentifier(); /** * The identifier must not have template identifiers, those need to be added using openQualifiedIdentifier(..) and closeQualifiedIdentifier(..) * */ void openIdentifier( const Identifier& identifier ) { m_states.top()->identifier.push(identifier); } /** * When closeIdentifier() is called, the last opened identifier is searched, and can be retrieved using lastDeclarations(). * Returns false when the search should be stopped. * The DUChain needs to be locked when this is called. * @param isFinalIdentifier Whether the closed identifier the last one. Needed to know when to apply what search-flags. * */ bool closeIdentifier(bool isFinalIdentifier); ///For debugging. Describes the last search context. QString describeLastContext() const { if( m_lastScopeContext ) { return "Context " + m_lastScopeContext->scopeIdentifier(true).toString() + QString(" from %1:%2").arg(m_lastScopeContext->url().str()).arg(m_lastScopeContext->range().start.line); } else { return QString("Global search with top-context %2").arg(topContext()->url().str()); } } /** * Returns the Declarations found for the last closed qualified identifier. * * */ QList lastDeclarations() const { return m_lastDeclarations; } const TopDUContext* topContext() const { return m_source; } uint currentLength() const { return m_states.size(); } private: ///Uses the instantiation-information from the context of decl as parent of templateArguments. Declaration* instantiateDeclaration( Declaration* decl, const InstantiationInformation& templateArguments ) const; struct State : public KShared { State() { } QualifiedIdentifier identifier; //identifier including eventual namespace prefix InstantiationInformation templateParameters; ///One of the following is filled QList result; ExpressionEvaluationResult expressionResult; }; typedef KSharedPtr StatePtr; QStack m_states; const DUContext* m_context; const TopDUContext* m_source; DUContext::SearchFlags m_flags; QList m_lastDeclarations; CursorInRevision m_position; AbstractType::Ptr m_dataType; DUContextPointer m_lastScopeContext; //For debugging, last context in which we searched }; /** * This is a du-context template that wraps the c++-specific logic around existing DUContext-derived classes. * In practice this means DUContext and TopDUContext. * */ template class CppDUContext : public BaseContext { public: template CppDUContext(Data& data) : BaseContext(data), m_instantiatedFrom(0) { } ///Parameters will be reached to the base-class template CppDUContext( const Param1& p1, const Param2& p2, bool isInstantiationContext ) : BaseContext(p1, p2, isInstantiationContext), m_instantiatedFrom(0) { static_cast(this)->d_func_dynamic()->setClassId(this); } ///Both parameters will be reached to the base-class. This fits TopDUContext. template CppDUContext( const Param1& p1, const Param2& p2, const Param3& p3) : BaseContext(p1, p2, p3), m_instantiatedFrom(0) { static_cast(this)->d_func_dynamic()->setClassId(this); } template CppDUContext( const Param1& p1, const Param2& p2) : BaseContext(p1, p2), m_instantiatedFrom(0) { static_cast(this)->d_func_dynamic()->setClassId(this); } ///Matches the qualified identifier represented by the search item to the tail of the context's scope identfier ///Also matches searches without template-parameters to fully instantiated contexts ///Returns true if they match inline bool matchSearchItem(DUContext::SearchItem::Ptr item, const DUContext* ctx) const { DUContext::SearchItem::PtrList items; while(1) { items << item; if(!item->next.isEmpty()) item = item->next[0]; else break; } while(ctx && !items.isEmpty()) { QualifiedIdentifier localId = ctx->localScopeIdentifier(); if(localId.isEmpty()) return false; int matchPos = localId.count()-1; while(!items.isEmpty() && matchPos >= 0) { if(items.back()->identifier.templateIdentifiersCount()) return false; //Don't match when there is template parameters, as that needs other mechanisms if((!items.back()->identifier.templateIdentifiersCount() && items.back()->identifier.identifier() == localId.at(matchPos).identifier()) || items.back()->identifier == localId.at(matchPos)) { --matchPos; items.resize(items.size()-1); }else{ return false; } } if(items.isEmpty()) return true; ctx = ctx->parentContext(); } return false; } ///Overridden to take care of templates and other c++ specific things virtual bool findDeclarationsInternal(const DUContext::SearchItem::PtrList& identifiers, const CursorInRevision& position, const AbstractType::Ptr& dataType, DUContext::DeclarationList& ret, const TopDUContext* source, typename BaseContext::SearchFlags basicFlags, uint depth ) const { if(this->type() == DUContext::Class && identifiers.count() == 1 && !(basicFlags & DUContext::NoSelfLookUp) && !(basicFlags & DUContext::OnlyFunctions) && this->localScopeIdentifier().count() && !identifiers[0]->isExplicitlyGlobal) { //Check whether we're searching for just the name of this context's class. If yes, return this context's owner. if(matchSearchItem(identifiers[0], this)) { Declaration* owner = this->owner(); if(owner) { if(basicFlags & DUContext::NoUndefinedTemplateParams) { //If no undefined template parameters are allowed, make sure this template has all parameters assigned. TemplateDeclaration* templateOwner = dynamic_cast(this->owner()); if(templateOwner) { if(!templateOwner->instantiatedFrom()) return false; DUContext* templateContext = templateOwner->templateContext(source); if(templateContext) { foreach(Declaration* decl, templateContext->localDeclarations()) { if(decl->type()) { return false; } } } } } ret << this->owner(); return true; } } } if( basicFlags & BaseContext::DirectQualifiedLookup ) { //ifDebug( kDebug(9007) << "redirecting findDeclarationsInternal in " << this << "(" << this->scopeIdentifier() <<") for \"" << identifier.toString() << "\""; ) //We use DirectQualifiedLookup to signal that we don't need to do the whole scope-search, template-resolution etc. logic. return BaseContext::findDeclarationsInternal(identifiers, position, dataType, ret, source, basicFlags, depth ); } FOREACH_ARRAY( const DUContext::SearchItem::Ptr& item, identifiers ) foreach( const QualifiedIdentifier& id, item->toList() ) if(!findDeclarationsInternal(id, position, dataType, ret, source, basicFlags)) return false; // Remove all forward-declarations if there is a real declaration in the list bool haveForwardDeclaration = false; bool haveNonForwardDeclaration = false; for(int a = 0; a < ret.size(); ++a) { if(ret[a]->isForwardDeclaration()) haveForwardDeclaration = true; else haveNonForwardDeclaration = true; } if(haveForwardDeclaration && haveNonForwardDeclaration) { DUContext::DeclarationList oldRet = ret; ret.clear(); for(int a = 0; a < oldRet.size(); ++a) if(!oldRet[a]->isForwardDeclaration()) ret.append(oldRet[a]); } return true; } bool findDeclarationsInternal(const QualifiedIdentifier& identifier, const CursorInRevision& position, const AbstractType::Ptr& dataType, DUContext::DeclarationList& ret, const TopDUContext* source, typename BaseContext::SearchFlags basicFlags) const { ifDebug( kDebug(9007) << "findDeclarationsInternal in " << this << "(" << this->scopeIdentifier() <<") for \"" << identifier.toString() << "\""; ) FindDeclaration find( this, source, basicFlags, position, dataType ); find.openQualifiedIdentifier( identifier.explicitlyGlobal() ); int idCount = identifier.count(); for( int num = 0; num < idCount; num++ ) { { Identifier current = identifier.at(num); current.clearTemplateIdentifiers(); find.openIdentifier(current); } { Identifier currentIdentifier = identifier.at(num); ///Step 1: Resolve the template-arguments //Since there may be non-type template-parameters, represent them as ExpressionEvaluationResult's int tempCount = currentIdentifier.templateIdentifiersCount(); for( int a = 0; a < tempCount; a++ ) { //Use the already available mechanism for resolving delayed types Cpp::ExpressionEvaluationResult res; IndexedTypeIdentifier i = currentIdentifier.templateIdentifier(a); //If the identifier is empty, it is probably just a mark that a template should be instantiated, but without explicit paremeters. QualifiedIdentifier qid(i.identifier().identifier()); if( !qid.isEmpty() ) { DelayedType::Ptr delayed( new DelayedType() ); delayed->setIdentifier( i ); - res.type = Cpp::resolveDelayedTypes( delayed.cast(), this, source, basicFlags & KDevelop::DUContext::NoUndefinedTemplateParams ? DUContext::NoUndefinedTemplateParams : DUContext::NoSearchFlags )->indexed(); + auto resolved = Cpp::resolveDelayedTypes( delayed.cast(), this, + source, basicFlags & DUContext::NoUndefinedTemplateParams ? DUContext::NoUndefinedTemplateParams : DUContext::NoSearchFlags ); + res.type = IndexedType(resolved); if( basicFlags & KDevelop::DUContext::NoUndefinedTemplateParams) { AbstractType::Ptr targetTypePtr = TypeUtils::unAliasedType(TypeUtils::targetType(res.type.abstractType(), 0)); if (targetTypePtr.cast() || (targetTypePtr.cast() && targetTypePtr.cast()->kind() == DelayedType::Delayed)) { ifDebug( kDebug() << "stopping because the type" << targetTypePtr->toString() << "of" << i.toString() << "is bad"; ) return false; } } ifDebug( if( !res.isValid() ) kDebug(9007) << "Could not resolve template-parameter \"" << currentIdentifier.templateIdentifier(a).toString() << "\" in \"" << identifier.toString() << "resolved:" << res.toString(); ) } find.openQualifiedIdentifier( res ); find.closeQualifiedIdentifier(); } } if( !find.closeIdentifier( num == idCount-1 ) ) return false; } find.closeQualifiedIdentifier(); foreach( const DeclarationPointer& decl, find.lastDeclarations() ) ret.append(decl.data()); return true; } virtual void findLocalDeclarationsInternal( const Identifier& identifier, const CursorInRevision & position, const AbstractType::Ptr& dataType, DUContext::DeclarationList& ret, const TopDUContext* source, typename BaseContext::SearchFlags flags ) const { ifDebug( kDebug(9007) << "findLocalDeclarationsInternal in " << this << "with parent" << this->parentContext() << "(" << this->scopeIdentifier() <<") for \"" << identifier.toString() << "\""; ) ifDebug( if( BaseContext::owner() && BaseContext::owner() ) kDebug(9007) << "in declaration: " << "(" << BaseContext::owner()->toString(); ) /** - When searching local declarations: - Check whether they are already in the instantiated context, if yes return them - If not, Search in non-instantiated context(only for local declarations), then: - Copy & Change returned objects: - Resolve virtual types (DelayedType) - Change parent-context to virtual context - Change internal context, (create virtual, move set parent) * */ int retCount = ret.size(); BaseContext::findLocalDeclarationsInternal(identifier, position, dataType, ret, source, flags ); ifDebug( kDebug(9007) << "basically found:" << ret.count() - retCount << "containing" << BaseContext::localDeclarations().count() << "searching-position" << position.castToSimpleCursor().textCursor(); ) if( !(flags & DUContext::NoFiltering) ) { //Filter out constructors and if needed unresolved template-params for(int a = 0; a < ret.size(); ) { AbstractType::Ptr retAbstractTypePtr = ret[a]->abstractType(); if( ( (flags & KDevelop::DUContext::NoUndefinedTemplateParams) && retAbstractTypePtr.cast() ) || ( (!(flags & BaseContext::OnlyFunctions)) && (dynamic_cast(ret[a]) && static_cast(ret[a])->isConstructor() ) ) ) { //Maybe this filtering should be done in the du-chain? //Erase the item for(int b = a+1; b < ret.size(); ++b) ret[b-1] = ret[b]; ret.resize(ret.size()-1); //kDebug(9007) << "filtered out 1 declaration"; } else { ++a; } } } ifDebug( if( BaseContext::owner() && BaseContext::owner() ) kDebug(9007) << "in declaration: " << "(" << BaseContext::owner()->toString(); ) ifDebug( kDebug(9007) << "instantiated from:" << m_instantiatedFrom; ) if( m_instantiatedFrom && ret.size() == retCount ) { ///Search in the context this one was instantiated from DUContext::DeclarationList decls; ifDebug( kDebug(9007) << "searching base"; ) m_instantiatedFrom->findLocalDeclarationsInternal( identifier, position, dataType, decls, source, flags ); ifDebug( if( BaseContext::owner() && BaseContext::owner() ) kDebug(9007) << "in declaration: " << "(" << BaseContext::owner()->toString(); ) ifDebug( kDebug(9007) << "found" << decls.count() << "in base"; ) InstantiationInformation memberInstantiationInformation; memberInstantiationInformation.previousInstantiationInformation = m_instantiatedWith; FOREACH_ARRAY( Declaration* decl, decls ) { TemplateDeclaration* templateDecl = dynamic_cast(decl); if(!templateDecl) { kDebug() << "problem"; } else { Declaration* copy; DUContext* current = decl->context(); while(current != m_instantiatedFrom && current) { // The declaration has been propagated up from a sub-context like an enumerator, add more empty instantiation information // so the depth is matched correctly by the information InstantiationInformation i; i.previousInstantiationInformation = memberInstantiationInformation.indexed(); memberInstantiationInformation = i; current = current->parentContext(); } Q_ASSERT(source); copy = templateDecl->instantiate(memberInstantiationInformation, source); //This can happen in case of explicit specializations // if(copy->context() != this) // kWarning() << "serious problem: Instatiation is in wrong context, should be in this one"; if(copy) ret.append(copy); } } } } virtual void visit(DUChainVisitor& visitor) { QMutexLocker l(&cppDuContextInstantiationsMutex); foreach(CppDUContext* ctx, m_instatiations) ctx->visit(visitor); BaseContext::visit(visitor); } virtual void deleteUses() { QMutexLocker l(&cppDuContextInstantiationsMutex); foreach(CppDUContext* ctx, m_instatiations) ctx->deleteUses(); BaseContext::deleteUses(); } virtual bool foundEnough( const DUContext::DeclarationList& decls, DUContext::SearchFlags flags ) const { if(flags & DUContext::NoFiltering) return false; if( decls.isEmpty() ) return false; if( dynamic_cast(decls[0]) && BaseContext::type() != DUContext::Class ) //In classes, one function hides all overloads return false; //Collect overloaded function-declarations return true; } /** * Set the context which this is instantiated from. This context will be strictly attached to that context, and will be deleted once the other is deleted. * */ void setInstantiatedFrom( CppDUContext* context, const InstantiationInformation& templateArguments ) { Q_ASSERT(!dynamic_cast(this)); if(context && context->m_instantiatedFrom) { setInstantiatedFrom(context->m_instantiatedFrom, templateArguments); return; } QMutexLocker l(&cppDuContextInstantiationsMutex); if( m_instantiatedFrom ) { Q_ASSERT(m_instantiatedFrom->m_instatiations[m_instantiatedWith] == this); m_instantiatedFrom->m_instatiations.remove( m_instantiatedWith ); } m_instantiatedWith = templateArguments.indexed(); if(context) { //Change the identifier so it contains the template-parameters QualifiedIdentifier totalId = this->localScopeIdentifier(); KDevelop::Identifier id; if( !totalId.isEmpty() ) { id = totalId.last(); totalId.pop(); } id.clearTemplateIdentifiers(); FOREACH_FUNCTION(const IndexedType& arg, templateArguments.templateParameters) { AbstractType::Ptr type(arg.abstractType()); IdentifiedType* identified = dynamic_cast(type.unsafeData()); if(identified) id.appendTemplateIdentifier( IndexedTypeIdentifier(identified->qualifiedIdentifier()) ); else if(type) id.appendTemplateIdentifier( IndexedTypeIdentifier(type->toString(), true) ); else id.appendTemplateIdentifier( IndexedTypeIdentifier("no type") ); } totalId.push(id); this->setLocalScopeIdentifier(totalId); } m_instantiatedFrom = context; Q_ASSERT(m_instantiatedFrom != this); if(m_instantiatedFrom) { if(!m_instantiatedFrom->m_instatiations.contains(m_instantiatedWith)) { m_instantiatedFrom->m_instatiations.insert( m_instantiatedWith, this ); }else{ kDebug(9007) << "created orphaned instantiation for" << context->m_instatiations[m_instantiatedWith]->scopeIdentifier(true).toString(); m_instantiatedFrom = 0; } } } virtual void applyUpwardsAliases(DUContext::SearchItem::PtrList& identifiers, const TopDUContext* source) const { BaseContext::applyUpwardsAliases(identifiers, source); ///@see Iso C++ 3.4.1 : Unqualified name lookup: ///We need to make sure that when leaving a function definition, the namespace components are searched DUContext::ContextType type = BaseContext::type(); if(type == DUContext::Function || type == DUContext::Other || type == DUContext::Helper) { QualifiedIdentifier prefix = BaseContext::localScopeIdentifier(); if(prefix.count() > 1) { prefix = Cpp::namespaceScopeComponentFromContext(prefix, this, source); if(!prefix.isEmpty()) { prefix.setExplicitlyGlobal(true); DUContext::SearchItem::Ptr newItem(new DUContext::SearchItem(prefix)); newItem->addToEachNode(identifiers); if(!newItem->next.isEmpty()) //Can happen if the identifier was explicitly global identifiers.insert(0, newItem); } } } } /** * If this returns nonzero value, this context is a instatiation of some other context, and that other context will be returned here. * */ CppDUContext* instantiatedFrom() const { return m_instantiatedFrom; } virtual bool inDUChain() const { ///There must be no changes from the moment m_instantiatedFrom is set, because then it can be found as an instantiation by other threads return m_instantiatedFrom || BaseContext::inDUChain(); } IndexedInstantiationInformation instantiatedWith() const { return m_instantiatedWith; } virtual bool shouldSearchInParent(typename BaseContext::SearchFlags flags) const { //If the parent context is a class context, we should even search it from an import return (BaseContext::parentContext() && BaseContext::parentContext()->type() == DUContext::Class) || BaseContext::shouldSearchInParent(flags); } virtual DUContext* specialize(const IndexedInstantiationInformation& specialization, const TopDUContext* topContext, int upDistance) { if(specialization.index() == 0) return this; else { InstantiationInformation information = specialization.information(); //Add empty elements until the specified depth for(int a = 0; a < upDistance; ++a) { InstantiationInformation nextInformation; nextInformation.previousInstantiationInformation = information.indexed(); information = nextInformation; } return instantiate(information, topContext); } } ///@see TemplateDeclaration::instantiate DUContext* instantiate(const InstantiationInformation& info, const TopDUContext* source, const int depth=0) { const int maxDepth = 50; if(!info.isValid() || m_instantiatedWith == info.indexed() || !this->parentContext() ||depth > maxDepth) return this; if(m_instantiatedFrom) return m_instantiatedFrom->instantiate(info, source); { typename QHash* >::const_iterator it = m_instatiations.constFind(info.indexed()); if(it != m_instatiations.constEnd()) return *it; } if(this->owner()) { TemplateDeclaration* templ = dynamic_cast(this->owner()); if(templ) { Declaration* instantiatedDecl = templ->instantiate(info, source); if(!instantiatedDecl) return 0; return instantiatedDecl->internalContext(); } } DUContext* surroundingContext = this->parentContext(); Q_ASSERT(surroundingContext); { //This context does not have an attached declaration, but it needs to be instantiated. CppDUContext* parent = dynamic_cast* >(this->parentContext()); if(parent) surroundingContext = parent->instantiate(info.previousInstantiationInformation.information(), source, depth+1); } if(!surroundingContext) return 0; return instantiateDeclarationAndContext( surroundingContext, source, this, info, 0, 0 ); } virtual QWidget* createNavigationWidget(Declaration* decl, TopDUContext* topContext, const QString& htmlPrefix, const QString& htmlSuffix) const; enum { Identity = BaseContext::Identity + 50 }; ///Duchain must be write-locked void deleteAllInstantiations() { //Specializations will be destroyed the same time this is destroyed CppDUContext* oldFirst = 0; QMutexLocker l(&cppDuContextInstantiationsMutex); while(!m_instatiations.isEmpty()) { CppDUContext* first = 0; first = *m_instatiations.begin(); Q_ASSERT(first != oldFirst); Q_UNUSED(oldFirst); oldFirst = first; l.unlock(); ///TODO: anonymous contexts should get deleted but that is crashy /// see also declarationbuilder which also encountered this /// issue before and also removed the context deletion... Q_ASSERT(first->m_instantiatedFrom == this); first->setInstantiatedFrom(0, InstantiationInformation()); Q_ASSERT(first->m_instantiatedFrom == 0); l.relock(); } } //Overridden to instantiate all not yet instantiated local declarations virtual QVector localDeclarations(const TopDUContext* source) const { if(m_instantiatedFrom && source && BaseContext::type() != DUContext::Template) { QVector decls = m_instantiatedFrom->localDeclarations(source); // DUContext::DeclarationList temp; InstantiationInformation inf; inf.previousInstantiationInformation = m_instantiatedWith; foreach( Declaration* baseDecl, decls ) { TemplateDeclaration* tempDecl = dynamic_cast(baseDecl); if(tempDecl) { tempDecl->instantiate(inf, source); }else{ kDebug() << "Strange: non-template within template context"; KDevVarLengthArray temp; this->findLocalDeclarationsInternal( baseDecl->identifier(), CursorInRevision::invalid(), AbstractType::Ptr(), temp, source, DUContext::NoFiltering ); } } } return BaseContext::localDeclarations(source); } private: ~CppDUContext() { if(m_instantiatedFrom) setInstantiatedFrom(0, InstantiationInformation()); deleteAllInstantiations(); } virtual void mergeDeclarationsInternal(QList< QPair >& definitions, const CursorInRevision& position, QHash& hadContexts, const TopDUContext* source, bool searchInParents, int currentDepth) const { Q_ASSERT(source); // kDebug() << "checking in" << this->scopeIdentifier(true).toString(); if( m_instantiatedFrom ) { //We need to make sure that all declarations from the specialization-base are instantiated, so they are returned. //This requests all declarations, so they all will be instantiated and instances of them added into this context. //DUContext::mergeDeclarationsInternal will then get them. //Calling localDeclarations() instantiates all not yet instantiated member declarations localDeclarations(source); // kDebug() << "final count of local declarations:" << this->localDeclarations().count(); //Instantiate up-propagating child-contexts with the correct same instantiation-information //This for examples makes unnamed enums accessible InstantiationInformation inf; inf.previousInstantiationInformation = m_instantiatedWith; foreach(DUContext* child, m_instantiatedFrom->childContexts()) { // kDebug() << "checking child-context" << child->isPropagateDeclarations(); if(child->isPropagateDeclarations()) static_cast*>(static_cast*>(child)->instantiate(inf, source))->mergeDeclarationsInternal(definitions, position, hadContexts, source, searchInParents, currentDepth); } } BaseContext::mergeDeclarationsInternal(definitions, position, hadContexts, source, searchInParents, currentDepth); } CppDUContext* m_instantiatedFrom; ///Every access to m_instatiations must be serialized through instatiationsMutex, because they may be written without a write-lock QHash* > m_instatiations; IndexedInstantiationInformation m_instantiatedWith; }; ///Returns whether the given declaration depends on a missing template-parameter bool isTemplateDependent(const Declaration* decl); bool isTemplateDependent(const DUContext* context); } #endif diff --git a/languages/cpp/cppduchain/declarationbuilder.cpp b/languages/cpp/cppduchain/declarationbuilder.cpp index 538f78f513..4ad7d3b9c2 100644 --- a/languages/cpp/cppduchain/declarationbuilder.cpp +++ b/languages/cpp/cppduchain/declarationbuilder.cpp @@ -1,1758 +1,1758 @@ /* This file is part of KDevelop Copyright 2006-2007 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "declarationbuilder.h" #include "debugbuilders.h" #include #include #include #include "templatedeclaration.h" #include "parser/type_compiler.h" #include "parser/commentformatter.h" #include "parser/parser.h" #include "parser/control.h" #include #include #include #include #include #include #include #include #include "qtfunctiondeclaration.h" #include "cppeditorintegrator.h" #include "environmentmanager.h" #include #include #include #include "templateparameterdeclaration.h" #include "tokens.h" #include "parsesession.h" #include "cpptypes.h" #include "cppduchain.h" #include "cpptypes.h" #include #include "cppdebughelper.h" #include "name_visitor.h" #include "usebuilder.h" #include "overloadresolutionhelper.h" #include "expressionparser.h" using namespace KTextEditor; using namespace KDevelop; using namespace Cpp; ClassDeclarationData::ClassType classTypeFromTokenKind(int kind) { switch(kind) { case Token_struct: return ClassDeclarationData::Struct; case Token_union: return ClassDeclarationData::Union; default: return ClassDeclarationData::Class; } } bool DeclarationBuilder::changeWasSignificant() const { ///@todo Also set m_changeWasSignificant if publically visible declarations were removed(needs interaction with abstractcontextbuilder) return m_changeWasSignificant; } DeclarationBuilder::DeclarationBuilder (ParseSession* session) : DeclarationBuilderBase(session), m_changeWasSignificant(false), m_ignoreDeclarators(false), m_functionFlag(NoFunctionFlag), m_collectQtFunctionSignature(false), m_lastDeclaration(0) { } ReferencedTopDUContext DeclarationBuilder::buildDeclarations(Cpp::EnvironmentFilePointer file, AST *node, IncludeFileList* includes, const ReferencedTopDUContext& updateContext, bool removeOldImports) { ReferencedTopDUContext top = buildContexts(file, node, includes, updateContext, removeOldImports); Q_ASSERT(m_accessPolicyStack.isEmpty()); Q_ASSERT(m_functionDefinedStack.isEmpty()); return top; } void DeclarationBuilder::visitTemplateParameter(TemplateParameterAST * ast) { //Backup and zero the parameter declaration, because we will handle it here directly, and don't want a normal one to be created m_ignoreDeclarators = true; DeclarationBuilderBase::visitTemplateParameter(ast); m_ignoreDeclarators = false; if( ast->type_parameter || ast->parameter_declaration ) { ///@todo deal with all the other stuff the AST may contain TemplateParameterDeclaration* decl; if(ast->type_parameter) decl = openDeclaration(ast->type_parameter->name, ast, Identifier(), false, !ast->type_parameter->name); else decl = openDeclaration(ast->parameter_declaration->declarator ? ast->parameter_declaration->declarator->id : 0, ast, Identifier(), false, !ast->parameter_declaration->declarator); DUChainWriteLocker lock(DUChain::lock()); AbstractType::Ptr type = lastType(); if( type.cast() ) { type.cast()->setDeclaration(decl); } else { kDebug(9007) << "bad last type"; } decl->setAbstractType(type); if( ast->type_parameter && ast->type_parameter->type_id ) { //Extract default type-parameter QualifiedIdentifier defaultParam; QString str; ///Only record the strings, because these expressions may depend on template-parameters and thus must be evaluated later str += stringFromSessionTokens( editor()->parseSession(), ast->type_parameter->type_id->start_token, ast->type_parameter->type_id->end_token ); defaultParam = QualifiedIdentifier(str); decl->setDefaultParameter(defaultParam); } if( ast->parameter_declaration ) { if( ast->parameter_declaration->expression ) decl->setDefaultParameter( QualifiedIdentifier( stringFromSessionTokens( editor()->parseSession(), ast->parameter_declaration->expression->start_token, ast->parameter_declaration->expression->end_token ) ) ); } closeDeclaration(ast->parameter_declaration); } } void DeclarationBuilder::parseComments(const ListNode *comments) { setComment(editor()->parseSession()->m_commentFormatter.formatComment(comments, editor()->parseSession())); } void DeclarationBuilder::visitFunctionDeclaration(FunctionDefinitionAST* node) { FunctionFlag flag = NoFunctionFlag; switch(node->defaultDeleted) { case FunctionDefinitionAST::NotDefaultOrDeleted: flag = NoFunctionFlag; break; case FunctionDefinitionAST::Default: flag = DefaultFunction; break; case FunctionDefinitionAST::Deleted: flag = DeleteFunction; break; } PushValue setDefaultDeleted(m_functionFlag, flag); parseComments(node->comments); parseStorageSpecifiers(node->storage_specifiers); parseFunctionSpecifiers(node->function_specifiers); //Used to map to the top level function node once the Declaration is built if(m_mapAst) m_mappedNodes.push(node); m_functionDefinedStack.push(node->start_token); DeclarationBuilderBase::visitFunctionDeclaration(node); m_functionDefinedStack.pop(); if(m_mapAst) m_mappedNodes.pop(); popSpecifiers(); } //Visitor that clears the ducontext from all AST nodes struct ClearDUContextVisitor : public DefaultVisitor { virtual void visit(AST* node) { if(node) node->ducontext = 0; DefaultVisitor::visit(node); } }; void DeclarationBuilder::visitInitDeclarator(InitDeclaratorAST *node) { ///FIXME: properly add support for member-declaration/member-declarator PushValue setHasInitialize(m_functionFlag, (node->initializer && node->initializer->initializer_clause && node->initializer->initializer_clause->expression) ? AbstractFunction : NoFunctionFlag); bool parameter_is_initializer = false; if(currentContext()->type() == DUContext::Other) { //Cannot declare a a function within a code-context parameter_is_initializer = true; }else if(!m_inFunctionDefinition && node->declarator && node->declarator->parameter_declaration_clause && node->declarator->id) { //Decide whether the parameter-declaration clause is valid DUChainWriteLocker lock(DUChain::lock()); CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge); QualifiedIdentifier id; identifierForNode(node->declarator->id, id); DUContext* previous = currentContext(); DUContext* previousLast = lastContext(); QVector importedParentContexts = m_importedParentContexts; openPrefixContext(node, id, pos); //We create a temporary prefix-context to search from within the right scope DUContext* tempContext = currentContext(); if (currentContext()->type() != DUContext::Class) parameter_is_initializer = !checkParameterDeclarationClause(node->declarator->parameter_declaration_clause); closePrefixContext(id); if(tempContext != previous) { //We remove all of its traces from the AST using ClearDUContextVisitor. ClearDUContextVisitor clear; clear.visit(node); ///@todo We don't delete the tempContext, as that may cause crashes. Problem: This leaves garbage in the duchain ///@todo Solve the redundancy issue once and for all, properly, using a SimpleDeclarationOrFunctionDeclarationAST or similar. //Since we don't delete the temporary context, at least collapse its range. tempContext->setRange(RangeInRevision(tempContext->range().start, tempContext->range().end)); setLastContext(previousLast); m_importedParentContexts = importedParentContexts; } Q_ASSERT(currentContext() == previous); } if (parameter_is_initializer && node->declarator->parameter_declaration_clause && !node->initializer) { Control control; Parser parser(&control); parser.fixupInitializerFromParameter(node, m_editor.parseSession()); } DeclarationBuilderBase::visitInitDeclarator(node); } void DeclarationBuilder::handleRangeBasedFor(ExpressionAST* container, ForRangeDeclarationAst* iterator) { ContextBuilder::handleRangeBasedFor(container, iterator); if (!container || !iterator) { return; } if (lastTypeWasAuto() && m_lastDeclaration) { // auto support for range-based for AbstractType::Ptr listType; { DUChainReadLocker lock; container->ducontext = currentContext(); Cpp::ExpressionParser parser; Cpp::ExpressionEvaluationResult res = parser.evaluateType( container, editor()->parseSession() ); listType = res.type.abstractType(); } if (!listType) { // invalid type DUChainWriteLocker lock; m_lastDeclaration->setAbstractType(AbstractType::Ptr(0)); return; } AbstractType::Ptr realListType = TypeUtils::realType(listType); // step 1: find type of elements in list AbstractType::Ptr elementType; if (ArrayType::Ptr array = realListType.cast()) { // case a: c-array, i.e. foo bar[5]; -> type is foo elementType = array->elementType(); } else { // case b: look for begin(listType) function using ADL DUChainReadLocker lock; OverloadResolutionHelper helper = OverloadResolutionHelper( DUContextPointer(currentContext()), TopDUContextPointer(topContext()) ); helper.setKnownParameters(OverloadResolver::ParameterList(listType, false)); // first try begin in current context static const QualifiedIdentifier begin("begin"); helper.setFunctionNameForADL(begin); helper.setFunctions( currentContext()->findDeclarations(begin, CursorInRevision::invalid(), AbstractType::Ptr(), 0, DUContext::OnlyFunctions) ); ViableFunction func = helper.resolve(); if (!func.isValid()) { // not valid, fall-back to std, it's an associated namespace, // see also: spec, 6.5.4 static const QualifiedIdentifier stdBegin("::std::begin"); helper.setFunctionNameForADL(stdBegin); helper.setFunctions( currentContext()->findDeclarations(stdBegin, CursorInRevision::invalid(), AbstractType::Ptr(), 0, DUContext::OnlyFunctions) ); func = helper.resolve(); } if (func.isValid()) { AbstractType::Ptr type = func.declaration()->type()->returnType(); // see spec: for-range-declaration = *__begin; elementType = TypeUtils::decreasePointerDepth(type, topContext(), true); } } // step 2: set last type, but keep const& DUChainWriteLocker lock; if (elementType) { AbstractType::Ptr type = m_lastDeclaration->abstractType(); elementType->setModifiers(type->modifiers()); if (ReferenceType::Ptr ref = type.cast()) { ref->setBaseType(elementType); } else { type = elementType; } m_lastDeclaration->setAbstractType(type); } else { // invalid type m_lastDeclaration->setAbstractType(AbstractType::Ptr(0)); } } } void DeclarationBuilder::visitSimpleDeclaration(SimpleDeclarationAST* node) { parseComments(node->comments); parseStorageSpecifiers(node->storage_specifiers); parseFunctionSpecifiers(node->function_specifiers); if(m_mapAst) m_mappedNodes.push(node); m_functionDefinedStack.push(0); DeclarationBuilderBase::visitSimpleDeclaration(node); m_functionDefinedStack.pop(); if(m_mapAst) m_mappedNodes.pop(); popSpecifiers(); } void DeclarationBuilder::findDeclarationForDefinition(const QualifiedIdentifier &definitionSearchId) { //TODO: FunctionDeclarations (as distinct from ClassFunctionDeclarations) should probably do what template forward declarations do. //That is, the function definition should have no idea they exist and any default arguments should just be copied over FunctionDefinition *funDef = dynamic_cast(currentDeclaration()); if (!funDef || (currentContext()->type() != DUContext::Namespace && currentContext()->type() != DUContext::Global)) return; QList declarations = currentContext()->findDeclarations(definitionSearchId, currentDeclaration()->range().start, AbstractType::Ptr(), 0, DUContext::OnlyFunctions); if (!declarations.size()) return; //First look for an exact match for the function declaration foreach (Declaration* dec, declarations) { if (dec->isForwardDeclaration() || dec->isDefinition()) continue; - if (dec->abstractType()->indexed() == lastType()->indexed()) { + if (IndexedType(dec->abstractType()) == IndexedType(lastType())) { //If this declaration is already assigned to a partial match, unassign it if (FunctionDefinition* oldDef = FunctionDefinition::definition(dec)) { - if (oldDef->abstractType()->indexed() != dec->abstractType()->indexed()) + if (IndexedType(oldDef->abstractType()) != IndexedType(dec->abstractType())) oldDef->setDeclaration(0); } funDef->setDeclaration(dec); return; } } //Allow claiming of unclaimed declarations with the same arg count. This allows the signature assistant to function. int functionArgumentCount = 0; if(FunctionType::Ptr funDefType = funDef->abstractType().cast()) functionArgumentCount = funDefType->arguments().count(); Declaration *anyUnclaimedFunctionDeclaration = 0; foreach (Declaration* dec, declarations) { if (!dec->isFunctionDeclaration() || dec->isDefinition()) continue; if(FunctionDefinition::definition(dec) && wasEncountered(FunctionDefinition::definition(dec))) continue; if (FunctionType::Ptr foundType = dec->abstractType().cast()) { if (foundType->arguments().count() == functionArgumentCount) { funDef->setDeclaration(dec); return; } } anyUnclaimedFunctionDeclaration = dec; } //Allow any unclaimed function-definition with a matching name. This allows the signature assistant to function. if (anyUnclaimedFunctionDeclaration) funDef->setDeclaration(anyUnclaimedFunctionDeclaration); } void DeclarationBuilder::visitDeclarator (DeclaratorAST* node) { if(m_ignoreDeclarators) { DeclarationBuilderBase::visitDeclarator(node); return; } m_collectQtFunctionSignature = !m_accessPolicyStack.isEmpty() && ((m_accessPolicyStack.top() & FunctionIsSlot) || (m_accessPolicyStack.top() & FunctionIsSignal)); m_qtFunctionSignature = QByteArray(); // pretty ugly but seems to work for now... bool isFuncPtr = node->parameter_declaration_clause && !node->id && node->sub_declarator && node->sub_declarator->ptr_ops; if (node->parameter_declaration_clause && !isFuncPtr) { if(m_collectQtFunctionSignature) //We need to do this just to collect the signature checkParameterDeclarationClause(node->parameter_declaration_clause); Declaration* decl = openFunctionDeclaration(node->id, node); ///Create mappings iff the AST feature is specified if(m_mapAst && !m_mappedNodes.empty()) editor()->parseSession()->mapAstDuChain(m_mappedNodes.top(), KDevelop::DeclarationPointer(decl)); if (m_functionFlag == DeleteFunction) { DUChainWriteLocker lock(DUChain::lock()); decl->setExplicitlyDeleted(true); } if( !m_functionDefinedStack.isEmpty() ) { DUChainWriteLocker lock(DUChain::lock()); // don't overwrite isDefinition if that was already set (see openFunctionDeclaration) decl->setDeclarationIsDefinition( (bool)m_functionDefinedStack.top() ); } applyFunctionSpecifiers(); } else if (isFuncPtr) { openDeclaration(node->sub_declarator->id, node); } else { openDefinition(node->id, node, node->id == 0); } m_collectQtFunctionSignature = false; applyStorageSpecifiers(); // don't visit nested declarators for function pointers DeclaratorAST* sub = node->sub_declarator; if (isFuncPtr) { node->sub_declarator = 0; } DeclarationBuilderBase::visitDeclarator(node); if (isFuncPtr) { node->sub_declarator = sub; } if (node->parameter_declaration_clause && !isFuncPtr) { if (!m_functionDefinedStack.isEmpty() && m_functionDefinedStack.top() && node->id) { DUChainWriteLocker lock(DUChain::lock()); //We have to search for the fully qualified identifier, so we always get the correct class QualifiedIdentifier id = currentContext()->scopeIdentifier(false); QualifiedIdentifier id2; identifierForNode(node->id, id2); id += id2; id.setExplicitlyGlobal(true); findDeclarationForDefinition(id); } } closeDeclaration(); } ForwardDeclaration * DeclarationBuilder::openForwardDeclaration(NameAST * name, AST * range) { return openDeclaration(name, range); } template Type hasTemplateContext( const QList& contexts ) { foreach( const Type& context, contexts ) if( context && context->type() == KDevelop::DUContext::Template ) return context; return Type(0); } DUContext::Import hasTemplateContext( const QVector& contexts, TopDUContext* top ) { foreach( const DUContext::Import& context, contexts ) if( context.context(top) && context.context(top)->type() == KDevelop::DUContext::Template ) return context; return DUContext::Import(); } //Check whether the given context is a template-context by checking whether it imports a template-parameter context KDevelop::DUContext* isTemplateContext( KDevelop::DUContext* context ) { return hasTemplateContext( context->importedParentContexts(), context->topContext() ).context(context->topContext()); } bool isSpecialization(TemplateDeclaration *templDecl) { //A class specialization or partial specialization will have template identifiers in its identifier if (ClassDeclaration* classDecl = dynamic_cast(templDecl)) { if (classDecl->identifier().templateIdentifiersCount()) return true; } //A function specialization may or may not have template identifiers, but at least has "template<>" if (dynamic_cast(templDecl)) { DUContext *specFromCtxt = templDecl->templateParameterContext(); if (specFromCtxt && !specFromCtxt->localDeclarations().size()) return true; } return false; } DUContext* functionClassContext(Declaration* functionDecl, DUContext *functionCtxt) { //FIXME: needed as long as functions have their QID merged into their id QualifiedIdentifier currentScope = functionCtxt->scopeIdentifier(true); QualifiedIdentifier className = currentScope + QualifiedIdentifier(functionDecl->identifier().toString()); className.pop(); //Pop off the function name at the end, leaving the class QID className.setExplicitlyGlobal(true); QList classDeclarations = functionCtxt->findDeclarations(className); if (classDeclarations.size()) return classDeclarations.first()->internalContext(); return 0; } TemplateDeclaration* DeclarationBuilder::findSpecializedFrom(Declaration* specializedDeclaration) { Identifier searchForIdentifier; if (dynamic_cast(specializedDeclaration)) searchForIdentifier = QualifiedIdentifier(specializedDeclaration->identifier().toString()).last(); else searchForIdentifier = specializedDeclaration->identifier(); searchForIdentifier.clearTemplateIdentifiers(); DUContext* searchInContext = 0; if (dynamic_cast(specializedDeclaration)) searchInContext = functionClassContext(specializedDeclaration, currentContext()); if (!searchInContext) searchInContext = currentContext(); QList specFromDecls = searchInContext->findDeclarations(searchForIdentifier); foreach(Declaration * possibleSpec, specFromDecls) { TemplateDeclaration *asTemplateDecl = dynamic_cast(possibleSpec); if (!isSpecialization(asTemplateDecl)) return asTemplateDecl; } return 0; } template T* DeclarationBuilder::openDeclaration(NameAST* name, AST* rangeNode, const Identifier& customName, bool collapseRangeAtStart, bool collapseRangeAtEnd) { DUChainWriteLocker lock(DUChain::lock()); KDevelop::DUContext* templateCtx = hasTemplateContext(m_importedParentContexts + currentContext()->importedParentContexts(), topContext()).context(topContext()); ///We always need to create a template declaration when we're within a template, so the declaration can be accessed ///by specialize(..) and its indirect DeclarationId if( templateCtx || m_templateDeclarationDepth ) { Cpp::SpecialTemplateDeclaration* ret = openDeclarationReal >( name, rangeNode, customName, collapseRangeAtStart, collapseRangeAtEnd ); ret->setTemplateParameterContext(templateCtx); //FIXME: A FunctionDeclaration w/o a definition should actually be a kind of forward declaration (ie, there can be more than one) if( templateCtx && !m_onlyComputeSimplified && isSpecialization(ret) && ( dynamic_cast(ret) || !dynamic_cast(ret) ) ) { if( TemplateDeclaration *specializedFrom = findSpecializedFrom(ret) ) { TemplateDeclaration *templateDecl = dynamic_cast(ret); IndexedInstantiationInformation specializedWith = createSpecializationInformation(name, templateCtx); templateDecl->setSpecializedFrom(specializedFrom); templateDecl->setSpecializedWith(specializedWith); } //TODO: else problem } return ret; } else{ return openDeclarationReal( name, rangeNode, customName, collapseRangeAtStart, collapseRangeAtEnd ); } } template T* DeclarationBuilder::openDeclarationReal(NameAST* name, AST* rangeNode, const Identifier& customName, bool collapseRangeAtStart, bool collapseRangeAtEnd, const RangeInRevision* customRange) { RangeInRevision newRange; if(name) { uint start = name->unqualified_name->start_token; uint end = name->unqualified_name->end_token; //We must exclude the tilde. Else we may get totally messed up ranges when the name of a destructor is renamed in a macro if(name->unqualified_name->tilde) { Q_ASSERT(name->unqualified_name->id); start = name->unqualified_name->id; } newRange = editor()->findRange(start, end); }else if(rangeNode) { newRange = editor()->findRange(rangeNode); }else if(customRange) { newRange = *customRange; } if(collapseRangeAtStart) newRange.end = newRange.start; else if(collapseRangeAtEnd) newRange.start = newRange.end; Identifier localId = customName; if (name) { //If this is an operator thing, build the type first. Since it's part of the name, the type-builder doesn't catch it normally if(name->unqualified_name && name->unqualified_name->operator_id) visit(name->unqualified_name->operator_id); QualifiedIdentifier id; identifierForNode(name, id); if(localId.isEmpty()) localId = id.last(); } T* declaration = 0; if (recompiling()) { // Seek a matching declaration ///@todo maybe order the declarations within ducontext and change here back to walking the indices, because that's easier to debug and faster QList decls = currentContext()->findLocalDeclarations(localId, CursorInRevision::invalid(), 0, AbstractType::Ptr(), DUContext::NoFiltering); foreach( Declaration* dec, decls ) { if( wasEncountered(dec) ) continue; if (dec->range() == newRange && (localId == dec->identifier() || (localId.isUnique() && dec->identifier().isUnique())) && typeid(T) == typeid(*dec) ) { // Match TemplateDeclaration* templateDecl = dynamic_cast(dec); if(templateDecl) templateDecl->deleteAllInstantiations(); //Delete all instantiations so we have a fresh start declaration = dynamic_cast(dec); break; } } if(!declaration) { ///Second run of the above, this time ignoring the ranges. foreach( Declaration* dec, decls ) { if( wasEncountered(dec) ) continue; if ((localId == dec->identifier() || (localId.isUnique() && dec->identifier().isUnique())) && typeid(*dec) == typeid(T) ) { // Match declaration = dynamic_cast(dec); declaration->setRange(newRange); break; } } } } #ifdef DEBUG_UPDATE_MATCHING if(declaration) kDebug() << "found match for" << localId.toString(); else kDebug() << "nothing found for" << localId.toString(); #endif if (!declaration) { if(currentContext()->inSymbolTable()) m_changeWasSignificant = true; //We are adding a declaration that comes into the symbol table, so mark the change significant declaration = new T(newRange, currentContext()); declaration->setIdentifier(localId); } //Clear some settings AbstractFunctionDeclaration* funDecl = dynamic_cast(declaration); if(funDecl) funDecl->clearDefaultParameters(); declaration->setDeclarationIsDefinition(false); //May be set later declaration->setIsTypeAlias(m_inTypedef); declaration->setComment(comment()); clearComment(); setEncountered(declaration); m_declarationStack.push(declaration); return declaration; } ClassDeclaration* DeclarationBuilder::openClassDefinition(NameAST* name, AST* range, bool collapseRange, ClassDeclarationData::ClassType classType) { Identifier id; if(!name) { //Unnamed class/struct, use a unique id static QAtomicInt& uniqueClassNumber( KDevelop::globalItemRepositoryRegistry().getCustomCounter("Unnamed Class Ids", 1) ); id = Identifier::unique( uniqueClassNumber.fetchAndAddRelaxed(1) ); } ClassDeclaration* ret = openDeclaration(name, range, id, collapseRange); DUChainWriteLocker lock(DUChain::lock()); ret->setDeclarationIsDefinition(true); ret->clearBaseClasses(); if(m_accessPolicyStack.isEmpty()) ret->setAccessPolicy(KDevelop::Declaration::Public); else ret->setAccessPolicy(currentAccessPolicy()); ret->setClassType(classType); return ret; } Declaration* DeclarationBuilder::openDefinition(NameAST* name, AST* rangeNode, bool collapseRange) { Declaration* ret = openNormalDeclaration(name, rangeNode, KDevelop::Identifier(), collapseRange); ///Create mappings iff the AST feature is specified if(m_mapAst && !m_mappedNodes.empty()) editor()->parseSession()->mapAstDuChain(m_mappedNodes.top(), KDevelop::DeclarationPointer(ret)); DUChainWriteLocker lock(DUChain::lock()); ret->setDeclarationIsDefinition(true); return ret; } Declaration* DeclarationBuilder::openNormalDeclaration(NameAST* name, AST* rangeNode, const Identifier& customName, bool collapseRange) { if(currentContext()->type() == DUContext::Class) { ClassMemberDeclaration* mem = openDeclaration(name, rangeNode, customName, collapseRange); DUChainWriteLocker lock(DUChain::lock()); mem->setAccessPolicy(currentAccessPolicy()); return mem; } else if(currentContext()->type() == DUContext::Template) { return openDeclaration(name, rangeNode, customName, collapseRange); } else { return openDeclaration(name, rangeNode, customName, collapseRange); } } Declaration* DeclarationBuilder::openFunctionDeclaration(NameAST* name, AST* rangeNode) { QualifiedIdentifier id; identifierForNode(name, id); Identifier localId = id.last(); //This also copies the template arguments if(id.count() > 1) { //Merge the scope of the declaration, else the declarations could be confused with global functions. //This is done before the actual search, so there are no name-clashes while searching the class for a constructor. //FIXME: Can we do without this? localId.setIdentifier(id.left(-1).toString() + "::" + localId.identifier().str()); } if(currentContext()->type() == DUContext::Class) { DUChainWriteLocker lock; ClassFunctionDeclaration* fun = 0; if(!m_collectQtFunctionSignature) { fun = openDeclaration(name, rangeNode, localId); }else{ QtFunctionDeclaration* qtFun = openDeclaration(name, rangeNode, localId); fun = qtFun; qtFun->setIsSlot(m_accessPolicyStack.top() & FunctionIsSlot); qtFun->setIsSignal(m_accessPolicyStack.top() & FunctionIsSignal); QByteArray temp(QMetaObject::normalizedSignature("(" + m_qtFunctionSignature + ")")); IndexedString signature(temp.mid(1, temp.length()-2)); // kDebug() << "normalized signature:" << signature.str() << "from:" << QString::fromUtf8(m_qtFunctionSignature); qtFun->setNormalizedSignature(signature); } Q_ASSERT(fun); fun->setAccessPolicy(currentAccessPolicy()); fun->setIsAbstract(m_functionFlag == AbstractFunction); return fun; } else if(m_inFunctionDefinition && (currentContext()->type() == DUContext::Namespace || currentContext()->type() == DUContext::Global)) { //May be a definition FunctionDefinition* ret = openDeclaration(name, rangeNode, localId); DUChainWriteLocker lock(DUChain::lock()); ret->setDeclaration(0); return ret; }else{ return openDeclaration(name, rangeNode, localId); } } void DeclarationBuilder::classTypeOpened(AbstractType::Ptr type) { //We override this so we can get the class-declaration into a usable state(with filled type) earlier DUChainWriteLocker lock(DUChain::lock()); IdentifiedType* idType = dynamic_cast(type.unsafeData()); if( idType && !idType->declarationId().isValid() ) //When the given type has no declaration yet, assume we are declaring it now idType->setDeclaration( currentDeclaration() ); currentDeclaration()->setType(type); } void DeclarationBuilder::closeDeclaration(bool forceInstance) { bool assignInternalContext = false; { DUChainWriteLocker lock(DUChain::lock()); if (lastType()) { AbstractType::Ptr type = typeForCurrentDeclaration(); IdentifiedType* idType = dynamic_cast(type.unsafeData()); DelayedType::Ptr delayed = type.cast(); //When the given type has no declaration yet, assume we are declaring it now. //If the type is a delayed type, it is a searched type, and not a declared one, so don't set the declaration then. if( !forceInstance && idType && !idType->declarationId().isValid() && !delayed ) { idType->setDeclaration( currentDeclaration() ); //Q_ASSERT(idType->declaration(topContext()) == currentDeclaration()); } if(currentDeclaration()->kind() != Declaration::NamespaceAlias && currentDeclaration()->kind() != Declaration::Alias) { //If the type is not identified, it is an instance-declaration too, because those types have no type-declarations. if( (((!idType) || (idType && idType->declarationId() != currentDeclaration()->id())) && !currentDeclaration()->isTypeAlias() && !currentDeclaration()->isForwardDeclaration() ) || dynamic_cast(currentDeclaration()) || forceInstance ) currentDeclaration()->setKind(Declaration::Instance); else currentDeclaration()->setKind(Declaration::Type); } currentDeclaration()->setType(type); }else{ currentDeclaration()->setAbstractType(AbstractType::Ptr()); if(dynamic_cast(currentDeclaration())) currentDeclaration()->setKind(Declaration::Type); } if(TemplateDeclaration* templateDecl = dynamic_cast(currentDeclaration())) { //The context etc. may have been filled with new items, and the declaration may have been searched unsuccessfully, or wrong instantiations created. TemplateDeclaration* deleteInstantiationsOf = 0; if(templateDecl->instantiatedFrom()) deleteInstantiationsOf = templateDecl->instantiatedFrom(); else if(templateDecl->specializedFrom().data()) deleteInstantiationsOf = dynamic_cast(templateDecl->specializedFrom().data()); else deleteInstantiationsOf = templateDecl; if(deleteInstantiationsOf) { CppDUContext* ctx = dynamic_cast*>(dynamic_cast(deleteInstantiationsOf)->internalContext()); deleteInstantiationsOf->deleteAllInstantiations(); if(ctx) ctx->deleteAllInstantiations(); } } if (lastContext() && !m_onlyComputeSimplified && currentDeclaration()->isFunctionDeclaration()) { currentDeclaration()->setInternalFunctionContext(lastContext()); } assignInternalContext = lastContext() && (lastContext()->type() != DUContext::Other || currentDeclaration()->isFunctionDeclaration()); } if (assignInternalContext) { eventuallyAssignInternalContext(); } ifDebugCurrentFile( DUChainReadLocker lock(DUChain::lock()); kDebug() << "closing declaration" << currentDeclaration()->toString() << "type" << (currentDeclaration()->abstractType() ? currentDeclaration()->abstractType()->toString() : QString("notype")) << "last:" << (lastType() ? lastType()->toString() : QString("(notype)")); ) m_lastDeclaration = m_declarationStack.pop(); } void DeclarationBuilder::visitTypedef(TypedefAST *def) { parseComments(def->comments); DeclarationBuilderBase::visitTypedef(def); } void DeclarationBuilder::visitEnumSpecifier(EnumSpecifierAST* node) { Declaration * declaration = 0; if (!node->isOpaque) { declaration = openDefinition(node->name, node, node->name == 0); } else { // opaque-enum-declaration declaration = openForwardDeclaration(node->name, node); } ///Create mappings iff the AST feature is specified if(m_mapAst) editor()->parseSession()->mapAstDuChain(node, KDevelop::DeclarationPointer(declaration)); DeclarationBuilderBase::visitEnumSpecifier(node); closeDeclaration(); } ///Replaces a CppTemplateParameterType with a DelayedType struct TemplateTypeExchanger : public KDevelop::TypeExchanger { TemplateTypeExchanger(TopDUContext* top) : m_top(top) { } virtual AbstractType::Ptr exchange( const AbstractType::Ptr& type ) { if(CppTemplateParameterType::Ptr templateParamType = type.cast()) { Declaration* decl = templateParamType->declaration(m_top); if(decl) { DelayedType::Ptr newType(new DelayedType()); IndexedTypeIdentifier id(QualifiedIdentifier(decl->identifier())); if(type->modifiers() & AbstractType::ConstModifier) id.setIsConstant(true); if(type->modifiers() & AbstractType::VolatileModifier) id.setIsVolatile(true); newType->setIdentifier(id); newType->setKind(KDevelop::DelayedType::Delayed); return newType.cast(); } } return type; } private: TopDUContext* m_top; }; Cpp::InstantiationInformation DeclarationBuilder::createSpecializationInformation(const Cpp::InstantiationInformation& base, UnqualifiedNameAST* name, KDevelop::DUContext* templateContext) { if(name->template_arguments || base.isValid()) { //Append a scope part InstantiationInformation newCurrent; newCurrent.previousInstantiationInformation = base.indexed(); if(!name->template_arguments) return newCurrent; //Process the template arguments if they exist const ListNode * start = name->template_arguments->toFront(); const ListNode * current = start; do { NameASTVisitor visitor(editor()->parseSession(), 0, templateContext, currentContext()->topContext(), templateContext, templateContext->range().end/*, DUContext::NoUndefinedTemplateParams*/); ExpressionEvaluationResult res = visitor.processTemplateArgument(current->element); AbstractType::Ptr type = res.type.abstractType(); TemplateTypeExchanger exchanger(currentContext()->topContext()); if(type) { type = exchanger.exchange(type); type->exchangeTypes(&exchanger); } newCurrent.addTemplateParameter(type); current = current->next; }while(current != start); return newCurrent; }else{ return base; } } Cpp::IndexedInstantiationInformation DeclarationBuilder::createSpecializationInformation(NameAST* name, DUContext* templateContext) { InstantiationInformation currentInfo; if(name->qualified_names) { const ListNode * start = name->qualified_names->toFront(); const ListNode * current = start; do { currentInfo = createSpecializationInformation(currentInfo, current->element, templateContext); current = current->next; }while(current != start); } if(name->unqualified_name) currentInfo = createSpecializationInformation(currentInfo, name->unqualified_name, templateContext); return currentInfo.indexed(); } void DeclarationBuilder::visitEnumerator(EnumeratorAST* node) { //Ugly hack: Since we want the identifier only to have the range of the id(not //the assigned expression), we change the range of the node temporarily uint oldEndToken = node->end_token; node->end_token = node->id + 1; Identifier id(editor()->parseSession()->token_stream->symbol(node->id)); Declaration* decl = openNormalDeclaration(0, node, id); node->end_token = oldEndToken; DeclarationBuilderBase::visitEnumerator(node); EnumeratorType::Ptr enumeratorType = lastType().cast(); if(ClassMemberDeclaration* classMember = dynamic_cast(currentDeclaration())) { DUChainWriteLocker lock(DUChain::lock()); classMember->setStatic(true); } closeDeclaration(true); if(enumeratorType) { ///@todo Move this into closeDeclaration in a logical way DUChainWriteLocker lock(DUChain::lock()); enumeratorType->setDeclaration(decl); decl->setAbstractType(enumeratorType.cast()); }else if(!lastType().cast()){ //If it's in a template, it may be DelayedType AbstractType::Ptr type = lastType(); kWarning() << "not assigned enumerator type:" << typeid(*type.unsafeData()).name() << type->toString(); } } void DeclarationBuilder::classContextOpened(ClassSpecifierAST* /*node*/, DUContext* context) { //We need to set this early, so we can do correct search while building DUChainWriteLocker lock(DUChain::lock()); currentDeclaration()->setInternalContext(context); } void DeclarationBuilder::visitNamespace(NamespaceAST* ast) { RangeInRevision range; { Identifier id; if(ast->namespace_name) { id = Identifier(editor()->tokensToStrings(ast->namespace_name, ast->namespace_name+1)); range = editor()->findRange(ast->namespace_name, ast->namespace_name+1); }else { id = unnamedNamespaceIdentifier().identifier(); range.start = editor()->findPosition(ast->linkage_body ? ast->linkage_body->start_token : ast->start_token, CppEditorIntegrator::FrontEdge); range.end = range.start; } DUChainWriteLocker lock(DUChain::lock()); Declaration * declaration = openDeclarationReal(0, 0, id, false, false, &range); ///Create mappings iff the AST feature is specified if(m_mapAst) editor()->parseSession()->mapAstDuChain(ast, KDevelop::DeclarationPointer(declaration)); } DeclarationBuilderBase::visitNamespace(ast); QualifiedIdentifier qid; { DUChainWriteLocker lock(DUChain::lock()); currentDeclaration()->setKind(KDevelop::Declaration::Namespace); qid = currentDeclaration()->qualifiedIdentifier(); clearLastType(); closeDeclaration(); } // support for C++11 inlined namespaces by implicitly "using" the namespace in the parent context // i.e. compare to visitUsingDirective() if( ast->inlined && compilingContexts() ) { RangeInRevision aliasRange(range.end + CursorInRevision(0, 1), 0); DUChainWriteLocker lock; NamespaceAliasDeclaration* decl = openDeclarationReal(0, 0, globalImportIdentifier(), false, false, &aliasRange); decl->setImportIdentifier( qid ); closeDeclaration(); } } void DeclarationBuilder::copyTemplateDefaultsFromForward(Identifier searchId, const CursorInRevision& pos) { KDevelop::DUContext* currentTemplateContext = getTemplateContext(currentDeclaration()); if (!currentTemplateContext) return; ///We need to clear the template identifiers, or else it may try to instantiate ///Note that template specializations cannot have default parameters searchId.clearTemplateIdentifiers(); QList declarations = Cpp::findDeclarationsSameLevel(currentContext(), searchId, pos); foreach( Declaration* decl, declarations ) { ForwardDeclaration* forward = dynamic_cast(decl); if (!forward || !decl->abstractType()) continue; KDevelop::DUContext* forwardTemplateContext = forward->internalContext(); if (!forwardTemplateContext || forwardTemplateContext->type() != DUContext::Template) continue; const QVector& forwardList = forwardTemplateContext->localDeclarations(); const QVector& realList = currentTemplateContext->localDeclarations(); if (forwardList.size() != realList.size()) continue; QVector::const_iterator forwardIt = forwardList.begin(); QVector::const_iterator realIt = realList.begin(); for( ; forwardIt != forwardList.end(); ++forwardIt, ++realIt ) { TemplateParameterDeclaration* forwardParamDecl = dynamic_cast(*forwardIt); TemplateParameterDeclaration* realParamDecl = dynamic_cast(*realIt); if( forwardParamDecl && realParamDecl && !forwardParamDecl->defaultParameter().isEmpty()) realParamDecl->setDefaultParameter(forwardParamDecl->defaultParameter()); } } } void DeclarationBuilder::visitClassSpecifier(ClassSpecifierAST *node) { PushValue setNotInTypedef(m_inTypedef, false); /**Open helper contexts around the class, so the qualified identifier matches. * Example: "class MyClass::RealClass{}" * Will create one helper-context named "MyClass" around RealClass * */ CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge); IndexedInstantiationInformation specializedWith; QualifiedIdentifier id; if( node->name ) { identifierForNode(node->name, id); openPrefixContext(node, id, pos); } int kind = editor()->parseSession()->token_stream->kind(node->class_key); ClassDeclaration * declaration = openClassDefinition(node->name, node, node->name == 0, classTypeFromTokenKind(kind)); if (kind == Token_struct || kind == Token_union) m_accessPolicyStack.push(Declaration::Public); else m_accessPolicyStack.push(Declaration::Private); DeclarationBuilderBase::visitClassSpecifier(node); eventuallyAssignInternalContext(); if( node->name ) { ///Copy template default-parameters from the forward-declaration to the real declaration if possible DUChainWriteLocker lock(DUChain::lock()); copyTemplateDefaultsFromForward(id.last(), pos); } closeDeclaration(); ///Create mappings iff the AST feature is specified if(m_mapAst) editor()->parseSession()->mapAstDuChain(node, KDevelop::DeclarationPointer(declaration)); if(node->name) closePrefixContext(id); m_accessPolicyStack.pop(); } void DeclarationBuilder::visitBaseSpecifier(BaseSpecifierAST *node) { DeclarationBuilderBase::visitBaseSpecifier(node); BaseClassInstance instance; { DUChainWriteLocker lock(DUChain::lock()); ClassDeclaration* currentClass = dynamic_cast(currentDeclaration()); if(currentClass) { instance.virtualInheritance = (bool)node->virt; //TypeUtils::unAliasedType( - instance.baseClass = TypeUtils::unAliasedType(lastType())->indexed(); + instance.baseClass = IndexedType(TypeUtils::unAliasedType(lastType())); if(currentClass->classType() == ClassDeclarationData::Struct) instance.access = KDevelop::Declaration::Public; else instance.access = KDevelop::Declaration::Private; if( node->access_specifier ) { quint16 tk = editor()->parseSession()->token_stream->token(node->access_specifier).kind; switch( tk ) { case Token_private: instance.access = KDevelop::Declaration::Private; break; case Token_public: instance.access = KDevelop::Declaration::Public; break; case Token_protected: instance.access = KDevelop::Declaration::Protected; break; } } currentClass->addBaseClass(instance); }else{ kWarning() << "base-specifier without class declaration"; } } addBaseType(instance, node); } QualifiedIdentifier DeclarationBuilder::resolveNamespaceIdentifier(const QualifiedIdentifier& identifier, const CursorInRevision& position) { QList< Declaration* > decls = currentContext()->findDeclarations(identifier, position); QList contexts; // qlist does not provide convenient stable iterators std::list worklist(decls.begin(), decls.end()); for (std::list::iterator it = worklist.begin(); it != worklist.end(); ++it) { Declaration * decl = *it; if(decl->kind() == Declaration::Namespace && decl->internalContext()) { contexts << decl->internalContext(); } else if (decl->kind() == Declaration::NamespaceAlias) { NamespaceAliasDeclaration *aliasDecl = dynamic_cast(decl); if (aliasDecl) { QList importedDecls = currentContext()->findDeclarations(aliasDecl->importIdentifier(), position); std::copy(importedDecls.begin(), importedDecls.end(), std::back_inserter(worklist)); } } } if( contexts.isEmpty() ) { //Failed to resolve namespace kDebug(9007) << "Failed to resolve namespace \"" << identifier << "\""; QualifiedIdentifier ret = identifier; ret.setExplicitlyGlobal(false); Q_ASSERT(ret.count()); return ret; } else { QualifiedIdentifier ret = contexts.first()->scopeIdentifier(true); ret.setExplicitlyGlobal(false); if(ret.isEmpty()) return ret; Q_ASSERT(ret.count()); return ret; } } void DeclarationBuilder::visitUsing(UsingAST * node) { DeclarationBuilderBase::visitUsing(node); QualifiedIdentifier id; identifierForNode(node->name, id); ///@todo only use the last name component as range AliasDeclaration* decl = openDeclaration(0, node->name ? (AST*)node->name : (AST*)node, id.last()); { DUChainWriteLocker lock(DUChain::lock()); CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge); QList declarations = currentContext()->findDeclarations(id, pos); if(!declarations.isEmpty()) { decl->setAliasedDeclaration(declarations[0]); }else{ kDebug(9007) << "Aliased declaration not found:" << id.toString(); } if(m_accessPolicyStack.isEmpty()) decl->setAccessPolicy(KDevelop::Declaration::Public); else decl->setAccessPolicy(currentAccessPolicy()); } closeDeclaration(); } void DeclarationBuilder::visitUsingDirective(UsingDirectiveAST * node) { DeclarationBuilderBase::visitUsingDirective(node); if( compilingContexts() ) { RangeInRevision range = editor()->findRange(node->start_token); DUChainWriteLocker lock(DUChain::lock()); NamespaceAliasDeclaration* decl = openDeclarationReal(0, 0, globalImportIdentifier(), false, false, &range); { QualifiedIdentifier id; identifierForNode(node->name, id); decl->setImportIdentifier( resolveNamespaceIdentifier(id, currentDeclaration()->range().start) ); } closeDeclaration(); } } void DeclarationBuilder::visitAliasDeclaration(AliasDeclarationAST* node) { DeclarationBuilderBase::visitAliasDeclaration(node); if( compilingContexts() ) { PushValue setTypeDef(m_inTypedef, true); openDeclaration(node->name, node->name); closeDeclaration(); } } void DeclarationBuilder::visitTypeId(TypeIdAST * typeId) { //TypeIdAST contains a declarator, but that one does not declare anything PushValue disableDeclarators(m_ignoreDeclarators, true); DeclarationBuilderBase::visitTypeId(typeId); } void DeclarationBuilder::visitNamespaceAliasDefinition(NamespaceAliasDefinitionAST* node) { DeclarationBuilderBase::visitNamespaceAliasDefinition(node); { DUChainReadLocker lock(DUChain::lock()); if( currentContext()->type() != DUContext::Namespace && currentContext()->type() != DUContext::Global ) { ///@todo report problem kDebug(9007) << "Namespace-alias used in non-global scope"; } } if( compilingContexts() ) { RangeInRevision range = editor()->findRange(node->namespace_name); DUChainWriteLocker lock(DUChain::lock()); NamespaceAliasDeclaration* decl = openDeclarationReal(0, 0, Identifier(editor()->parseSession()->token_stream->symbol(node->namespace_name)), false, false, &range); { QualifiedIdentifier id; identifierForNode(node->alias_name, id); decl->setImportIdentifier( resolveNamespaceIdentifier(id, currentDeclaration()->range().start) ); } closeDeclaration(); } } void DeclarationBuilder::visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST* node) { PushValue setNotInTypedef(m_inTypedef, false); int kind = editor()->parseSession()->token_stream->kind(node->type); if( kind == Token_typename ) { //typename is completely handled by the type-builder DeclarationBuilderBase::visitElaboratedTypeSpecifier(node); return; } bool isFriendDeclaration = !m_storageSpecifiers.isEmpty() && (m_storageSpecifiers.top() & ClassMemberDeclaration::FriendSpecifier); bool openedDeclaration = false; if (node->name) { QualifiedIdentifier id; identifierForNode(node->name, id); bool forwardDeclarationGlobal = false; if(m_typeSpecifierWithoutInitDeclarators != node->start_token || isFriendDeclaration) { /**This is an elaborated type-specifier * * See iso c++ draft 3.3.4 for details. * Said shortly it means: * - Search for an existing declaration of the type. If it is found, * it will be used, and we don't need to create a declaration. * - If it is not found, create a forward-declaration in the global/namespace scope. * - @todo While searching for the existing declarations, non-fitting overloaded names should be ignored. * */ ///@todo think how this interacts with re-using duchains. In some cases a forward-declaration should still be created. QList declarations; CursorInRevision pos = editor()->findPosition(node->start_token, CppEditorIntegrator::FrontEdge); { DUChainReadLocker lock(DUChain::lock()); declarations = currentContext()->findDeclarations( id, pos); forwardDeclarationGlobal = true; //If a good declaration has been found, use its type. Else, create a new forward-declaration. foreach(Declaration* decl, declarations) { if((decl->topContext() != currentContext()->topContext() || wasEncountered(decl)) && decl->abstractType()) { setLastType(declarations.first()->abstractType()); if( isFriendDeclaration ) { lock.unlock(); createFriendDeclaration(node); } return; } } } } node->isDeclaration = true; // Create forward declaration switch (kind) { case Token_class: case Token_struct: case Token_union: case Token_enum: if(forwardDeclarationGlobal) { //Open the global context, so it is currentContext() and we can insert the forward-declaration there DUContext* globalCtx; { DUChainReadLocker lock(DUChain::lock()); globalCtx = currentContext(); while(globalCtx && globalCtx->type() != DUContext::Global && globalCtx->type() != DUContext::Namespace) globalCtx = globalCtx->parentContext(); Q_ASSERT(globalCtx); } //Just temporarily insert the new context injectContext( globalCtx ); } openForwardDeclaration(node->name, node); if(forwardDeclarationGlobal) { closeInjectedContext(); } openedDeclaration = true; break; } } DeclarationBuilderBase::visitElaboratedTypeSpecifier(node); if (openedDeclaration) { /* DUChainWriteLocker lock(DUChain::lock()); //Resolve forward-declarations that are declared after the real type was already declared Q_ASSERT(dynamic_cast(currentDeclaration())); IdentifiedType* idType = dynamic_cast(lastType().data()); if(idType && idType->declaration()) static_cast(currentDeclaration())->setResolved(idType->declaration());*/ closeDeclaration(); } if( isFriendDeclaration ) createFriendDeclaration(node); } void DeclarationBuilder::createFriendDeclaration(AST* range) { static IndexedIdentifier friendIdentifier(Identifier("friend")); openDeclaration(0, range, friendIdentifier.identifier(), true); closeDeclaration(); } void DeclarationBuilder::visitAccessSpecifier(AccessSpecifierAST* node) { bool isSlot = false; bool isSignal = false; if (node->specs) { const ListNode *it = node->specs->toFront(); const ListNode *end = it; do { int kind = editor()->parseSession()->token_stream->kind(it->element); switch (kind) { case Token___qt_slots__: case Token_k_dcop: isSlot = true; break; case Token_public: setAccessPolicy(Declaration::Public); break; case Token_k_dcop_signals: case Token___qt_signals__: isSignal = true; case Token_protected: setAccessPolicy(Declaration::Protected); break; case Token_private: setAccessPolicy(Declaration::Private); break; } it = it->next; } while (it != end); } if(isSignal) setAccessPolicy((KDevelop::Declaration::AccessPolicy)(currentAccessPolicy() | FunctionIsSignal)); if(isSlot) setAccessPolicy((KDevelop::Declaration::AccessPolicy)(currentAccessPolicy() | FunctionIsSlot)); DeclarationBuilderBase::visitAccessSpecifier(node); } void DeclarationBuilder::parseStorageSpecifiers(const ListNode* storage_specifiers) { ClassMemberDeclaration::StorageSpecifiers specs = 0; if (storage_specifiers) { const ListNode *it = storage_specifiers->toFront(); const ListNode *end = it; do { int kind = editor()->parseSession()->token_stream->kind(it->element); switch (kind) { case Token_friend: specs |= ClassMemberDeclaration::FriendSpecifier; break; case Token_auto: specs |= ClassMemberDeclaration::AutoSpecifier; break; case Token_register: specs |= ClassMemberDeclaration::RegisterSpecifier; break; case Token_static: specs |= ClassMemberDeclaration::StaticSpecifier; break; case Token_extern: specs |= ClassMemberDeclaration::ExternSpecifier; break; case Token_mutable: specs |= ClassMemberDeclaration::MutableSpecifier; break; } it = it->next; } while (it != end); } m_storageSpecifiers.push(specs); } void DeclarationBuilder::parseFunctionSpecifiers(const ListNode* function_specifiers) { AbstractFunctionDeclaration::FunctionSpecifiers specs = 0; if (function_specifiers) { const ListNode *it = function_specifiers->toFront(); const ListNode *end = it; do { int kind = editor()->parseSession()->token_stream->kind(it->element); switch (kind) { case Token_inline: specs |= AbstractFunctionDeclaration::InlineSpecifier; break; case Token_virtual: specs |= AbstractFunctionDeclaration::VirtualSpecifier; break; case Token_explicit: specs |= AbstractFunctionDeclaration::ExplicitSpecifier; break; } it = it->next; } while (it != end); } m_functionSpecifiers.push(specs); } void DeclarationBuilder::visitParameterDeclaration(ParameterDeclarationAST* node) { if(m_mapAst) m_mappedNodes.push(node); // arguments of a function pointer typedef are not typedefs themselves PushValue setNotInTypedef(m_inTypedef, false); DeclarationBuilderBase::visitParameterDeclaration(node); AbstractFunctionDeclaration* function = currentDeclaration(); if( function ) { if( node->expression ) { DUChainWriteLocker lock(DUChain::lock()); //Fill default-parameters QString defaultParam = stringFromSessionTokens( editor()->parseSession(), node->expression->start_token, node->expression->end_token ).trimmed(); function->addDefaultParameter(IndexedString(defaultParam)); } if( !node->declarator ) { //If there is no declarator, still create a declaration openDefinition(0, node, true); closeDeclaration(); } } if(m_mapAst) m_mappedNodes.pop(); } void DeclarationBuilder::popSpecifiers() { m_functionSpecifiers.pop(); m_storageSpecifiers.pop(); } void DeclarationBuilder::applyStorageSpecifiers() { if (!m_storageSpecifiers.isEmpty() && m_storageSpecifiers.top() != 0) if (ClassMemberDeclaration* member = dynamic_cast(currentDeclaration())) { DUChainWriteLocker lock(DUChain::lock()); member->setStorageSpecifiers(m_storageSpecifiers.top()); } } void DeclarationBuilder::inheritVirtualSpecifierFromOverridden(ClassFunctionDeclaration* classFun) { //To be truly correct, this function should: // 1. differentiate between various overloads // 2. differentiate between cast operators, which all have the same identifier // 3. perform a correct search for the destructor (which has a different identifier in each base class) //This correctness is currently ignored as a matter of cost(in speed) vs benefit (TODO: #3 at least) if(!classFun || classFun->isVirtual() || classFun->isConstructor() || classFun->isDestructor()) return; QList overridden; Identifier searchId = classFun->identifier(); //In correct code this should actually only happen in the case of a specialization destructor //(Which isn't handled). In any case though, we don't need or want to search in instantiations. searchId.clearTemplateIdentifiers(); foreach(const DUContext::Import &import, currentContext()->importedParentContexts()) { DUContext* iContext = import.context(topContext()); if(iContext && iContext->type() == DUContext::Class) { overridden += iContext->findDeclarations(QualifiedIdentifier(searchId), CursorInRevision::invalid(), classFun->abstractType(), classFun->topContext(), DUContext::DontSearchInParent); } } foreach(Declaration* decl, overridden) { if(AbstractFunctionDeclaration* fun = dynamic_cast(decl)) if(fun->isVirtual()) classFun->setVirtual(true); } } void DeclarationBuilder::applyFunctionSpecifiers() { DUChainWriteLocker lock(DUChain::lock()); AbstractFunctionDeclaration* function = dynamic_cast(currentDeclaration()); if(!function) return; if (!m_functionSpecifiers.isEmpty() && m_functionSpecifiers.top() != 0) { function->setFunctionSpecifiers(m_functionSpecifiers.top()); }else{ function->setFunctionSpecifiers((AbstractFunctionDeclaration::FunctionSpecifiers)0); } inheritVirtualSpecifierFromOverridden(dynamic_cast(function)); } bool DeclarationBuilder::checkParameterDeclarationClause(ParameterDeclarationClauseAST* clause) { { DUChainReadLocker lock(DUChain::lock()); if(currentContext()->type() == DUContext::Other) //Cannot declare a function in a code-context return false; ///@todo create warning/error } if(!clause || !clause->parameter_declarations) return true; AbstractType::Ptr oldLastType = lastType(); bool oldLastTypeWasAuto = lastTypeWasAuto(); bool oldLastTypeWasInstance = lastTypeWasInstance(); // type builder must do all its work here bool oldComputeSimplified = m_onlyComputeSimplified; setComputeSimplified(false); const ListNode *start = clause->parameter_declarations->toFront(); const ListNode *it = start; bool ret = false; do { ParameterDeclarationAST* ast = it->element; if(ast) { if(m_collectQtFunctionSignature) { uint endToken = ast->end_token; if(ast->type_specifier) endToken = ast->type_specifier->end_token; if(ast->declarator) { if(ast->declarator->id) endToken = ast->declarator->id->start_token; else endToken = ast->declarator->end_token; } if(!m_qtFunctionSignature.isEmpty()) m_qtFunctionSignature += ", "; m_qtFunctionSignature += editor()->tokensToByteArray(ast->start_token, endToken); ret = true; }else{ if(ast->expression || ast->declarator) { ret = true; //If one parameter has a default argument or a parameter name, it is surely a parameter break; } visit(ast->type_specifier); if( lastType() ) { //Break on the first valid thing found if( lastTypeWasInstance() ) { ret = false; break; }else if(lastType().cast() && lastType().cast()->kind() == DelayedType::Unresolved) { //When the searched item was not found, expect it to be a non-type //except for varargs ret = TypeUtils::isVarArgs(lastType()); break; }else{ ret = true; break; } } } } it = it->next; } while (it != start); setLastType(oldLastType); setLastTypeWasAuto(oldLastTypeWasAuto); setLastTypeWasInstance(oldLastTypeWasInstance); setComputeSimplified(oldComputeSimplified); return ret; } /// Set the internal context of a declaration; for example, a class declaration's internal context /// is the context inside the brackets: class ClassName { ... } void DeclarationBuilder::eventuallyAssignInternalContext() { if (TypeBuilder::lastContext()) { DUChainWriteLocker lock(DUChain::lock()); if( dynamic_cast(currentDeclaration()) ) Q_ASSERT( !static_cast(currentDeclaration())->isConstructor() || currentDeclaration()->context()->type() == DUContext::Class ); if(TypeBuilder::lastContext() && (TypeBuilder::lastContext()->type() == DUContext::Class || TypeBuilder::lastContext()->type() == DUContext::Other || TypeBuilder::lastContext()->type() == DUContext::Function || TypeBuilder::lastContext()->type() == DUContext::Template || TypeBuilder::lastContext()->type() == DUContext::Enum || (TypeBuilder::lastContext()->type() == DUContext::Namespace && currentDeclaration()->kind() == Declaration::Namespace) ) ) { if( !TypeBuilder::lastContext()->owner() || !TypeBuilder::wasEncountered(TypeBuilder::lastContext()->owner()) ) { //if the context is already internalContext of another declaration, leave it alone currentDeclaration()->setInternalContext(TypeBuilder::lastContext()); TypeBuilder::clearLastContext(); } } } } diff --git a/languages/cpp/cppduchain/expressionparser.cpp b/languages/cpp/cppduchain/expressionparser.cpp index be2832887a..6b925bc813 100644 --- a/languages/cpp/cppduchain/expressionparser.cpp +++ b/languages/cpp/cppduchain/expressionparser.cpp @@ -1,221 +1,221 @@ /* This file is part of KDevelop Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "expressionparser.h" #include #include "cppduchain/usebuilder.h" #include "cppduchain/declarationbuilder.h" #include "cppduchain/dumpchain.h" #include "cppduchain/dumptypes.h" #include #include #include "ast.h" #include "parsesession.h" #include "parser.h" #include "control.h" #include #include #include "expressionvisitor.h" #include namespace Cpp { using namespace KDevelop; ExpressionParser::ExpressionParser( bool strict, bool debug, bool propagateConstness, bool mapAst ) : m_strict(strict) , m_debug(debug) , m_propagateConstness(propagateConstness) , m_mapAst(mapAst) { } QHash buildStaticLookupTable() { QHash ret; ExpressionEvaluationResult res; IntegralType::Ptr type(new IntegralType()); type->setDataType(IntegralType::TypeBoolean); res.type = type->indexed(); ret.insert("bool", res); type->setDataType(IntegralType::TypeChar); res.type = type->indexed(); ret.insert("char", res); type->setDataType(IntegralType::TypeFloat); res.type = type->indexed(); ret.insert("float", res); type->setDataType(IntegralType::TypeDouble); res.type = type->indexed(); ret.insert("double", res); type->setDataType(IntegralType::TypeInt); res.type = type->indexed(); ret.insert("int", res); type->setDataType(IntegralType::TypeVoid); res.type = type->indexed(); ret.insert("void", res); type->setDataType(IntegralType::TypeWchar_t); res.type = type->indexed(); ret.insert("wchar_t", res); ConstantIntegralType::Ptr constType(new ConstantIntegralType); constType->setDataType(IntegralType::TypeBoolean); constType->setValue(true); res.type = constType->indexed(); ret.insert("true", res); ///NOTE: the trailing space is by intention, apparently thats what gets queried ret.insert("true ", res); constType->setValue(false); res.type = constType->indexed(); ret.insert("false", res); ///NOTE: the trailing space is by intention, apparently thats what gets queried ret.insert("false ", res); DelayedType::Ptr ellipsis(new DelayedType); ellipsis->setKind(DelayedType::Unresolved); ellipsis->setIdentifier(IndexedTypeIdentifier("...")); res.type = ellipsis->indexed(); ret.insert("...", res); ///TODO: extend at will return ret; } bool tryDirectLookup(const QByteArray& unit) { if (unit.isEmpty()) { return false; } if (!std::isalpha(unit.at(0)) && unit.at(0) != '_') { return false; } for (QByteArray::const_iterator it = unit.constBegin() + 1; it != unit.constEnd(); ++it) { if (!std::isalnum(*it) && *it != ':' && *it != '_') { return false; } } return true; } ExpressionEvaluationResult ExpressionParser::evaluateType( const QByteArray& unit, DUContextPointer context, const TopDUContext* source, bool forceExpression ) { if( m_debug ) kDebug(9007) << "==== .Evaluating ..:" << endl << unit; // fast path for common built-in types static const QHash staticLookupTable = buildStaticLookupTable(); QHash< QByteArray, ExpressionEvaluationResult >::const_iterator it = staticLookupTable.constFind(unit); if (it != staticLookupTable.constEnd()) { return it.value(); } // fast path for direct lookup of identifiers if (!forceExpression && tryDirectLookup(unit)) { DUChainReadLocker lock; QList< Declaration* > decls = context->findDeclarations(QualifiedIdentifier(QString::fromUtf8(unit)), CursorInRevision::invalid(), AbstractType::Ptr(), source); if (!decls.isEmpty()) { ExpressionEvaluationResult res; foreach(Declaration* decl, decls) { res.allDeclarations.append(decl->id()); } res.type = decls.first()->indexedType(); return res; } } ParseSession session; Control control; DumpChain dumper; Parser parser(&control); AST* ast = 0; session.setContentsAndGenerateLocationTable(tokenizeFromByteArray(unit)); ast = parser.parseTypeOrExpression(&session, forceExpression); if(!ast) { kDebug(9007) << "Failed to parse \"" << unit << "\""; return ExpressionEvaluationResult(); } if (m_debug) { kDebug(9007) << "===== AST:"; dumper.dump(ast, &session); } ast->ducontext = context.data(); if(!ast->ducontext) { kDebug() << "context disappeared"; return ExpressionEvaluationResult(); } ExpressionEvaluationResult ret = evaluateType( ast, &session, source ); return ret; } ExpressionEvaluationResult ExpressionParser::evaluateExpression( const QByteArray& expression, DUContextPointer context, const TopDUContext* source ) { return evaluateType( expression, context, source, true ); } ExpressionEvaluationResult ExpressionParser::evaluateType( AST* ast, ParseSession* session, const TopDUContext* source ) { if (m_debug) { DumpChain dumper; kDebug(9007) << "===== AST:"; dumper.dump(ast, session); } ExpressionEvaluationResult ret; ExpressionVisitor v(session, source, m_strict, m_propagateConstness, m_mapAst); v.parse( ast ); DUChainReadLocker lock(DUChain::lock()); - ret.type = v.lastType()->indexed(); + ret.type = v.lastType() ? v.lastType()->indexed() : IndexedType(); ret.isInstance = v.lastInstance().isInstance; if(v.lastInstance().declaration) ret.instanceDeclaration = DeclarationId(IndexedDeclaration(v.lastInstance().declaration.data())); foreach(const DeclarationPointer &decl, v.lastDeclarations()) { if(decl) ret.allDeclarations.append(decl->id()); } return ret; } } diff --git a/languages/cpp/cppduchain/name_visitor.cpp b/languages/cpp/cppduchain/name_visitor.cpp index 15ae8771d5..8c9bfba679 100644 --- a/languages/cpp/cppduchain/name_visitor.cpp +++ b/languages/cpp/cppduchain/name_visitor.cpp @@ -1,296 +1,296 @@ /* This file is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //krazy:excludeall=cpp #include "name_visitor.h" #include "name_compiler.h" #include "type_visitor.h" #include "lexer.h" #include "symbol.h" #include "parsesession.h" #include "tokens.h" #include "expressionparser.h" #include "expressionvisitor.h" #include #include #define LOCKDUCHAIN DUChainReadLocker lock(DUChain::lock()) using namespace KDevelop; using namespace Cpp; NameASTVisitor::NameASTVisitor(ParseSession* session, Cpp::ExpressionVisitor* visitor, const KDevelop::DUContext* context, const KDevelop::TopDUContext* source, const KDevelop::DUContext* localVisibilityContext, const CursorInRevision& position, KDevelop::DUContext::SearchFlags localSearchFlags, bool debug ) : m_session(session), m_visitor(visitor), m_context(context), m_source(source), m_localContext(localVisibilityContext), m_find(m_context, m_source, localSearchFlags, CursorInRevision(position) ), m_debug(debug), m_finalName(0) { m_flags = localSearchFlags; m_stopSearch = false; } void NameASTVisitor::visitUnqualifiedName(UnqualifiedNameAST *node) { if(m_stopSearch) return; IndexedString tmp_name; if (node->id) tmp_name = m_session->token_stream->symbol(node->id); if (node->tilde) tmp_name = IndexedString(QLatin1String("~") + tmp_name.str()); if (OperatorFunctionIdAST *op_id = node->operator_id) { QString tmpString = QLatin1String("operator"); if (op_id->op && op_id->op->op) tmpString += m_session->stringForNode(op_id->op, true); else tmpString += QLatin1String("{...cast...}"); tmp_name = IndexedString(tmpString); m_typeSpecifier = op_id->type_specifier; } m_currentIdentifier = Identifier(tmp_name); m_find.openIdentifier(m_currentIdentifier); if (node->template_arguments) { visitNodes(this, node->template_arguments); } else if(node->end_token == node->start_token + 3 && node->id == node->start_token && m_session->token_stream->kind(node->id+1) == '<') { ///@todo Represent this nicer in the AST ///It's probably a type-specifier with instantiation of the default-parameter, like "Bla<>". m_find.openQualifiedIdentifier( ExpressionEvaluationResult() ); m_find.closeQualifiedIdentifier(); } { LOCKDUCHAIN; m_find.closeIdentifier(node == m_finalName); } if( (node->id || node->operator_id) && !m_find.lastDeclarations().isEmpty() ) { bool had = false; uint start_token; uint end_token; if ( node->id ) { start_token = node->id; end_token = node->id + 1; } else { start_token = node->start_token; end_token = node->end_token; } foreach(const DeclarationPointer &decl, m_find.lastDeclarations()) { if(decl && !decl->isForwardDeclaration()) { //Prefer registering non forward-declaration uses if(m_visitor) m_visitor->newUse( node, start_token, end_token, decl ); had = true; break; } } if(!had) //only forward-declarations, register to any. if(m_visitor) m_visitor->newUse( node, start_token,end_token, m_find.lastDeclarations()[0] ); } else { if(node == m_finalName) { if(m_visitor) { //Create a zero use, which will be highlighted as an error bool createUse = false; { LOCKDUCHAIN; createUse = !m_foundSomething || !Cpp::isTemplateDependent(m_foundSomething.data()); } if(createUse) m_visitor->newUse(node, node->id, node->id+1, DeclarationPointer()); } if( m_debug ) kDebug( 9007 ) << "failed to find " << m_currentIdentifier << " as part of " << m_session->stringForNode(node) << ", searched in " << m_find.describeLastContext(); } } _M_name.push(m_currentIdentifier); if(!m_find.lastDeclarations().isEmpty()) { m_foundSomething = m_find.lastDeclarations().first(); } } TypeSpecifierAST* NameASTVisitor::lastTypeSpecifier() const { if(m_stopSearch) return 0; return m_typeSpecifier; } void NameASTVisitor::visitTemplateArgument(TemplateArgumentAST *node) { if(m_stopSearch) return; processTemplateArgument(node); } ExpressionEvaluationResult NameASTVisitor::processTemplateArgument(TemplateArgumentAST *node) { if(m_stopSearch) return ExpressionEvaluationResult(); ExpressionEvaluationResult res; bool opened = false; if( node->expression ) { bool ownVisitor = false; if(!m_visitor) { m_visitor = new ExpressionVisitor(m_session, m_source); ownVisitor = true; } if (!node->expression->ducontext) { node->expression->ducontext = const_cast(m_localContext); } m_visitor->parse( node->expression ); if( m_visitor->lastType() ) { LOCKDUCHAIN; - res.type = m_visitor->lastType()->indexed(); + res.type = IndexedType(m_visitor->lastType()); foreach(const DeclarationPointer &decl, m_visitor->lastDeclarations()) if(decl) res.allDeclarationsList().append(decl->id()); res.isInstance = m_visitor->lastInstance().isInstance; if(m_visitor->lastInstance().declaration) res.instanceDeclaration = m_visitor->lastInstance().declaration->id(); m_find.openQualifiedIdentifier(res); opened = true; }else if( m_debug ) { kDebug(9007) << "Failed to resolve template-argument " << m_session->stringForNode(node->expression); } if(ownVisitor) { delete m_visitor; m_visitor = 0; } } else if( node->type_id ) { TypeASTVisitor v( m_session, m_visitor, m_localContext, m_source, m_localContext, m_debug ); v.run( node->type_id ); if(v.stoppedSearch()) { m_stopSearch = true; return ExpressionEvaluationResult(); } - res.type = v.type()->indexed(); + res.type = IndexedType(v.type()); if( res.type ) { LOCKDUCHAIN; foreach(const DeclarationPointer &decl, v.declarations()) if(decl) res.allDeclarationsList().append(decl->id()); m_find.openQualifiedIdentifier(res); opened = true; } }else{ LOCKDUCHAIN; m_find.openQualifiedIdentifier(false); m_find.openIdentifier(Identifier(m_session->stringForNode(node))); m_find.closeIdentifier(false); opened = true; } LOCKDUCHAIN; if(opened) m_find.closeQualifiedIdentifier(); m_currentIdentifier.appendTemplateIdentifier( typeIdentifierFromTemplateArgument(m_session, node) ); return res; } const QualifiedIdentifier& NameASTVisitor::identifier() const { if(m_stopSearch) { static const QualifiedIdentifier tmpQI; return tmpQI; } return _M_name; } void NameASTVisitor::run(UnqualifiedNameAST *node, bool skipThisName) { m_finalName = node; m_find.openQualifiedIdentifier(false); m_typeSpecifier = 0; _M_name.clear(); if(skipThisName) DefaultVisitor::visitUnqualifiedName(node); else visit(node); if(m_stopSearch) return; LOCKDUCHAIN; m_find.closeQualifiedIdentifier(); if(m_find.lastDeclarations().isEmpty() && (m_flags & DUContext::NoUndefinedTemplateParams)) { m_stopSearch = true; return; } } QList NameASTVisitor::declarations() const { if(m_stopSearch) return QList(); return m_find.lastDeclarations(); } void NameASTVisitor::run(NameAST *node, bool skipLastNamePart) { m_find.openQualifiedIdentifier(node->global); m_typeSpecifier = 0; _M_name.clear(); m_finalName = node->unqualified_name; if(skipLastNamePart) visitNodes(this, node->qualified_names); //Skip the unqualified name else visit(node); if(m_stopSearch) return; _M_name.setExplicitlyGlobal( node->global ); LOCKDUCHAIN; m_find.closeQualifiedIdentifier(); } DeclarationPointer NameASTVisitor::foundSomething() const { return m_foundSomething; } diff --git a/languages/cpp/cppduchain/templatedeclaration.cpp b/languages/cpp/cppduchain/templatedeclaration.cpp index d5ea57301e..aa1ea2fd4b 100644 --- a/languages/cpp/cppduchain/templatedeclaration.cpp +++ b/languages/cpp/cppduchain/templatedeclaration.cpp @@ -1,1190 +1,1190 @@ /* This file is part of KDevelop Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "templatedeclaration.h" #include #include #include #include #include #include #include #include #include #include "templateparameterdeclaration.h" #include "qtfunctiondeclaration.h" #include "cppducontext.h" #include "expressionparser.h" #include "templateresolver.h" #include #include #include #include using namespace KDevelop; using namespace Cpp; #define REGISTER_TEMPLATE_DECLARATION(Declaration) typedef SpecialTemplateDeclaration TheTemplate ## Declaration; \ REGISTER_DUCHAIN_ITEM_WITH_DATA(TheTemplate ## Declaration, SpecialTemplateDeclarationData); REGISTER_TEMPLATE_DECLARATION(Declaration) REGISTER_TEMPLATE_DECLARATION(ClassDeclaration) REGISTER_TEMPLATE_DECLARATION(TemplateParameterDeclaration) REGISTER_TEMPLATE_DECLARATION(ClassFunctionDeclaration) REGISTER_TEMPLATE_DECLARATION(ClassMemberDeclaration) REGISTER_TEMPLATE_DECLARATION(FunctionDeclaration) REGISTER_TEMPLATE_DECLARATION(QtFunctionDeclaration) REGISTER_TEMPLATE_DECLARATION(FunctionDefinition) REGISTER_TEMPLATE_DECLARATION(AliasDeclaration) REGISTER_TEMPLATE_DECLARATION(ForwardDeclaration) QMutex TemplateDeclaration::instantiationsMutex(QMutex::Recursive); typedef CppDUContext StandardCppDUContext; namespace Cpp { DEFINE_LIST_MEMBER_HASH(SpecialTemplateDeclarationData, m_specializations, IndexedDeclaration) DEFINE_LIST_MEMBER_HASH(SpecialTemplateDeclarationData, m_specializedWith, IndexedType) } AbstractType::Ptr applyPointerReference( AbstractType::Ptr ptr, const KDevelop::IndexedTypeIdentifier& id ) { AbstractType::Ptr ret = ptr; if(ret && ((static_cast(ret->modifiers() & AbstractType::ConstModifier) != id.isConstant()) || (static_cast(ret->modifiers() & AbstractType::VolatileModifier) != id.isVolatile()))) { ret->setModifiers((id.isConstant() ? AbstractType::ConstModifier : AbstractType::NoModifiers) | (id.isVolatile() ? AbstractType::VolatileModifier : AbstractType::NoModifiers)); } for( int a = 0; a < id.pointerDepth(); ++a ) { uint modifiers = AbstractType::NoModifiers; if( id.isConstPointer( a ) ) modifiers = AbstractType::ConstModifier; PointerType::Ptr newRet( new PointerType() ); newRet->setModifiers(modifiers); newRet->setBaseType( ret ); ret = newRet.cast(); } if(id.isReference() ) { uint modifiers = AbstractType::NoModifiers; if( id.isConstant() ) modifiers |= AbstractType::ConstModifier; if( id.isVolatile() ) modifiers |= AbstractType::VolatileModifier; ReferenceType::Ptr newRet( new ReferenceType() ); newRet->setModifiers(modifiers); newRet->setBaseType( ret ); newRet->setIsRValue(id.isRValue()); ret = newRet.cast(); } return ret; } ///Finds out whether any DelayedType's are involved in a given type(It searches return-values, argument-types, base-classes, etc.) struct DelayedTypeSearcher : public KDevelop::SimpleTypeVisitor { bool found; DelayedTypeSearcher() : found(false) { } // We want to use SimpleTypeVisitor implementations using SimpleTypeVisitor::visit; virtual bool visit ( const AbstractType* type ) override { if( found ) return false; if( dynamic_cast(type) ) found = true; return !found; } virtual bool visit (const StructureType *) override { ///We do not want to visit member-types, so return false here return false; } }; namespace Cpp { TypePtr containsDelayedType(AbstractType::Ptr type) { PointerType::Ptr pType = type.cast(); ReferenceType::Ptr rType = type.cast(); DelayedType::Ptr delayedType = type.cast(); TypeAliasType::Ptr aType = type.cast(); if( pType ) return containsDelayedType(pType->baseType()); if( rType ) return containsDelayedType(rType->baseType()); if( aType ) return containsDelayedType(aType->type()); return delayedType; } } /** * Thread-local data to ensure recursion limits are not exceeded. * Also holds the implementation data to support defaulte template parameters. */ struct ThreadLocalData { ThreadLocalData() : delayedDepth(0) , aliasDepth(0) {} // used to apply default template parameters QMultiHash typeOverloads; // recursion counter for delayed type resolution uint delayedDepth; // recursion counter for alias type resolution uint aliasDepth; }; #if (QT_VERSION >= 0x040801) QThreadStorage threadData; inline ThreadLocalData& threadDataLocal() { return threadData.localData(); } #else QThreadStorage threadData; inline ThreadLocalData& threadDataLocal() { if(!threadData.localData()) threadData.setLocalData(new ThreadLocalData()); return *threadData.localData(); } #endif /** * RAII class to push/pop a type overload for a given identifier. */ struct PushTypeOverload { PushTypeOverload(const IndexedQualifiedIdentifier& qid_, const IndexedType& type_) : qid(qid_) , type(type_) , data(threadDataLocal()) { data.typeOverloads.insert(qid, type); } ~PushTypeOverload() { data.typeOverloads.remove(qid, type); } private: IndexedQualifiedIdentifier qid; IndexedType type; ThreadLocalData& data; }; /** * Replaces any DelayedTypes in interesting positions with their resolved versions, * if they can be resolved. */ struct DelayedTypeResolver : public KDevelop::TypeExchanger { const KDevelop::DUContext* searchContext; const KDevelop::TopDUContext* source; KDevelop::DUContext::SearchFlags searchFlags; DelayedTypeResolver(const DUContext* _searchContext, const TopDUContext* _source, DUContext::SearchFlags _searchFlags = DUContext::NoUndefinedTemplateParams) : searchContext(_searchContext) , source(_source) , searchFlags(_searchFlags) { } virtual AbstractType::Ptr exchange( const AbstractType::Ptr& type ) { ThreadLocalData& data = threadDataLocal(); PushValue inc(data.delayedDepth, data.delayedDepth + 1); if( data.delayedDepth > 30 ) { kDebug(9007) << "Too much depth in DelayedTypeResolver::exchange, while exchanging" << (type ? type->toString() : QString("(null)")); return type; } DelayedType::Ptr delayedType = type.cast(); if( delayedType && delayedType->kind() == DelayedType::Delayed ) { QualifiedIdentifier qid = delayedType->identifier().identifier().identifier(); if( !qid.isExpression() ) { // look for default template parameters IndexedType indexedType = data.typeOverloads.value(qid); if(!indexedType) { // fall back to normal DUContext search DUContext::SearchItem::PtrList identifiers; identifiers << DUContext::SearchItem::Ptr( new DUContext::SearchItem(qid) ); DUContext::DeclarationList decls; if( !searchContext->findDeclarationsInternal( identifiers, searchContext->range().end, AbstractType::Ptr(), decls, source, searchFlags, 0 ) ) return type; if( !decls.isEmpty() ) { indexedType = decls[0]->indexedType(); } } if( indexedType.isValid() ) { return applyPointerReference(indexedType.abstractType(), delayedType->identifier()); } } ///Resolution as type has failed, or is not appropriate. ///Resolve delayed expression, for example static numeric expressions ExpressionParser p; ExpressionEvaluationResult res; if( qid.isExpression() ) res = p.evaluateExpression( delayedType->identifier().toString().toUtf8(), DUContextPointer(const_cast(searchContext)), source ); else res = p.evaluateType( delayedType->identifier().toString().toUtf8(), DUContextPointer(const_cast(searchContext)), source ); // NOTE: This looks hacky, but see e.g. TestDUChain::testDecltypeTypedef - we really _never_ // can replace a delayed type with a CppTemplateParameterType. Instead we need to create // a new delayed type for that with its identifier. //TODO: try to add support for replacing CppTemplateParameterTypes directly eventually... // dunno if its actually doable though AbstractType::Ptr ret = res.type.abstractType(); if (CppTemplateParameterType::Ptr tplParam = ret.cast()) { DelayedType::Ptr delayed(new DelayedType); delayed->setIdentifier(IndexedTypeIdentifier(tplParam->declarationId().qualifiedIdentifier().last().toString())); return delayed.cast(); } return ret; }else{ if( containsDelayedType(type) ) { //Copy the type to keep the correct reference/pointer structure AbstractType::Ptr typeCopy( type->clone() ); PointerType::Ptr pType = typeCopy.cast(); ReferenceType::Ptr rType = typeCopy.cast(); TypeAliasType::Ptr aType = typeCopy.cast(); if( pType ) //Replace the base pType->exchangeTypes(this); if( rType ) //Replace the base rType->exchangeTypes(this); if( aType ) aType->exchangeTypes(this); return typeCopy; } } return type; } virtual bool exchangeMembers() const { return false; } private: AbstractType::Ptr keepAlive; }; // bool operator==( const ExpressionEvaluationResult& left, const ExpressionEvaluationResult& right ) { // return left.type == right.type && left.isInstance == right.isInstance; // } namespace Cpp { // bool operator==( const QList& left, const QList& right ) { // return true; // } TemplateDeclaration::TemplateDeclaration(const TemplateDeclaration& /*rhs*/) : m_instantiatedFrom(0) , m_instantiationDepth(0) { } TemplateDeclaration::TemplateDeclaration() : m_instantiatedFrom(0) , m_instantiationDepth(0) { } Declaration* TemplateDeclaration::specialize(const IndexedInstantiationInformation& specialization, const TopDUContext* topContext, int upDistance) { if(!specialization.isValid()) return dynamic_cast(this); else { InstantiationInformation information = IndexedInstantiationInformation( specialization ).information(); //Add empty elements until the specified depth for(int a = 0; a < upDistance; ++a) { InstantiationInformation nextInformation; nextInformation.previousInstantiationInformation = information.indexed(); information = nextInformation; } return instantiate(information, topContext); } } IndexedInstantiationInformation TemplateDeclaration::specialization() const { if(m_instantiatedWith.isValid()) return m_instantiatedWith; else return IndexedInstantiationInformation(); } // DeclarationId TemplateDeclaration::id() const { // const Declaration* thisDecl = dynamic_cast(this); // if(m_instantiatedFrom) { // DeclarationId ret = m_instantiatedFrom->id(); // ret.setSpecialization(m_instantiatedWith.index()); // return ret; // } else{ // thisDecl->topContext()->ownIndex(); // return thisDecl->Declaration::id(); // } // } DUContext* TemplateDeclaration::templateContext(const TopDUContext* source) const { return getTemplateContext(dynamic_cast(const_cast(this)), source); } TemplateDeclaration::~TemplateDeclaration() { InstantiationsHash instantiations; { ///Unregister at the declaration this one is instantiated from if( m_instantiatedFrom ) { InstantiationsHash::iterator it = m_instantiatedFrom->m_instantiations.find(m_instantiatedWith); if( it != m_instantiatedFrom->m_instantiations.end() ) { Q_ASSERT(*it == this); m_instantiatedFrom->m_instantiations.erase(it); } m_instantiatedFrom = 0; } } deleteAllInstantiations(); } TemplateDeclaration* TemplateDeclaration::instantiatedFrom() const { return m_instantiatedFrom; } void TemplateDeclaration::setSpecializedFrom(TemplateDeclaration* other) { if(other && other->instantiatedFrom()) { setSpecializedFrom(other->instantiatedFrom()); return; } if(other && other->specializedFrom().data()) { setSpecializedFrom(dynamic_cast(other->specializedFrom().data())); return; } IndexedDeclaration indexedSelf(dynamic_cast(this)); IndexedDeclaration indexedOther(dynamic_cast(other)); Q_ASSERT(indexedSelf.data()); if( TemplateDeclaration* tplDec = dynamic_cast(specializedFrom().data()) ) tplDec->removeSpecializationInternal(indexedSelf); setSpecializedFromInternal(indexedOther); if( TemplateDeclaration* otherTemplate = dynamic_cast(indexedOther.data()) ) { otherTemplate->addSpecializationInternal(indexedSelf); otherTemplate->deleteAllInstantiations(); } } void TemplateDeclaration::reserveInstantiation(const IndexedInstantiationInformation& info) { QMutexLocker l(&instantiationsMutex); Q_ASSERT(m_instantiations.find(info) == m_instantiations.end()); m_instantiations.insert(info, 0); } ///Reads the template-parameters from the template-context of the declaration, and puts them into the identifier. ///Must be called AFTER the declaration was instantiated. void updateIdentifierTemplateParameters( Identifier& identifier, Declaration* basicDeclaration, const TopDUContext* top ) { identifier.clearTemplateIdentifiers(); TemplateDeclaration* tempDecl = dynamic_cast(basicDeclaration); if(tempDecl) { InstantiationInformation specializedWith(tempDecl->specializedWith().information()); if(specializedWith.templateParametersSize()) { //Use the information from the specialization-information to build the template-identifiers FOREACH_FUNCTION(const IndexedType& indexedType, specializedWith.templateParameters) { AbstractType::Ptr type = indexedType.abstractType(); if(type) identifier.appendTemplateIdentifier( IndexedTypeIdentifier(type->toString()) ); else identifier.appendTemplateIdentifier( IndexedTypeIdentifier("(missing template type)") ); } return; } } DUContext* templateCtx = getTemplateContext(basicDeclaration, top); if( !templateCtx ) return; for( int a = 0; a < templateCtx->localDeclarations().count(); a++ ) { AbstractType::Ptr type = templateCtx->localDeclarations()[a]->abstractType(); if(type) identifier.appendTemplateIdentifier( IndexedTypeIdentifier(type->toString()) ); else identifier.appendTemplateIdentifier( IndexedTypeIdentifier("(missing template type)") ); } } void TemplateDeclaration::setInstantiatedFrom(TemplateDeclaration* from, const InstantiationInformation& instantiatedWith) { Q_ASSERT(from != this); //Change the identifier so it contains the template-parameters QMutexLocker l(&instantiationsMutex); if( m_instantiatedFrom ) { InstantiationsHash::iterator it = m_instantiatedFrom->m_instantiations.find(m_instantiatedWith); if( it != m_instantiatedFrom->m_instantiations.end() && *it == this ) m_instantiatedFrom->m_instantiations.erase(it); m_instantiatedFrom = 0; } m_instantiatedFrom = from; m_instantiatedWith = instantiatedWith.indexed(); //Only one instantiation is allowed if(from) { //Either it must be reserved, or not exist yet Q_ASSERT(from->m_instantiations.find(instantiatedWith.indexed()) == from->m_instantiations.end() || (*from->m_instantiations.find(instantiatedWith.indexed())) == 0); from->m_instantiations.insert(m_instantiatedWith, this); Q_ASSERT(from->m_instantiations.contains(m_instantiatedWith)); } } bool TemplateDeclaration::isInstantiatedFrom(const TemplateDeclaration* other) const { QMutexLocker l(&instantiationsMutex); InstantiationsHash::const_iterator it = other->m_instantiations.find(m_instantiatedWith); if( it != other->m_instantiations.end() && (*it) == this ) return true; else return false; } void TemplateDeclaration::setTemplateParameterContext(KDevelop::DUContext* context) { dynamicTemplateData()->m_parameterContext = context; } KDevelop::DUContext* TemplateDeclaration::templateParameterContext() const { return const_cast(templateData()->m_parameterContext.data()); ///@todo make data() const; return non-const pointer in duchain-pointer } bool isTemplateDeclaration(const KDevelop::Declaration* decl) { return (bool)dynamic_cast(decl); } ///@todo prevent endless recursion when resolving base-classes!(Parent is not yet in du-chain, so a base-class that references it will cause endless recursion) CppDUContext* instantiateDeclarationAndContext( KDevelop::DUContext* parentContext, const TopDUContext* source, KDevelop::DUContext* context, const InstantiationInformation& templateArguments, Declaration* instantiatedDeclaration, Declaration* instantiatedFrom, bool doNotRegister ) { Q_ASSERT(parentContext); TemplateDeclaration* instantiatedFromTemplate = dynamic_cast(instantiatedFrom); StandardCppDUContext* contextCopy = 0; if( context ) { ///Specialize involved contexts Q_ASSERT(context->parentContext()); //Top-context is not allowed contextCopy = new StandardCppDUContext(context->range(), parentContext, true); //We do not need to care about TopDUContext here, because a top-context can not be instantiated contextCopy->setType(context->type()); contextCopy->setLocalScopeIdentifier(context->localScopeIdentifier()); if( instantiatedDeclaration ) instantiatedDeclaration->setInternalContext(contextCopy); ///Now the created context is already partially functional and can be used for searching(not the instantiated template-params yet though) if( context->type() == KDevelop::DUContext::Template ) { ///We're in the template-parameter context, exchange the template-parameters with their values. ///Specialize the local template-declarations uint currentArgument = 0; InstantiationInformation parameterInstantiationInformation; parameterInstantiationInformation.previousInstantiationInformation = templateArguments.indexed(); foreach(Declaration* decl, context->localDeclarations()) { #ifdef QT_DEBUG TemplateDeclaration* tempDecl = dynamic_cast(decl); Q_ASSERT(tempDecl); // tempDecl->instantiate(parameterInstantiationInformation, source, true); #endif TemplateParameterDeclaration* templateDecl = dynamic_cast(decl); Q_ASSERT(templateDecl); //Only template-parameter declarations are allowed in template-contexts TemplateParameterDeclaration* declCopy = dynamic_cast(decl->clone()); Q_ASSERT(declCopy); TemplateDeclaration* tempCopyDecl = dynamic_cast(declCopy); Q_ASSERT(tempCopyDecl); if( currentArgument < templateArguments.templateParametersSize() && templateArguments.templateParameters()[currentArgument].abstractType() ) { declCopy->setAbstractType( templateArguments.templateParameters()[currentArgument].abstractType() ); } else { //Apply default-parameters, although these should have been applied before //Use the already available delayed-type resolution to resolve the value/type if( !templateDecl->defaultParameter().isEmpty() ) { DelayedType::Ptr delayed( new DelayedType() ); delayed->setIdentifier( IndexedTypeIdentifier(templateDecl->defaultParameter()) ); declCopy->setAbstractType( resolveDelayedTypes( delayed.cast(), contextCopy, source) ); }else{ //Parameter missing } } ///We mark the declaration as a specialization, so no index is created for it within the top-context(that needs a write-lock) tempCopyDecl->setInstantiatedFrom(0, parameterInstantiationInformation); ///This inserts the copied declaration into the copied context declCopy->setContext(contextCopy); ++currentArgument; } } ///Find the template-parameter context, and recurse into it, so we can replace the template parameters foreach( const DUContext::Import &importedContext, context->importedParentContexts() ) { CppDUContext* import = dynamic_cast*>(importedContext.context(source)); if( !import) continue; ///For functions, the Template-context is one level deeper(it is imported by the function-context) so also copy the function-context if( import->type() == KDevelop::DUContext::Template || import->type() == KDevelop::DUContext::Function ) { DUContext* ctx = import->instantiate(templateArguments, source); contextCopy->addImportedParentContext( ctx, CursorInRevision(), true ); if( instantiatedDeclaration && import->type() == KDevelop::DUContext::Template ) { TemplateDeclaration* tempDecl = dynamic_cast(instantiatedDeclaration); if( instantiatedDeclaration ) tempDecl->setTemplateParameterContext( ctx ); else kDebug(9007) << "instantiated declaration is not a template declaration"; } } else { //Import all other imported contexts contextCopy->addImportedParentContext( import, CursorInRevision::invalid(), true ); } } if( instantiatedDeclaration ) { ///We do not need to respect forward-declarations here, because they are not allowed as base-classes. ClassDeclaration* klass = dynamic_cast( instantiatedDeclaration ); if( klass ) { //It could also be a function ///Resolve template-dependent base-classes(They can not be found in the imports-list, because their type is DelayedType and those have no context) uint num = 0; FOREACH_FUNCTION( const BaseClassInstance& base, klass->baseClasses ) { DelayedType::Ptr delayed = base.baseClass.type(); if( delayed ) { ///Resolve the delayed type, and import the context DelayedTypeResolver res(contextCopy, source); AbstractType::Ptr newType( res.exchange(delayed.cast()) ); newType = TypeUtils::unAliasedType(newType); if( CppClassType::Ptr baseClass = newType.cast() ) { if( baseClass->declaration(source) && baseClass->declaration(source)->internalContext() ) { contextCopy->addImportedParentContext( baseClass->declaration(source)->internalContext(), CursorInRevision::invalid(), true ); } BaseClassInstance newInstance(base); newInstance.baseClass = newType->indexed(); klass->replaceBaseClass( num, newInstance ); } else { kWarning(9007) << "Resolved bad base-class" << delayed->toString() << (newType ? newType->toString() : QString()); } } ++num; } } } } else { ///Note: this is possible, for example for template function-declarations(They do not have an internal context, because they have no compound statement), for variables, etc.. ///Directly take their assigned template-parameter-context and specialize it. We need it at least for overload-resolution. TemplateDeclaration* fromTemplateDecl = dynamic_cast(instantiatedFrom); TemplateDeclaration* toTemplateDecl = dynamic_cast(instantiatedDeclaration); if( toTemplateDecl && fromTemplateDecl && fromTemplateDecl->templateParameterContext() ) { CppDUContext* templCtx = dynamic_cast*>(fromTemplateDecl->templateParameterContext()); DUContext* ctx = templCtx->instantiate(templateArguments, source); toTemplateDecl->setTemplateParameterContext( ctx ); } } if( contextCopy && !doNotRegister ) contextCopy->setInstantiatedFrom(dynamic_cast*>(context), templateArguments); ///Since now the context is accessible through the du-chain, so it must not be changed any more. if( instantiatedDeclaration && instantiatedDeclaration->abstractType() ) { ///an AliasDeclaration represents a C++ "using bla::bla;" declaration. if(AliasDeclaration* alias = dynamic_cast(instantiatedDeclaration)) { ThreadLocalData& data = threadDataLocal(); PushValue safety(data.aliasDepth, data.delayedDepth + 1); if(data.aliasDepth > 30) { kWarning() << "depth-limit reached while resolving alias-declaration" << alias->identifier().toString() << "within" << parentContext->scopeIdentifier(true).toString(); }else { ///For alias declaration, we resolve the declaration that is aliased instead of a type. ///For this reason, template alias-declarations have a DelayedType assigned DelayedType::Ptr delayed = alias->type(); if(delayed) { QList declarations = parentContext->findDeclarations(delayed->identifier().identifier().identifier()); if(!declarations.isEmpty()) alias->setAliasedDeclaration(declarations.first()); } } }else{ TemplateDeclaration* instantiatedTemplate = dynamic_cast(instantiatedDeclaration); InstantiationInformation globalTemplateArguments = templateArguments; if(instantiatedTemplate) { //Update the "specializedWith" information InstantiationInformation oldSpecializedWith = instantiatedTemplate->specializedWith().information(); if(oldSpecializedWith.templateParametersSize()) { //Replace the delayed types in the specialization-information with their real types InstantiationInformation newSpecializedWith(oldSpecializedWith); newSpecializedWith.templateParametersList().clear(); FOREACH_FUNCTION(const IndexedType& type, oldSpecializedWith.templateParameters) newSpecializedWith.addTemplateParameter(resolveDelayedTypes(type.abstractType(), instantiatedDeclaration->internalContext() ? instantiatedDeclaration->internalContext() : parentContext, source )); instantiatedTemplate->setSpecializedWith(newSpecializedWith.indexed()); globalTemplateArguments = newSpecializedWith; } } ///Resolve all involved delayed types AbstractType::Ptr t(instantiatedDeclaration->abstractType()); IdentifiedType* idType = dynamic_cast(t.unsafeData()); ///Use the internal context if it exists, so undefined template-arguments can be found and the DelayedType can be further delayed then. AbstractType::Ptr changedType = resolveDelayedTypes( instantiatedDeclaration->abstractType(), instantiatedDeclaration->internalContext() ? instantiatedDeclaration->internalContext() : parentContext, source ); if( idType && idType->declarationId() == instantiatedFrom->id() ) { if( changedType == instantiatedDeclaration->abstractType() ) changedType = instantiatedDeclaration->abstractType()->clone(); IdentifiedType* changedIdType = dynamic_cast(changedType.unsafeData()); if( changedIdType ) { DeclarationId base = instantiatedFrom->id(); if(instantiatedFromTemplate && instantiatedFromTemplate->specializedFrom().data()) base = instantiatedFromTemplate->specializedFrom().data()->id(); base.setSpecialization(globalTemplateArguments.indexed()); changedIdType->setDeclarationId(base); } } instantiatedDeclaration->setAbstractType( changedType ); } // } } if( instantiatedDeclaration ) { TemplateDeclaration* instantiatedTemplate = dynamic_cast(instantiatedDeclaration); if(instantiatedTemplate && templateArguments.templateParametersSize()) { //Since this is also called for normal members, this does not have to be the case. ///Change the identifier to reflect the used template-arguments KDevelop::Identifier id = instantiatedDeclaration->identifier(); updateIdentifierTemplateParameters( id, instantiatedDeclaration, source ); instantiatedDeclaration->setIdentifier(id); } ///Last step, because after this, the declaration will be potentially findable if(instantiatedTemplate && instantiatedFromTemplate) instantiatedTemplate->setInstantiatedFrom(instantiatedFromTemplate, templateArguments); ///@todo check for possible multi-threading issues when inserting visible declarations into anonymous contexts instantiatedDeclaration->setContext(parentContext, templateArguments.templateParametersSize() || parentContext->inSymbolTable()); } return contextCopy; } DeclarationId TemplateDeclaration::id(bool forceDirect) const { if(m_instantiatedFrom) { DeclarationId ret = m_instantiatedFrom->id(forceDirect); ret.setSpecialization(specialization()); return ret; }else{ return dynamic_cast(this)->Declaration::id(forceDirect); } } void TemplateDeclaration::deleteAllInstantiations() { if(m_instantiations.isEmpty() && m_defaultParameterInstantiations.isEmpty()) return; InstantiationsHash instantiations; { QMutexLocker l(&instantiationsMutex); instantiations = m_instantiations; m_defaultParameterInstantiations.clear(); m_instantiations.clear(); } foreach( TemplateDeclaration* decl, instantiations ) { Q_ASSERT(decl); decl->m_instantiatedFrom = 0; //Only delete real insantiations, not specializations //FIXME: before this checked for decl->isAnonymous //This was a problem because some instantiations are not anonymous, so they end up orphaned from their m_instantiatedFrom //If strange crashes start cropping up in template code, this needs more thought if(!decl->specializedFrom().isValid()) { Declaration* realDecl = dynamic_cast(decl); delete realDecl; } } } //TODO: QHash? typedef QMap TemplateParams; uint matchInstantiationParameters(const InstantiationInformation &info, const InstantiationInformation &matchAgainst, const TopDUContext *topCtxt, TemplateParams &requiredParams) { TemplateResolver resolver(topCtxt); uint matchQuality = 1; for(uint a = 0; a < info.templateParametersSize(); ++a) { uint parameterMatchQuality = resolver.matchTemplateParameterTypes(info.templateParameters()[a].abstractType(), matchAgainst.templateParameters()[a].abstractType(), requiredParams); if (!parameterMatchQuality) return 0; matchQuality += parameterMatchQuality; } return matchQuality; } //Returns the first (and barring bugs the only) imported template context. //A template decl imports one template context, which in turn imports the template context for the next scope, etc. DUContext* nextTemplateContext(const DUContext* importingContext, const TopDUContext *topCtxt) { foreach( const DUContext::Import &import, importingContext->importedParentContexts() ) { DUContext* c = import.context(topCtxt); if (c && c->type() == DUContext::Template) return c; } return 0; } uint TemplateDeclaration::matchInstantiation(IndexedInstantiationInformation indexedInfo, const TopDUContext* topCtxt, InstantiationInformation &instantiateWith, bool &instantiationRequired) const { DUContext *templateContext = this->templateParameterContext(); IndexedInstantiationInformation indexedSpecializedWith = this->specializedWith(); uint matchQuality = 1; instantiationRequired = false; while(indexedInfo.isValid() && templateContext) { if (templateContext->localDeclarations().size()) instantiationRequired = true; InstantiationInformation info = indexedInfo.information(); InstantiationInformation specializedWith = indexedSpecializedWith.information(); if (info.templateParametersSize() != specializedWith.templateParametersSize()) return 0; if (!info.templateParametersSize()) { indexedInfo = info.previousInstantiationInformation; indexedSpecializedWith = specializedWith.previousInstantiationInformation; continue; } TemplateParams requiredParameters; foreach(Declaration* parameterDecl, templateContext->localDeclarations()) requiredParameters[parameterDecl->identifier().identifier()] = AbstractType::Ptr(); uint match = matchInstantiationParameters(info, specializedWith, topCtxt, requiredParameters); if (!match) return 0; matchQuality += match; InstantiationInformation currentInstantiation; foreach( Declaration* decl, templateContext->localDeclarations() ) { if( decl->abstractType().cast() ) { IndexedString identifier = decl->identifier().identifier(); if (requiredParameters[identifier]) currentInstantiation.addTemplateParameter(requiredParameters[identifier]); //Take the type we have assigned. else return 0; } else currentInstantiation.addTemplateParameter(decl->abstractType()); //Use the specialized type } currentInstantiation.previousInstantiationInformation = instantiateWith.indexed(); instantiateWith = currentInstantiation; indexedSpecializedWith = specializedWith.previousInstantiationInformation; indexedInfo = info.previousInstantiationInformation; templateContext = nextTemplateContext(templateContext, topCtxt); } return matchQuality; } TemplateDeclaration* TemplateDeclaration::instantiateSpecialization(const InstantiationInformation& info, const TopDUContext* source) { TemplateDeclaration *specialization = 0; InstantiationInformation specializationInstantiationInfo; bool instantiationRequired; uint matchQuality = 0; FOREACH_FUNCTION(const IndexedDeclaration& decl, specializations) { //We only use visible specializations here if(source->recursiveImportIndices().contains(decl.indexedTopContext())) { TemplateDeclaration* curSpecialization = dynamic_cast(decl.data()); if(curSpecialization) { InstantiationInformation possibleInstantiation; uint match = curSpecialization->matchInstantiation(info.indexed(), source, possibleInstantiation, instantiationRequired); if(match > matchQuality) { matchQuality = match; specializationInstantiationInfo = possibleInstantiation; specialization = curSpecialization; } } } } if (specialization) { if (!instantiationRequired) return specialization; else return dynamic_cast(specialization->instantiate(specializationInstantiationInfo, source, true)); } return 0; } void applyDefaultParameters(const DUContext* templateContext, const TopDUContext* source, const DUContext* surroundingContext, InstantiationInformation* templateArguments) { Q_ASSERT(templateContext); Q_ASSERT(templateContext->type() == DUContext::Template); Q_ASSERT(source); Q_ASSERT(surroundingContext); const int totalParameters = templateContext->localDeclarations().count(); KDevVarLengthArray explicitParameters = templateArguments->templateParametersList(); if(totalParameters <= explicitParameters.size() //TODO: why is this required? && (explicitParameters.size() != 1 || explicitParameters.at(0).isValid())) { // nothing to do return; } KDevVarLengthArray appliedParameters; int currentArgument = 0; QVector typeOverloads; foreach(Declaration* decl, templateContext->localDeclarations()) { TemplateParameterDeclaration* templateDecl = dynamic_cast(decl); Q_ASSERT(templateDecl); //Only template-parameter declarations are allowed in template-contexts IndexedType type = decl->indexedType(); Q_ASSERT(type.isValid()); if( currentArgument < explicitParameters.size() && explicitParameters.at(currentArgument).isValid() ) { // use explicit parameter type = explicitParameters.at(currentArgument); Q_ASSERT(type); } else if(templateDecl->hasDefaultParameter()) { // Apply default-parameter Q_ASSERT(!templateDecl->defaultParameter().isEmpty()); DelayedType::Ptr delayed( new DelayedType() ); delayed->setIdentifier( IndexedTypeIdentifier(templateDecl->defaultParameter()) ); - type = resolveDelayedTypes( delayed.cast(), surroundingContext, source)->indexed(); + type = IndexedType(resolveDelayedTypes(delayed.cast(), surroundingContext, source)); } // else the parameter is missing //TODO: why is this neccessary? if(type.abstractType().cast()) { ++currentArgument; continue; } appliedParameters << type; if(type != decl->indexedType()) { // add type overload typeOverloads << new PushTypeOverload(decl->qualifiedIdentifier(), type); } ++currentArgument; } qDeleteAll(typeOverloads); templateArguments->templateParametersList() = appliedParameters; } Declaration* TemplateDeclaration::instantiate( const InstantiationInformation& _templateArguments, const TopDUContext* source, bool forceLocal ) { InstantiationInformation templateArguments(_templateArguments); /* if(dynamic_cast(dynamic_cast(this)->context())) { Q_ASSERT(templateArguments.previousInstantiationInformation == 0); }*/ if( m_instantiatedFrom && !forceLocal) return m_instantiatedFrom->instantiate( templateArguments, source ); if ( specializedFrom().data() && !forceLocal ) return dynamic_cast(specializedFrom().declaration())->instantiate(templateArguments, source); { QMutexLocker l(&instantiationsMutex); { DefaultParameterInstantiationHash::const_iterator it = m_defaultParameterInstantiations.constFind(templateArguments.indexed()); if(it != m_defaultParameterInstantiations.constEnd()) templateArguments = (*it).information(); } InstantiationsHash::const_iterator it; it = m_instantiations.constFind( templateArguments.indexed() ); if( it != m_instantiations.constEnd() ) { if(*it) { return dynamic_cast(*it); }else{ ///@todo What if the same thing is instantiated twice in parralel? Then this may trigger as well, altough one side should wait ///We are currently instantiating this declaration with the same template arguments. This would lead to an assertion. kDebug() << "tried to recursively instantiate" << dynamic_cast(this)->toString() << "with" << templateArguments.toString(); ///Maybe problematic, because the returned declaration is not in the correct context etc. return 0; } } } if(!source) return 0; if (m_instantiationDepth > 5) { kWarning() << "depth-limit reached while instantiating template declaration with" << _templateArguments.toString(); return 0; } PushValue depthCounter(m_instantiationDepth, m_instantiationDepth + 1); DUContext* surroundingContext = dynamic_cast(this)->context(); if(!surroundingContext) { kDebug() << "Declaration has no context:" << dynamic_cast(this)->qualifiedIdentifier().toString() << dynamic_cast(this)->toString(); return dynamic_cast(this); } Declaration* decl = dynamic_cast(this); Q_ASSERT(decl); Q_ASSERT(decl->topContext()); DUContext* templateContext = getTemplateContext(dynamic_cast(this), source); // kDebug() << decl->qualifiedIdentifier().toString() << "got template-context" << templateContext << templateArguments.toString(); if(!forceLocal) { if(templateContext) { applyDefaultParameters(templateContext, source, surroundingContext, &templateArguments); } ///Check whether there is type-aliases in the parameters that need to be resolved ///Generally, resolve all type-aliases that are part of a template-class, and keep the others { InstantiationInformation newTemplateArguments = templateArguments; newTemplateArguments.templateParametersList().clear(); struct UnAliasExchanger : public KDevelop::TypeExchanger { UnAliasExchanger(const TopDUContext* _source) : source(_source) { } const TopDUContext* source; virtual KDevelop::AbstractType::Ptr exchange(const KDevelop::AbstractType::Ptr& type) { KDevelop::AbstractType::Ptr check = type; KDevelop::TypeAliasType::Ptr alias = type.cast(); if(alias) { //We exchange type-aliases with their real types only of the type-alias is in a template //class. In that case, we cannot be sure that it's not used for meta-programming. //All other aliases can be kept, for user-friendliness, even if it's not 100% C++ correct Declaration* decl = alias->declaration(source); if(!decl || dynamic_cast(decl)) { return exchange(alias->type()); } } if(check) check->exchangeTypes(this); return check; } }; UnAliasExchanger exchanger(source); for(uint a = 0; a < templateArguments.templateParametersSize(); ++a) - newTemplateArguments.templateParametersList().append(exchanger.exchange(templateArguments.templateParameters()[a].abstractType())->indexed()); + newTemplateArguments.templateParametersList().append(IndexedType(exchanger.exchange(templateArguments.templateParameters()[a].abstractType()))); templateArguments = newTemplateArguments; } if(!(templateArguments == _templateArguments)) { QMutexLocker l(&instantiationsMutex); m_defaultParameterInstantiations[_templateArguments.indexed()] = templateArguments.indexed(); } } { //Now we have the final template-parameters. Once again check whether we have already instantiated this, //and if not, reserve the instantiation so we cannot crash later on ///@todo When the same declaration is instantuated multiple times, this sucks because one is returned invalid QMutexLocker l(&instantiationsMutex); InstantiationsHash::const_iterator it; it = m_instantiations.constFind( templateArguments.indexed() ); if( it != m_instantiations.constEnd() ) { if(*it) { return dynamic_cast(*it); }else{ //Problem return dynamic_cast(this); } } ///@warning Once we've called reserveInstantiation, we have to be 100% sure that we actually create the instantiation reserveInstantiation(templateArguments.indexed()); } TemplateDeclaration *instantiatedSpecialization = instantiateSpecialization(templateArguments, source); //We have reserved the instantiation, so it must have stayed untouched Q_ASSERT(m_instantiations[templateArguments.indexed()] == 0); if(instantiatedSpecialization) { //A specialization has been chosen and instantiated. Just register it here, and return it. instantiatedSpecialization->setInstantiatedFrom(this, templateArguments); return dynamic_cast(instantiatedSpecialization); } { //Check whether the instantiation also instantiates the parent context, and if it does, replace surroundingContext with the instantiated version CppDUContext* parent = dynamic_cast*>(surroundingContext); if(parent && templateArguments.previousInstantiationInformation.index() && templateArguments.previousInstantiationInformation.index() != parent->instantiatedWith().index()) { DUContext* surroundingCandidate = parent->instantiate(IndexedInstantiationInformation(templateArguments.previousInstantiationInformation).information(), source); if(surroundingCandidate) surroundingContext = surroundingCandidate; else kDebug() << "could not instantiate surrounding context for" << dynamic_cast(this)->qualifiedIdentifier().toString(); } } Declaration* clone = decl->clone(); Q_ASSERT(clone); #ifdef QT_DEBUG TemplateDeclaration* cloneTemplateDecl = dynamic_cast(clone); Q_ASSERT(cloneTemplateDecl); #endif ///Now eventually create the virtual contexts, and fill new information into the declaration instantiateDeclarationAndContext( surroundingContext, source, decl->internalContext(), templateArguments, clone, decl ); // cloneTemplateDecl->setInstantiatedFrom(this); Q_ASSERT(clone->topContext()); return clone; } AbstractType::Ptr resolveDelayedTypes( AbstractType::Ptr type, const KDevelop::DUContext* context, const KDevelop::TopDUContext* source, KDevelop::DUContext::SearchFlags searchFlags ) { if( !type ) return type; ///First, find out if delayed types are involved(Function return-type, function-arguments, reference/pointer target, base-class) DelayedTypeSearcher search; type->accept(&search); DelayedType::Ptr delayedType = type.cast(); if( search.found || delayedType ) { ///Delayed types were found. We must copy the whole type, and replace the delayed types. DelayedTypeResolver resolver(context, source, searchFlags); AbstractType::Ptr typeCopy; if( delayedType ) ///The type itself is a delayed type, resolve it typeCopy = resolver.exchange( type ); else { ///Resolve involved delayed types, now hopefully we know the template-parameters typeCopy = AbstractType::Ptr( type->clone() ); DelayedTypeSearcher testSearch; typeCopy->accept(&testSearch); Q_ASSERT(testSearch.found); typeCopy->exchangeTypes( &resolver ); } return typeCopy; } else { return type; } } IndexedInstantiationInformation TemplateDeclaration::instantiatedWith() const { return m_instantiatedWith; } TemplateDeclaration::InstantiationsHash TemplateDeclaration::instantiations() const { QMutexLocker l(&instantiationsMutex); return m_instantiations; } template<> Declaration* SpecialTemplateDeclaration::resolve(const TopDUContext* topContext) const { if( instantiatedFrom() ) { SpecialTemplateDeclaration* instantiatedFrom = dynamic_cast*>(this->instantiatedFrom()); if( instantiatedFrom ) { Declaration* baseResolved = instantiatedFrom->resolve(topContext); TemplateDeclaration* baseTemplate = dynamic_cast(baseResolved); if( baseResolved && baseTemplate ) { return baseTemplate->instantiate(instantiatedWith().information(), topContext ? topContext : this->topContext()); }else{ //Forward-declaration was not resolved return 0; } }else{ //TODO: report this in the problem reporter? kWarning(9007) << "Problem in template forward-declaration"; return 0; } }else{ return ForwardDeclaration::resolve(topContext); } } } diff --git a/languages/cpp/cppduchain/typeconversion.cpp b/languages/cpp/cppduchain/typeconversion.cpp index 9eeb127c3f..a9447170e2 100644 --- a/languages/cpp/cppduchain/typeconversion.cpp +++ b/languages/cpp/cppduchain/typeconversion.cpp @@ -1,623 +1,623 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "typeconversion.h" #include "cppduchain/typeutils.h" #include "cppduchain/cpptypes.h" #include "overloadresolution.h" #include #include #include #include #include #include #include #include using namespace Cpp; using namespace KDevelop; using namespace TypeUtils; #define ifDebug(x) // #define ifDebug(x) x struct ImplicitConversionParams { IndexedType from, to; bool fromLValue, noUserDefinedConversion; bool operator==(const ImplicitConversionParams& rhs) const { return from == rhs.from && to == rhs.to && fromLValue == rhs.fromLValue && noUserDefinedConversion == rhs.noUserDefinedConversion; } }; uint qHash(const ImplicitConversionParams& params) { return (params.from.hash() * 36109 + params.to.hash()) * (params.fromLValue ? 111 : 53) * (params.noUserDefinedConversion ? 317293 : 1); } namespace Cpp { class TypeConversionCache { public: QHash m_implicitConversionResults; /* QHash, uint> m_standardConversionResults; QHash, uint> m_userDefinedConversionResults;*/ // QHash, bool> m_isPublicBaseCache; }; } QHash typeConversionCaches; QMutex typeConversionCacheMutex; void TypeConversion::startCache() { QMutexLocker lock(&typeConversionCacheMutex); if(!typeConversionCaches.contains(QThread::currentThreadId())) typeConversionCaches[QThread::currentThreadId()] = new TypeConversionCache; } void TypeConversion::stopCache() { QMutexLocker lock(&typeConversionCacheMutex); if(typeConversionCaches.contains(QThread::currentThreadId())) { delete typeConversionCaches[QThread::currentThreadId()]; typeConversionCaches.remove(QThread::currentThreadId()); } } TypeConversion::TypeConversion(const TopDUContext* topContext) : m_baseConversionLevels(0) , m_topContext(topContext) { QMutexLocker lock(&typeConversionCacheMutex); QHash::iterator it = typeConversionCaches.find(QThread::currentThreadId()); if(it != typeConversionCaches.end()) m_cache = *it; else m_cache = 0; } TypeConversion::~TypeConversion() { } /** * All information taken from iso c++ draft * * Standard-conversion-sequence: * - zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, function-to-pointer conversion * - zero or one conversion from the following set: integral promotions, floating point promotions, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions. * * Standard-conversion-sequence will be applied to expression when it needs to be converted to another type. * * Note: lvalue = reference to existing object * rvalue = copied object * * When is an expression implicitly converted? : * - When used as operands of operators. * - When used in a condition statement(destination type is bool) * - When used in the expression of a switch statement * - When used as the source expression for an initialization(includes argument in function-call and return-statement) * * User-defined conversions: * - Constructors and conversion-functions. * - At most one such conversion is applied when doing implicit type-conversion * */ /** * An implicit conversion-sequence is one of the following: * - a standard conversion sequence * - a user-defined conversion sequence * - an ellipsis conversion sequence * * */ uint TypeConversion::implicitConversion( const IndexedType& _from, const IndexedType& _to, bool fromLValue, bool noUserDefinedConversion ) { m_baseConversionLevels = 0; int conv = 0; ImplicitConversionParams params; params.from = _from; params.to = _to; params.fromLValue = fromLValue; params.noUserDefinedConversion = noUserDefinedConversion; if(m_cache) { QHash::const_iterator it = m_cache->m_implicitConversionResults.constFind(params); if(it != m_cache->m_implicitConversionResults.constEnd()) return *it; } AbstractType::Ptr to = unAliasedType(_to.abstractType()); AbstractType::Ptr from = unAliasedType(_from.abstractType()); // we need the 'real' types for the below checks to work properly, esp. // in regard to constness // TODO: is this maybe something that should *always* be done in removeConstants? AbstractType::Ptr nonConstFrom = removeConstants(from, m_topContext); if (nonConstFrom && nonConstFrom != from) { from = nonConstFrom; from->setModifiers(from->modifiers() | AbstractType::ConstModifier); } ifDebug(qDebug() << "Checking conversion from " << (from ? from->toString() : "") << " to " << (to ? to->toString() : "");); if( !from || !to ) { problem( from, to, "one type is invalid" ); goto ready; }else{ ReferenceType::Ptr fromReference = from.cast(); if( fromReference ) fromLValue = !fromReference->isRValue(); ///iso c++ draft 13.3.3.1.4 reference-binding, modeled roughly ReferenceType::Ptr toReference = to.cast(); if( toReference ) { AbstractType::Ptr realFrom = realType(from, m_topContext); AbstractType::Ptr realTo = realType(to, m_topContext); if(!realFrom || !realTo) { problem( from, to, "one type is invalid" ); goto ready; } if( fromLValue && ((realTo->modifiers() & AbstractType::ConstModifier) || (realTo->modifiers() & AbstractType::ConstModifier) == isConstant(from)) ) { ///Since from is an lvalue, and the constant-specification matches, we can maybe directly create a reference //Either identity-conversion: if( identityConversion( realFrom, realTo ) ) { conv = ExactMatch + 2*ConversionRankOffset; // see c++ draft 13.3.3.2 - prefer equal cv-qualifications if ( isConstant(realFrom) == isConstant(realTo) ) { conv += 1; } goto ready; } //Or realType(toReference) is a public base-class of realType(fromReference) CppClassType::Ptr fromClass = realFrom.cast(); CppClassType::Ptr toClass = realTo.cast(); if( fromClass && toClass && isPublicBaseClass( fromClass, toClass, m_topContext, &m_baseConversionLevels ) ) { conv = ExactMatch + 2*ConversionRankOffset; goto ready; } } //We cannot directly create a reference, but maybe there is a user-defined conversion that creates a compatible reference, as in iso c++ 13.3.3.1.4.1 if( !noUserDefinedConversion ) { if( int rank = userDefinedConversion( from, to, fromLValue, true ) ) { conv = rank + ConversionRankOffset; goto ready; } } if( realTo->modifiers() & AbstractType::ConstModifier ) { //For constant references, the compiler can create a temporary object holding the converted value. So just forget whether the types are references. - conv = implicitConversion( realType(from, m_topContext)->indexed(), realType(to, m_topContext)->indexed(), fromLValue, noUserDefinedConversion ); + conv = implicitConversion( IndexedType(realType(from, m_topContext)), IndexedType(realType(to, m_topContext)), fromLValue, noUserDefinedConversion ); goto ready; } } { int tempConv = 0; //This is very simplified, see iso c++ draft 13.3.3.1 if( (tempConv = standardConversion(from,to)) ) { tempConv += 2*ConversionRankOffset; if( tempConv > conv ) conv = tempConv; } if( !noUserDefinedConversion ) { if( (tempConv = userDefinedConversion(from, to, fromLValue)) ) { tempConv += ConversionRankOffset; if( tempConv > conv ) conv = tempConv; } } if( (tempConv = ellipsisConversion(from, to)) && tempConv > conv ) conv = tempConv; } } ready: if(m_cache) m_cache->m_implicitConversionResults.insert(params, conv); return conv; } int TypeConversion::baseConversionLevels() const { return m_baseConversionLevels; } ///Helper for standardConversion(..) that makes sure that when one category is taken out of the possible ones, the earlier are taken out too, because categories must be checked in order. int removeCategories( int categories, ConversionCategories remove ) { for( int a = 1; a <= remove; a*=2 ) { categories &= ~a; } return categories; } ///if myRank is better than rank, rank will be set to myRank void maximizeRank( ConversionRank& rank, ConversionRank myRank ) { if( myRank > rank ) rank = myRank; } ///Returns the worse of the both given ranks ConversionRank worseRank( ConversionRank rank1, ConversionRank rank2 ) { return rank1 > rank2 ? rank2 : rank1; } ConversionRank TypeConversion::pointerConversion( PointerType::Ptr from, PointerType::Ptr to ) { //We can convert non-const -> const, but not const -> non-const // if(to->modifiers() & AbstractType::ConstModifier || !(from->modifiers()& AbstractType::ConstModifier)) { if(!from || !to) return NoMatch; AbstractType::Ptr nextFrom = unAliasedType(from->baseType()); AbstractType::Ptr nextTo = unAliasedType(to->baseType()); if(!nextTo || !nextFrom) return NoMatch; if((nextFrom->modifiers() & AbstractType::ConstModifier) && !(nextTo->modifiers() & AbstractType::ConstModifier)) return NoMatch; //Cannot convert const -> non-const PointerType::Ptr pointerFrom = nextFrom.cast(); PointerType::Ptr pointerTo = nextTo.cast(); if(pointerFrom && pointerTo) return pointerConversion(pointerFrom, pointerTo); CppClassType::Ptr fromClass = nextFrom.cast(); CppClassType::Ptr toClass = nextTo.cast(); if( toClass && fromClass ) if(toClass->modifiers() & AbstractType::ConstModifier || !(fromClass->modifiers()& AbstractType::ConstModifier)) if( isPublicBaseClass( fromClass, toClass, m_topContext, &m_baseConversionLevels ) ) return ((toClass->modifiers() & AbstractType::ConstModifier) != (fromClass->modifiers() & AbstractType::ConstModifier)) ? Conversion : ExactMatch; bool changed = false; //Change the constness matches, so they are equal if compatible if(nextTo->modifiers() & AbstractType::ConstModifier) { nextFrom->setModifiers(nextFrom->modifiers() | AbstractType::ConstModifier); changed = true; } if(identityConversion(nextFrom, nextTo)) return changed ? Conversion : ExactMatch; // } return NoMatch; } /** * * **/ ConversionRank TypeConversion::standardConversion( AbstractType::Ptr from, AbstractType::Ptr to, int categories, int maxCategories ) { /** Lowest conversion-rank of all sub-conversions is returned * See iso c++ draft 13.3.3.1.1 * * Conversions from up to 3 different categories are allowed * * Table about category and rank: * * Conversion Category Rank iso c++ clause * ----------------------------------------------------------------------------------------------------- * No conversion Identity Exact Match * Lvalue-to-rvalue conv. Lvalue Transformation Exact Match 4.1 * Array-to-pointer conv. Lvalue Transformation Exact Match 4.2 * Function-to-pointer conv. Lvalue Transformation Exact Match 4.3 * Qualification conversion Qualification Adjustment Exact Match 4.4 * Integral promotions Promotion Promotion 4.5 * Floating point promotion Promotion Promotion 4.6 * Integral conversions Conversion Conversion 4.7 * Floating point conversions Conversion Conversion 4.8 * Floating-integral conversions Conversion Conversion 4.9 * Pointer conversions Conversion Conversion 4.10 * Pointer to member conversions Conversion Conversion 4.11 * Boolean conversions Conversion Conversion 4.12 * * A standard-conversion may consist of up to 3 conversions from different categories * * * This function achieves the rules recursively. Performance-wise that may not be perfect, because sometimes many different paths can are followed. **/ from = unAliasedType(from); to = unAliasedType(to); if( (categories & IdentityCategory) && identityConversion( from, to ) ) return ExactMatch; if(!from || !to) return NoMatch; ConversionRank bestRank = NoMatch; ///Try lvalue-transformation category if( (categories & LValueTransformationCategory) ) { if( isReferenceType(from) ) { ///Transform lvalue to rvalue. Iso c++ draft 4.1 modeled roughly - AbstractType::Ptr fromNonConstant = realType(from, m_topContext)->indexed().abstractType(); + AbstractType::Ptr fromNonConstant = IndexedType(realType(from, m_topContext)).abstractType(); //When copying, the type becomes non-constant if(fromNonConstant && fromNonConstant->modifiers() & AbstractType::ConstModifier) fromNonConstant->setModifiers(fromNonConstant->modifiers() & ~(AbstractType::ConstModifier)); ConversionRank ret = standardConversion( fromNonConstant, to, removeCategories(categories,LValueTransformationCategory), maxCategories-1 ); maximizeRank( bestRank, ret ); }else if( ArrayType::Ptr array = realType(from, m_topContext).cast() ) { //realType(from) is used here so reference-to-array can be transformed to a pointer. This does not exactly follow the standard I think, check that. ///Transform array to pointer. Iso c++ draft 4.2 modeled roughly. PointerType::Ptr p( new PointerType() ); p->setBaseType(array->elementType()); ConversionRank rank = standardConversion( p.cast(), to, removeCategories(categories,LValueTransformationCategory), maxCategories-1 ); maximizeRank( bestRank, worseRank(rank, ExactMatch ) ); } else if( FunctionType::Ptr function = realType(from, m_topContext).cast() ) { ///Transform lvalue-function. Iso c++ draft 4.3 //This code is nearly the same as the above array-to-pointer conversion. Maybe it should be merged. PointerType::Ptr p( new PointerType() ); p->setBaseType( function.cast() ); ConversionRank rank = standardConversion( p.cast(), to, removeCategories(categories,LValueTransformationCategory), maxCategories-1 ); maximizeRank( bestRank, worseRank(rank, ExactMatch ) ); }else if(from->modifiers() & AbstractType::ConstModifier) { ///We can transform a constant lvalue to a non-constant rvalue - AbstractType::Ptr fromNonConstant = from->indexed().abstractType(); + AbstractType::Ptr fromNonConstant = IndexedType(from).abstractType(); fromNonConstant->setModifiers(fromNonConstant->modifiers() & ~(AbstractType::ConstModifier)); ConversionRank ret = standardConversion( fromNonConstant, to, removeCategories(categories,LValueTransformationCategory), maxCategories-1 ); maximizeRank( bestRank, ret ); } } // if( categories & QualificationAdjustmentCategory ) { // PointerType::Ptr pnt = from.cast(); // // ///@todo iso c++ 4.4.2 etc: pointer to member // } EnumerationType::Ptr toEnumeration = to.cast(); if(toEnumeration) { //Eventually convert enumerator -> enumeration if the enumeration equals EnumeratorType::Ptr fromEnumerator = from.cast(); if(fromEnumerator) { Declaration* enumeratorDecl = fromEnumerator->declaration(m_topContext); Declaration* enumerationDecl = toEnumeration->declaration(m_topContext); if(enumeratorDecl && enumerationDecl && enumeratorDecl->context()->owner() == enumerationDecl) return ExactMatch; //Converting an enumeration value into its own enumerator type, perfect match. } ///iso c++ 7.2.9: No conversion or promotion to enumerator types is possible return bestRank; } if( categories & PromotionCategory ) { IntegralType::Ptr integral = from.cast(); if( integral ) { ///Integral promotions, iso c++ 4.5 if( integerConversionRank(integral) < unsignedIntConversionRank && integral->dataType() != IntegralType::TypeBoolean && integral->dataType() != IntegralType::TypeChar16_t && integral->dataType() != IntegralType::TypeChar32_t && integral->dataType() != IntegralType::TypeWchar_t && integral->dataType() != IntegralType::TypeVoid ) { ///iso c++ 4.5.1 ///@todo re-create a mini repository for fast lookup of such integral types, so we don't have to do allocations here AbstractType::Ptr newFrom( new IntegralType(IntegralType::TypeInt) ); newFrom->setModifiers((integral->modifiers() & AbstractType::UnsignedModifier) ? AbstractType::UnsignedModifier : AbstractType::NoModifiers); ConversionRank rank = standardConversion( newFrom, to, removeCategories(categories,PromotionCategory), maxCategories-1 ); maximizeRank( bestRank, worseRank(rank, Promotion ) ); } ///Floating point promotion, iso c++ 4.6 if( integral->dataType() == IntegralType::TypeDouble ) { AbstractType::Ptr newFrom( new IntegralType(IntegralType::TypeDouble) ); ConversionRank rank = standardConversion( newFrom, to, removeCategories(categories,PromotionCategory), maxCategories-1 ); maximizeRank( bestRank, worseRank(rank, Promotion ) ); } } } if( categories & ConversionCategory ) { IntegralType::Ptr fromIntegral = from.cast(); EnumerationType::Ptr fromEnumeration = fromIntegral.cast(); EnumeratorType::Ptr fromEnumerator = fromIntegral.cast(); IntegralType::Ptr toIntegral = to.cast(); if( fromIntegral && toIntegral ) { ///iso c++ 4.7 integral conversion: we can convert from any integer type to any other integer type, and from enumeration-type to integer-type if( (fromEnumeration || fromEnumerator || isIntegerType(fromIntegral)) && isIntegerType(toIntegral) ) { maximizeRank( bestRank, Conversion ); } ///iso c++ 4.8 floating point conversion: any floating-point to any other floating-point if( isFloatingPointType(fromIntegral) && isFloatingPointType(toIntegral) ) { maximizeRank( bestRank, Conversion ); } ///iso c++ 4.9 floating-integral conversion: floating point can be converted to integral, enumeration and integral can be converted to floating point if( ( ( fromEnumeration || fromEnumerator || isIntegerType(fromIntegral) ) && isFloatingPointType(toIntegral) ) || ( isFloatingPointType(fromIntegral) && isIntegerType(toIntegral) ) ) { maximizeRank( bestRank, Conversion ); } } ///iso c++ 4.10 pointer conversion: null-type con be converted to pointer PointerType::Ptr fromPointer = from.cast(); PointerType::Ptr toPointer = to.cast(); if( isNullType(from) && toPointer ) { maximizeRank( bestRank, Conversion ); } ///Pointer can be converted to void* if( fromPointer && toPointer && isVoidType(toPointer->baseType()) ) { maximizeRank( bestRank, Conversion ); } ///iso c++ 4.10.3 - class-pointer conversion if( fromPointer && toPointer /*&& fromPointer->cv() == toPointer->cv()*/ ) maximizeRank( bestRank, pointerConversion(fromPointer, toPointer) ); ///@todo pointer-to-member conversion ///iso c++ 4.12 Boolean conversions if( toIntegral && toIntegral->dataType() == IntegralType::TypeBoolean ) { //We are converting to a boolean value if( fromPointer || fromEnumeration || fromEnumerator || (fromIntegral && (isIntegerType(fromIntegral) || isFloatingPointType(fromIntegral))) ) { maximizeRank( bestRank, Conversion ); } } } return bestRank; } bool TypeConversion::identityConversion( AbstractType::Ptr from, AbstractType::Ptr to ) { from = TypeUtils::unAliasedType(from); to = TypeUtils::unAliasedType(to); if( !from && !to ) return true; else if( !from || !to ) return false; //ConstantIntegralType::equals does not return true on equals in this case, but the type is compatible. if(from.cast() && typeid(*to) == typeid(IntegralType)) return true; return from->equals(to.unsafeData()); } void TypeConversion::problem( AbstractType::Ptr from, AbstractType::Ptr to, const QString& desc ) { Q_UNUSED(from) Q_UNUSED(to) Q_UNUSED(desc) } ConversionRank TypeConversion::userDefinedConversion( AbstractType::Ptr from, AbstractType::Ptr to, bool fromLValue, bool secondConversionIsIdentity ) { /** * Two possible cases: * - from is a class, that has a conversion-function * - to is a class that has a converting(non-explicit) matching constructor **/ ConversionRank bestRank = NoMatch; AbstractType::Ptr realFrom( realType(from, m_topContext) ); CppClassType::Ptr fromClass = realFrom.cast(); { ///Try user-defined conversion using a conversion-function, iso c++ 12.3 if( fromClass ) { ///Search for a conversion-function that has a compatible output QHash conversionFunctions; getMemberFunctions(fromClass, m_topContext, conversionFunctions, "operator{...cast...}", fromClass->modifiers() & AbstractType::ConstModifier); for( QHash::const_iterator it = conversionFunctions.constBegin(); it != conversionFunctions.constEnd(); ++it ) { if(isAccessible(it.value())) { AbstractType::Ptr convertedType( it.key()->returnType() ); ConversionRank rank = standardConversion( convertedType, to ); if( rank != NoMatch && (!secondConversionIsIdentity || rank == ExactMatch) ) { //We have found a matching conversion-function if( identityConversion(realType(convertedType, m_topContext), to) ) maximizeRank( bestRank, ExactMatch ); else maximizeRank( bestRank, Conversion ); } } } } } AbstractType::Ptr realTo( realType(to, m_topContext) ); { ///Try conversion using constructor CppClassType::Ptr toClass = realTo.cast(); //@todo think whether the realType(..) is ok if( toClass && toClass->declaration(m_topContext) ) { if( fromClass ) { if( isPublicBaseClass(fromClass, toClass, m_topContext, &m_baseConversionLevels ) ) { ///@todo check whether this is correct //There is a default-constructor in toClass that initializes from const toClass&, which fromClass can be converted to maximizeRank( bestRank, Conversion ); } } DUContextPointer ptr(toClass->declaration(m_topContext)->logicalInternalContext(m_topContext)); OverloadResolver resolver( ptr, TopDUContextPointer( const_cast(m_topContext) ) ); Declaration* function = resolver.resolveConstructor( OverloadResolver::Parameter( from, fromLValue ), true, true ); if( function && isAccessible(dynamic_cast(function)) ) { //We've successfully located an overloaded constructor that accepts the argument if( to == realFrom ) maximizeRank( bestRank, ExactMatch ); else maximizeRank( bestRank, Conversion ); } } } return bestRank; } bool TypeConversion::isAccessible(const ClassMemberDeclaration* decl) { ///@todo Use Cpp::isAccessible here if(!decl) return false; return decl->accessPolicy() == Declaration::Public; } ConversionRank TypeConversion::ellipsisConversion( AbstractType::Ptr from, AbstractType::Ptr to ) { Q_UNUSED(from); if (TypeUtils::isVarArgs(to)) { return ExactMatch; } return NoMatch; } diff --git a/languages/cpp/cppduchain/typeutils.cpp b/languages/cpp/cppduchain/typeutils.cpp index bee1c2e85f..02be6dae6a 100644 --- a/languages/cpp/cppduchain/typeutils.cpp +++ b/languages/cpp/cppduchain/typeutils.cpp @@ -1,331 +1,331 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "typeutils.h" #include "cppduchain/cpptypes.h" #include #include #include #include #include "typeconversion.h" #include namespace TypeUtils { using namespace KDevelop; AbstractType::Ptr realType(const AbstractType::Ptr& _base, const TopDUContext* /*topContext*/) { AbstractType::Ptr base = _base; ReferenceType::Ptr ref = base.cast(); TypeAliasType::Ptr alias = base.cast(); while( ref || alias ) { uint hadModifiers = base->modifiers(); if(ref) { base = ref->baseType(); }else{ base = alias->type(); } if(base) base->setModifiers(base->modifiers() | hadModifiers); ref = base.cast(); alias = base.cast(); } return base; } AbstractType::Ptr realTypeKeepAliases(const AbstractType::Ptr& _base) { AbstractType::Ptr base = _base; ReferenceType::Ptr ref = base.cast(); while( ref ) { uint hadModifiers = base->modifiers(); base = ref->baseType(); if(base) base->setModifiers(base->modifiers() | hadModifiers); ref = base.cast(); } return base; } bool isPointerType(const AbstractType::Ptr& type) { return realType(type, 0).cast(); } bool isReferenceType(const AbstractType::Ptr& type) { return type.cast(); } bool isConstant( const AbstractType::Ptr& t ) { return t && t->modifiers() & AbstractType::ConstModifier; } bool isNullType( const AbstractType::Ptr& t ) { ConstantIntegralType::Ptr integral = t.cast(); if( integral && integral->dataType() == IntegralType::TypeInt && integral->value() == 0 ) return true; else return false; } const int unsignedIntConversionRank = 4; int integerConversionRank( const IntegralType::Ptr& type ) { /** * Ranks: * 1 - bool * 2 - 1 byte, char * 2 - 2 byte, char16 * 3 - 2 byte, short int, wchar_t, unsigned short int * 4 - 4 byte, int, unsigned int, char32_t * 5 - 4 byte, long int * 6 - 4 byte, long long int **/ switch( type->dataType() ) { case IntegralType::TypeBoolean: return 1; break; case IntegralType::TypeChar: case IntegralType::TypeChar16_t: return 2; break; case IntegralType::TypeWchar_t: return 3; break; case IntegralType::TypeChar32_t: return 4; case IntegralType::TypeInt: if( type->modifiers() & AbstractType::ShortModifier ) return 3; if( type->modifiers() & AbstractType::LongModifier ) return 5; if( type->modifiers() & AbstractType::LongLongModifier ) return 6; return 4; //default-integer //All other types have no integer-conversion-rank default: return 0; }; } bool isIntegerType( const IntegralType::Ptr& type ) { return integerConversionRank(type) != 0; //integerConversionRank returns 0 for non-integer types } bool isFloatingPointType( const IntegralType::Ptr& type ) { return type->dataType() == IntegralType::TypeFloat || type->dataType() == IntegralType::TypeDouble; } bool isVoidType( const AbstractType::Ptr& type ) { IntegralType::Ptr integral = type.cast(); if( !integral ) return false; return integral->dataType() == IntegralType::TypeVoid; } ///Returns whether base is a base-class of c void getMemberFunctions(const CppClassType::Ptr& klass, const TopDUContext* topContext, QHash& functions, const QString& functionName, bool mustBeConstant,int recursionCount) { static const int MaxRecursionCount = 50; Declaration* klassDecl = klass->declaration(topContext); ClassDeclaration* cppClassDecl = dynamic_cast(klassDecl); DUContext* context = klassDecl ? klassDecl->internalContext() : 0; int functionCount = functions.size(); if( context ) { QList declarations = context->findLocalDeclarations(Identifier(functionName), CursorInRevision::invalid(), topContext); for( QList::iterator it = declarations.begin(); it != declarations.end(); ++it ) { KDevelop::FunctionType::Ptr function = (*it)->abstractType().cast(); ClassFunctionDeclaration* functionDeclaration = dynamic_cast( *it ); if( function && functionDeclaration ) { if( !functions.contains(function) && (!mustBeConstant || (function->modifiers() & AbstractType::ConstModifier)) ) { functions[function] = functionDeclaration; } } } } ///One overloaded function of a specific name overloads all inherited with the same name. Think about it in the context where getMemberFunctions is used. if( functionCount != functions.size() ) return; if(cppClassDecl) { //equivalent to using the imported parent-contexts FOREACH_FUNCTION(const KDevelop::BaseClassInstance& base, cppClassDecl->baseClasses) { if( base.access != KDevelop::Declaration::Private ) { //we need const-cast here because the constant list makes also the pointers constant, which is not intended CppClassType::Ptr baseClass = base.baseClass.type(); if(baseClass && !baseClass->equals(klass.constData()) && recursionCount < MaxRecursionCount) getMemberFunctions(baseClass, topContext, functions, functionName, mustBeConstant, recursionCount+1); } } } } void getMemberFunctions(const CppClassType::Ptr& klass, const TopDUContext* topContext, QList& functions, const QString& functionName, bool mustBeConstant) { QHash tempFunctions; getMemberFunctions( klass, topContext, tempFunctions, functionName, mustBeConstant ); for( QHash::const_iterator it = tempFunctions.constBegin(); it != tempFunctions.constEnd(); ++it ) functions << (*it); } QList getConstructors(const CppClassType::Ptr& klass, const TopDUContext* topContext) { QList functions; Declaration* klassDecl = klass->declaration(topContext); DUContext* context = klassDecl ? klassDecl->internalContext() : 0; if( !context || !context->owner() || !context->owner() ) { // kDebug(9007) << "Tried to get constructors of a class without context"; return functions; } Identifier id(context->owner()->identifier()); id.clearTemplateIdentifiers(); const QList& declarations = context->findLocalDeclarations(id, CursorInRevision::invalid(), topContext, AbstractType::Ptr(), DUContext::OnlyFunctions); foreach( Declaration* dec, declarations ) { ClassFunctionDeclaration* functionDeclaration = dynamic_cast( dec ); if( functionDeclaration && functionDeclaration->isConstructor() ) { functions << dec; } } return functions; } bool isPublicBaseClass( const CppClassType::Ptr& c, const CppClassType::Ptr& base, const KDevelop::TopDUContext* topContext, int* baseConversionLevels ) { if (!c || !base) { return false; } ClassDeclaration* fromDecl = dynamic_cast(c->declaration(topContext)); ClassDeclaration* toDecl = dynamic_cast(base->declaration(topContext)); if(fromDecl && toDecl) return fromDecl->isPublicBaseClass(toDecl, topContext, baseConversionLevels); else return false; } KDevelop::AbstractType::Ptr matchingClassPointer(const KDevelop::AbstractType::Ptr& matchTo, const KDevelop::AbstractType::Ptr& actual, const KDevelop::TopDUContext* topContext) { Cpp::TypeConversion conversion(topContext); StructureType::Ptr actualStructure = realType(actual, topContext).cast(); if(actualStructure) { DUContext* internal = actualStructure->internalContext(topContext); if(internal) { foreach(Declaration* decl, internal->findDeclarations(Cpp::castIdentifier().identifier(), CursorInRevision::invalid(), topContext, (DUContext::SearchFlags)(DUContext::DontSearchInParent | DUContext::NoFiltering))) { FunctionType::Ptr funType = decl->type(); if(funType && funType->returnType()) { - if(conversion.implicitConversion(funType->returnType()->indexed(), matchTo->indexed(), true)) { + if(conversion.implicitConversion(IndexedType(funType->returnType()), IndexedType(matchTo), true)) { return funType->returnType(); } } } } } return actual; } Declaration* getDeclaration( const AbstractType::Ptr& type, TopDUContext* top ) { if( !type) return 0; const IdentifiedType* idType = dynamic_cast(type.unsafeData()); if( idType ) { return idType->declaration(top); } else { return 0; } } AbstractType::Ptr decreasePointerDepth(AbstractType::Ptr type, TopDUContext* top, bool useOperator) { type = realType(type, top); if( PointerType::Ptr pt = type.cast() ) { //Dereference return pt->baseType(); }else if( ArrayType::Ptr pt = type.cast() ) { return pt->elementType(); }else{ if(useOperator) { Declaration* decl = getDeclaration(type, top); if(decl && decl->internalContext()) { QList decls = decl->internalContext()->findDeclarations(Identifier("operator*"), CursorInRevision::invalid(), top, DUContext::DontSearchInParent); if(!decls.isEmpty()) { FunctionType::Ptr fun = decls.first()->type(); if(fun) return fun->returnType(); } } } } return AbstractType::Ptr(); } AbstractType::Ptr increasePointerDepth(AbstractType::Ptr type) { AbstractType::Ptr oldType = realType(type, 0); ///Dereference references PointerType::Ptr newPointer(new PointerType()); newPointer->setBaseType( oldType ); return newPointer.cast(); } IndexedType removeConstModifier(const IndexedType& indexedType) { AbstractType::Ptr type = indexedType.abstractType(); removeConstModifier(type); - return type->indexed(); + return IndexedType(type); } void removeConstModifier(AbstractType::Ptr& type) { if(type && type->modifiers() & AbstractType::ConstModifier) { type->setModifiers(type->modifiers() & (~AbstractType::ConstModifier)); } } AbstractType::Ptr removeConstants(AbstractType::Ptr type, const TopDUContext* source) { ENSURE_CHAIN_READ_LOCKED if(TypePtr< EnumeratorType > enumerator = type.cast()) { Declaration* decl = enumerator->declaration(source); if(decl && decl->context()->owner()) { return decl->context()->owner()->abstractType(); } }else if(ConstantIntegralType::Ptr integral = type.cast()) return AbstractType::Ptr(new IntegralType(*integral)); return type; } bool isVarArgs(const AbstractType::Ptr& type) { DelayedType::Ptr delayed = type.cast(); if (!delayed) { return false; } static const IndexedQualifiedIdentifier ellipsis = IndexedQualifiedIdentifier(QualifiedIdentifier("...")); return delayed->identifier().identifier() == ellipsis; } }