/* 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 "cppduchain.h" #include #include #include #include #include #include #include #include #include "environmentmanager.h" #include "parser/rpp/pp-engine.h" #include "parser/rpp/preprocessor.h" #include "parser/rpp/pp-environment.h" #include "parser/rpp/pp-macro.h" #include "parser/rpp/macrorepository.h" #include "cppduchainexport.h" #include #include "templatedeclaration.h" #include "cppducontext.h" #include #include "templateparameterdeclaration.h" #include using namespace Cpp; using namespace KDevelop; const uint maxParentDepth = 20; namespace Cpp { KDEVCPPDUCHAIN_EXPORT QList findLocalDeclarations( KDevelop::DUContext* context, const KDevelop::Identifier& identifier, const TopDUContext* topContext, uint depth ) { QList ret; if(depth > maxParentDepth) { kDebug() << "maximum parent depth reached on" << context->scopeIdentifier(true); return ret; } ret += context->findLocalDeclarations( identifier, CursorInRevision::invalid(), topContext ); if( !ret.isEmpty() ) return ret; if( context->type() != DUContext::Class ) return ret; QVector bases = context->importedParentContexts(); for( QVector::const_iterator it = bases.constBegin(); it != bases.constEnd(); ++it ) { if( it->context(topContext) ) ret += findLocalDeclarations( (*it).context(topContext), identifier, topContext, depth+1 ); } return ret; } void minimize(int& i, const int with) { if( with < i ) i = with; } uint buildIdentifierForType(AbstractType::Ptr type, IndexedTypeIdentifier& id, uint pointerLevel, TopDUContext* top) { if(!type) return pointerLevel; TypePtr< ReferenceType > refType = type.cast(); if(refType) { id.setIsReference(true); if(refType->modifiers() & AbstractType::ConstModifier) id.setIsConstant(true); return buildIdentifierForType(refType->baseType(), id, pointerLevel, top); } TypePtr< PointerType > pointerType = type.cast(); if(pointerType) { ++pointerLevel; uint maxPointerLevel = buildIdentifierForType(pointerType->baseType(), id, pointerLevel, top); if(type->modifiers() & AbstractType::ConstModifier) id.setIsConstPointer(maxPointerLevel - pointerLevel, true); if(static_cast(id.pointerDepth()) < pointerLevel) id.setPointerDepth(pointerLevel); return maxPointerLevel; } IdentifiedType* idType = dynamic_cast(type.unsafeData()); if(idType) { Declaration* decl = idType->declaration(top); if(decl) { id.setIdentifier(decl->qualifiedIdentifier()); }else id.setIdentifier(idType->qualifiedIdentifier()); }else{ //Just create it as an expression AbstractType::Ptr useTypeText = type; if(type->modifiers() & AbstractType::ConstModifier) { //Remove the 'const' modifier, as it will be added to the type-identifier below useTypeText = type->indexed().abstractType(); useTypeText->setModifiers(useTypeText->modifiers() & (~AbstractType::ConstModifier)); } id.setIdentifier(QualifiedIdentifier(useTypeText->toString(), true)); } if(type->modifiers() & AbstractType::ConstModifier) id.setIsConstant(true); if(type->modifiers() & AbstractType::VolatileModifier) id.setIsVolatile(true); return pointerLevel; } IndexedTypeIdentifier identifierForType(AbstractType::Ptr type, TopDUContext* top) { IndexedTypeIdentifier ret; buildIdentifierForType(type, ret, 0, top); return ret; } QList< QPair > hideOverloadedDeclarations( const QList< QPair >& declarations, bool preferConst ) { QHash nearestDeclaration; QHash depthHash; QSet hadNonForwardDeclaration; //Set of all non function-declarations that had a declaration that was not a forward-declaration. // Set of function-declarations with a const overload QSet hasConstOverload; typedef QPair Pair; foreach( const Pair& decl, declarations ) { depthHash[decl.first] = decl.second; QHash::iterator it = nearestDeclaration.find(decl.first->identifier()); bool prefer = false; if(it == nearestDeclaration.end()) { prefer = true; } else if((!decl.first->isForwardDeclaration() && (*it)->isForwardDeclaration())) { //Always prefer non forward-declarations over forward-declarations prefer = true; } else if(decl.first->isForwardDeclaration() == (*it)->isForwardDeclaration()) { const int depth = depthHash.value(*it); if (decl.second < depth) { prefer = true; } else if (decl.second == depth && decl.first->isFunctionDeclaration() && decl.first->context()->type() == DUContext::Class && TypeUtils::isConstant((*it)->abstractType()) != TypeUtils::isConstant(decl.first->abstractType())) { // const overload hasConstOverload << decl.first->indexedIdentifier(); } } if(prefer) { nearestDeclaration[ decl.first->identifier() ] = decl.first; } if(!decl.first->isForwardDeclaration() && !decl.first->isFunctionDeclaration()) hadNonForwardDeclaration.insert(decl.first->identifier()); } QList< QPair > ret; ///Only keep the declarations of each name on the lowest inheritance-level, or that are not forward-declarations foreach( const Pair& decl, declarations ) { if( depthHash[nearestDeclaration[decl.first->identifier()]] == decl.second ) { if(decl.first->isFunctionDeclaration() && decl.first->context()->type() == DUContext::Class) { // hide non-const methods if preferConst is set, or vice-versa const bool isConst = TypeUtils::isConstant(decl.first->abstractType()); const bool hasOverload = hasConstOverload.contains(decl.first->indexedIdentifier()); if (hasOverload && isConst != preferConst) { // if we have an overload, always prefer the version with correct constness continue; } else if (preferConst && !isConst) { // otherwise only hide non-cost methods if we are looking for const methods // const-methods should be shown when we are looking for non-const methods though continue; } } if(decl.first->isFunctionDeclaration() || !decl.first->isForwardDeclaration() || !hadNonForwardDeclaration.contains(decl.first->identifier())) ret << decl; } } return ret; } QList findDeclarationsSameLevel(KDevelop::DUContext* context, const Identifier& identifier, const KDevelop::CursorInRevision& position) { if( context->type() == DUContext::Namespace || context->type() == DUContext::Global ) { ///May have been forward-declared anywhere QualifiedIdentifier totalId = context->scopeIdentifier(); totalId += identifier; return context->findDeclarations(totalId, position); }else{ ///Only search locally within this context return context->findLocalDeclarations(identifier, position); } } KDevelop::QualifiedIdentifier namespaceScopeComponentFromContext(QualifiedIdentifier prefix, const KDevelop::DUContext* context, const KDevelop::TopDUContext* source) { const DUContext* classContext = 0; if(context->type() == DUContext::Helper) { //This is a prefix-context for an external class-definition like "class A::B {..};" if(context->importedParentContexts().size()) classContext = context->importedParentContexts()[0].context(source); } else if(context->type() == DUContext::Class) { classContext = context; }else if(context->type() == DUContext::Namespace) { return context->scopeIdentifier(true); }else{ //This must be a function-definition, like void A::B::test() {} Declaration* classDeclaration = Cpp::localClassFromCodeContext(const_cast(context)); if(classDeclaration) classContext = classDeclaration->logicalInternalContext(source); if(!prefix.isEmpty()) prefix.pop(); } if(classContext) { while(!prefix.isEmpty() && classContext && classContext->type() == DUContext::Class) { Q_ASSERT(!prefix.isEmpty()); prefix.pop(); //This way we can correctly resolve the namespace-component for multiple externally defined classes, //see testDeclareStructInNamespace() in test_duchain.cpp if(classContext->parentContext() && classContext->parentContext()->type() == DUContext::Helper && !context->importedParentContexts().isEmpty()) { classContext = context->importedParentContexts()[0].context(source); continue; } break; } } return prefix; } Declaration* localClassFromCodeContext(const DUContext* context) { if(!context) return 0; while( context->parentContext() && context->type() == DUContext::Other && context->parentContext()->type() == DUContext::Other ) { //Move context to the top context of type "Other". This is needed because every compound-statement creates a new sub-context. context = context->parentContext(); } if(context->type() == DUContext::Class) return context->owner(); //For function declarations, this is the solution. if(context->parentContext() && context->parentContext()->type() == DUContext::Class) return context->parentContext()->owner(); if(context->type() == DUContext::Other) { //Jump from code-context to function-context foreach(const DUContext::Import& import, context->importedParentContexts()) { if(DUContext* i = import.context(context->topContext())) { if(i->type() == DUContext::Function) { context = i; break; } } } } //For external function definitions, find the class-context by following the import-structure if(context->type() == DUContext::Function) { foreach(const DUContext::Import& import, context->importedParentContexts()) { DUContext* ctx = import.context(context->topContext()); if(ctx && ctx->type() == DUContext::Class && ctx->owner()) return ctx->owner(); } if(!context->importers().isEmpty()) context = context->importers().first(); } return 0; } Declaration* localFunctionFromCodeContext(const DUContext* context) { while( context->parentContext() && context->type() == DUContext::Other && context->parentContext()->type() == DUContext::Other ) { //Move context to the top context of type "Other". This is needed because every compound-statement creates a new sub-context. context = context->parentContext(); } if(context->type() == DUContext::Function || context->owner()) { return context->owner(); } if(context->type() == DUContext::Other) { //Jump from code-context to function-context foreach(const DUContext::Import& import, context->importedParentContexts()) { if(DUContext* i = import.context(context->topContext())) { if(i->type() == DUContext::Function) { return i->owner(); } } } } return 0; } ClassMemberDeclaration::AccessPolicy mostRestrictiveInheritanceAccessPolicy(DUContext* startContext, DUContext* targetContext, TopDUContext* top, bool ignoreFirst = false) { ClassMemberDeclaration::AccessPolicy ret = ClassMemberDeclaration::Public; if(startContext != targetContext) { ClassDeclaration* classDecl = dynamic_cast(startContext->owner()); if(classDecl) { FOREACH_FUNCTION(const BaseClassInstance& import, classDecl->baseClasses) { AbstractType::Ptr type = import.baseClass.abstractType(); IdentifiedType* identified = dynamic_cast(type.unsafeData()); if(identified) { Declaration* decl = identified->declaration(top); ///@todo This is not very efficient if(decl && decl->internalContext() && decl->internalContext()->imports(targetContext)) { ret = mostRestrictiveInheritanceAccessPolicy(decl->internalContext(), targetContext, top); if(import.access > ret && !ignoreFirst) ret = import.access; break; } } } } } return ret; } KDEVCPPDUCHAIN_EXPORT bool isAccessible(DUContext* fromContext, ClassMemberDeclaration* declaration, TopDUContext* source, DUContext* declarationContext) { if(declarationContext) { ClassMemberDeclaration::AccessPolicy restriction = mostRestrictiveInheritanceAccessPolicy(declarationContext, declaration->context(), source); if(restriction != ClassMemberDeclaration::Public) return false; }else if(!declarationContext && fromContext->type() == DUContext::Class && fromContext->imports(declaration->context())) { declarationContext = fromContext; ClassMemberDeclaration::AccessPolicy restriction = mostRestrictiveInheritanceAccessPolicy(declarationContext, declaration->context(), source, true); if(restriction == ClassMemberDeclaration::Private) return false; } ClassMemberDeclaration::AccessPolicy effectiveAccessPolicy = declaration->accessPolicy(); if(effectiveAccessPolicy == ClassMemberDeclaration::Public) return true; if(!fromContext) return false; if(fromContext->type() == DUContext::Other || fromContext->type() == DUContext::Function) { Declaration* classDecl = localClassFromCodeContext(fromContext); if(!classDecl || !classDecl->internalContext()) { return false; } return isAccessible(classDecl->internalContext(), declaration, source, declarationContext); } if(fromContext->type() != DUContext::Class) return false; if(effectiveAccessPolicy == ClassMemberDeclaration::Protected) { if(fromContext->imports( declaration->context() )) { return true; } }else if(effectiveAccessPolicy == ClassMemberDeclaration::Private) { if(fromContext == declaration->context()) return true; } if(isFriend(declaration->context()->owner(), fromContext->owner())) return true; DUContext* parent = logicalParentContext(fromContext, fromContext->topContext()); if(parent && parent->type() == DUContext::Class) return isAccessible(parent, declaration, source, declarationContext); return false; } KDevelop::DUContext* logicalParentContext(KDevelop::DUContext* context, KDevelop::TopDUContext* source) { if(!context->parentContext()) return 0; if(context->parentContext()->type() == DUContext::Helper && !context->parentContext()->importedParentContexts().isEmpty()) return context->parentContext()->importedParentContexts()[0].context(source); return context->parentContext(); } /** * Preprocess the given string using the macros from given EnvironmentFile up to the given line * If line is -1, all macros are respected. * This is a quite slow operation, because thousands of macros need to be shuffled around. * */ QString preprocess( const QString& text, Cpp::EnvironmentFile* file, int line, QSet disableMacros ) { rpp::Preprocessor preprocessor; rpp::pp pp(&preprocessor); { DUChainReadLocker lock(DUChain::lock()); //Copy in all macros from the file for( Cpp::ReferenceCountedMacroSet::Iterator it( file->definedMacros().iterator() ); it; ++it ) { if( line == -1 || line > it.ref().sourceLine || file->url() != it.ref().file ) { if(!disableMacros.contains( it.ref().name )) { pp.environment()->setMacro( new rpp::pp_macro(it.ref()) ); } } } for( Cpp::ReferenceCountedMacroSet::Iterator it( file->usedMacros().iterator() ); it; ++it ) { if( line == -1 || line > it.ref().sourceLine || file->url() != it.ref().file ) { if(!disableMacros.contains( it.ref().name )) pp.environment()->setMacro( new rpp::pp_macro(it.ref()) ); } } } QString ret = QString::fromUtf8(stringFromContents(pp.processFile("anonymous", text.toUtf8()))); return ret; } QPair qtFunctionSignature(QByteArray fullFunction) { if(fullFunction.startsWith('"') && fullFunction.endsWith('"')) fullFunction = fullFunction.mid(1, fullFunction.length()-2); int parenBegin = fullFunction.indexOf('('); int parenEnd = fullFunction.lastIndexOf(')'); Identifier id; QByteArray signature; if(parenBegin < parenEnd && parenBegin != -1) { id = Identifier(IndexedString(fullFunction.left(parenBegin).trimmed())); signature = QMetaObject::normalizedSignature(fullFunction.mid(parenBegin, parenEnd-parenBegin+1).data()); signature = signature.mid(1, signature.length()-2); } return qMakePair(id, signature); } KDevelop::Identifier exchangeQualifiedIdentifier(KDevelop::Identifier id, KDevelop::QualifiedIdentifier replace, KDevelop::QualifiedIdentifier replaceWith) { KDevelop::Identifier ret(id); ret.clearTemplateIdentifiers(); for(unsigned int a = 0; a < id.templateIdentifiersCount(); ++a) ret.appendTemplateIdentifier(exchangeQualifiedIdentifier(id.templateIdentifier(a), replace, replaceWith)); return ret; } KDevelop::IndexedTypeIdentifier exchangeQualifiedIdentifier(KDevelop::IndexedTypeIdentifier id, KDevelop::QualifiedIdentifier replace, KDevelop::QualifiedIdentifier replaceWith) { KDevelop::IndexedTypeIdentifier ret(id); QualifiedIdentifier oldId(id.identifier().identifier()); QualifiedIdentifier qid; if(oldId == replace) { for(int a = 0; a < replaceWith.count(); ++a) qid.push(replaceWith.at(a)); }else{ for(int a = 0; a < oldId.count(); ++a) qid.push(exchangeQualifiedIdentifier(oldId.at(a), replace, replaceWith)); } ret.setIdentifier(IndexedQualifiedIdentifier(qid)); return ret; } KDevelop::IndexedTypeIdentifier unTypedefType(Declaration* decl, KDevelop::IndexedTypeIdentifier type) { for(int a = 0; a < decl->context()->usesCount(); ++a) { Use use = decl->context()->uses()[a]; if(use.m_range.end > decl->range().start) break; Declaration* usedDecl = use.usedDeclaration(decl->topContext()); ///@todo Make this work nicely for template-parameters. We need to know from where they were instantiated to do this though. if(usedDecl && usedDecl->isTypeAlias() && !dynamic_cast(usedDecl) && TypeUtils::targetType(usedDecl->abstractType(), 0)) { QualifiedIdentifier exchange(TypeUtils::targetType(usedDecl->abstractType(), 0)->toString()); QualifiedIdentifier exchangeWith(usedDecl->qualifiedIdentifier()); type = exchangeQualifiedIdentifier(type, exchange, exchangeWith); } } return type; } IndexedTypeIdentifier removeTemplateParameters(IndexedTypeIdentifier identifier, int behindPosition); Identifier removeTemplateParameters(Identifier id, int behindPosition) { Identifier ret(id); ret.clearTemplateIdentifiers(); for(unsigned int a = 0; a < id.templateIdentifiersCount(); ++a) { IndexedTypeIdentifier replacement = removeTemplateParameters(id.templateIdentifier(a), behindPosition); if((int) a < behindPosition) ret.appendTemplateIdentifier(replacement); else { ret.appendTemplateIdentifier(IndexedTypeIdentifier(QualifiedIdentifier("..."))); break; } } return ret; } IndexedTypeIdentifier removeTemplateParameters(IndexedTypeIdentifier identifier, int behindPosition) { IndexedTypeIdentifier ret(identifier); QualifiedIdentifier oldId(identifier.identifier().identifier()); QualifiedIdentifier qid; for(int a = 0; a < oldId.count(); ++a) qid.push(removeTemplateParameters(oldId.at(a), behindPosition)); ret.setIdentifier(qid); return ret; } KDevelop::IndexedTypeIdentifier stripPrefixIdentifiers(KDevelop::IndexedTypeIdentifier id, KDevelop::QualifiedIdentifier strip); KDevelop::Identifier stripPrefixIdentifiers(KDevelop::Identifier id, KDevelop::QualifiedIdentifier strip) { KDevelop::Identifier ret(id); ret.clearTemplateIdentifiers(); for(unsigned int a = 0; a < id.templateIdentifiersCount(); ++a) ret.appendTemplateIdentifier(stripPrefixIdentifiers(id.templateIdentifier(a), strip)); return ret; } KDevelop::IndexedTypeIdentifier stripPrefixIdentifiers(KDevelop::IndexedTypeIdentifier id, KDevelop::QualifiedIdentifier strip) { QualifiedIdentifier oldId(id.identifier().identifier()); QualifiedIdentifier qid; int commonPrefix = 0; for(;commonPrefix < oldId.count()-1 && commonPrefix < strip.count(); ++commonPrefix) if(strip.at(commonPrefix).toString() != oldId.at(commonPrefix).toString()) break; for(int a = commonPrefix; a < oldId.count(); ++a) qid.push( stripPrefixIdentifiers(oldId.at(a), strip) ); KDevelop::IndexedTypeIdentifier ret(id); ret.setIdentifier(qid); return ret; } int reservedIdentifierCount(QString name) { QStringList l = name.split("::"); int ret = 0; foreach(const QString& s, l) if(s.startsWith('_')) ++ret; return ret; } AbstractType::Ptr shortenTypeForViewing(AbstractType::Ptr type) { struct ShortenAliasExchanger : public KDevelop::TypeExchanger { virtual KDevelop::AbstractType::Ptr exchange(const KDevelop::AbstractType::Ptr& type) { if(!type) return type; KDevelop::AbstractType::Ptr newType( type->clone() ); KDevelop::TypeAliasType::Ptr alias = type.cast(); if(alias) { //If the aliased type has less involved template arguments, prefer it AbstractType::Ptr shortenedTarget = exchange(alias->type()); if(shortenedTarget && shortenedTarget->toString().count('<') < alias->toString().count('<') && reservedIdentifierCount(shortenedTarget->toString()) <= reservedIdentifierCount(alias->toString())) { shortenedTarget->setModifiers(shortenedTarget->modifiers() | alias->modifiers()); return shortenedTarget; } } newType->exchangeTypes(this); return newType; } }; ShortenAliasExchanger exchanger; type = exchanger.exchange(type); return type; } ///Returns a type that has all template types replaced with DelayedType's that have their template default parameters stripped away, ///and all scope prefixes removed that are redundant within the given context ///The returned type should not actively be used in the type-system, but rather only for displaying. AbstractType::Ptr stripType(KDevelop::AbstractType::Ptr type, DUContext* ctx) { if(!type) return AbstractType::Ptr(); struct ShortenTemplateDefaultParameter : public KDevelop::TypeExchanger { DUContext* ctx; ShortenTemplateDefaultParameter(DUContext* _ctx) : ctx(_ctx) { Q_ASSERT(ctx); } virtual KDevelop::AbstractType::Ptr exchange(const KDevelop::AbstractType::Ptr& type) { if(!type) return type; KDevelop::AbstractType::Ptr newType( type->clone() ); if(const KDevelop::IdentifiedType* idType = dynamic_cast(type.unsafeData())) { KDevelop::Declaration* decl = idType->declaration(ctx->topContext()); if(!decl) return type; QualifiedIdentifier newTypeName; if(TemplateDeclaration* tempDecl = dynamic_cast(decl)) { if(decl->context()->type() == DUContext::Class && decl->context()->owner()) { //Strip template default-parameters from the parent class AbstractType::Ptr parentType = stripType(decl->context()->owner()->abstractType(), ctx); if(parentType) { newTypeName = QualifiedIdentifier(parentType->toString(), true); } } if(newTypeName.isEmpty()) newTypeName = decl->context()->scopeIdentifier(true); Identifier currentId; if(!idType->qualifiedIdentifier().isEmpty()) currentId = idType->qualifiedIdentifier().last(); currentId.clearTemplateIdentifiers(); KDevelop::InstantiationInformation instantiationInfo = tempDecl->instantiatedWith().information(); KDevelop::InstantiationInformation newInformation(instantiationInfo); newInformation.templateParametersList().clear(); for(uint neededParameters = 0; neededParameters < instantiationInfo.templateParametersSize(); ++neededParameters) { newInformation.templateParametersList().append(instantiationInfo.templateParameters()[neededParameters]); AbstractType::Ptr niceParam = stripType(instantiationInfo.templateParameters()[neededParameters].abstractType(), ctx); if(niceParam) { currentId.appendTemplateIdentifier(IndexedTypeIdentifier(niceParam->toString(), true)); // kDebug() << "testing param" << niceParam->toString(); } if(tempDecl->instantiate(newInformation, ctx->topContext()) == decl) { // kDebug() << "got full instantiation"; break; } } newTypeName.push(currentId); }else{ newTypeName = decl->qualifiedIdentifier(); } //Strip unneded prefixes of the scope KDevelop::QualifiedIdentifier candidate = newTypeName; while(candidate.count() > 1) { candidate = candidate.mid(1); QList< KDevelop::Declaration* > decls = ctx->findDeclarations(candidate); if(decls.isEmpty()) continue; // type aliases might be available for nested sub scopes, hence we must not break early if(decls[0]->kind() != Declaration::Type || TypeUtils::removeConstModifier(decls[0]->indexedType()) != TypeUtils::removeConstModifier(type->indexed())) break; newTypeName = candidate; } if(newTypeName == decl->qualifiedIdentifier()) return type; DelayedType::Ptr ret(new KDevelop::DelayedType); IndexedTypeIdentifier ti(newTypeName); ti.setIsConstant(type->modifiers() & AbstractType::ConstModifier); ret->setIdentifier(ti); return ret.cast(); } newType->exchangeTypes(this); return newType; } }; ShortenTemplateDefaultParameter exchanger(ctx); type = exchanger.exchange(type); return type; } AbstractType::Ptr typeForShortenedString (Declaration* decl) { AbstractType::Ptr type = decl->abstractType(); if(decl->isTypeAlias()) { if(type.cast()) type = type.cast()->type(); } if(decl->isFunctionDeclaration()) { FunctionType::Ptr funType = decl->type(); if(!funType) return AbstractType::Ptr(); type = funType->returnType(); } return type; } QString shortenedTypeString(KDevelop::Declaration* decl, KDevelop::DUContext* ctx, int desiredLength, KDevelop::QualifiedIdentifier stripPrefix) { return shortenedTypeString(typeForShortenedString(decl), ctx, desiredLength, stripPrefix); } QString simplifiedTypeString(KDevelop::AbstractType::Ptr type, KDevelop::DUContext* visibilityFrom) { return shortenedTypeString(type, visibilityFrom, 100000); } QString shortenedTypeString(KDevelop::AbstractType::Ptr type, KDevelop::DUContext* ctx, int desiredLength, KDevelop::QualifiedIdentifier stripPrefix) { return shortenedTypeIdentifier(type, ctx, desiredLength, stripPrefix).toString(); } IndexedTypeIdentifier shortenedTypeIdentifier(AbstractType::Ptr type, DUContext* ctx, int desiredLength, QualifiedIdentifier stripPrefix) { bool isReference = false; bool isRValue = false; if(ReferenceType::Ptr refType = type.cast()) { isReference = true; type = refType->baseType(); isRValue = refType->isRValue(); } type = shortenTypeForViewing(type); if(ctx) type = stripType(type, ctx); if(!type) return IndexedTypeIdentifier(); IndexedTypeIdentifier identifier = identifierForType(type, ctx ? ctx->topContext() : 0); if(type.cast()) identifier = type.cast()->identifier(); identifier = stripPrefixIdentifiers(identifier, stripPrefix); if(isReference) identifier.setIsReference(true); if(isRValue) identifier.setIsRValue(true); // if(identifier.toString().length() > desiredLength) // identifier = Cpp::unTypedefType(decl, identifier); int removeTemplateParametersFrom = 10; while(identifier.toString().length() > desiredLength * 3 && removeTemplateParametersFrom >= 0) { --removeTemplateParametersFrom; identifier = removeTemplateParameters(identifier, removeTemplateParametersFrom); } return identifier; } bool isFriend(KDevelop::Declaration* _class, KDevelop::Declaration* _friend) { if(!_class || !_friend) return false; DUContext* classInternal = _class->internalContext(); if(!classInternal) return false; static IndexedIdentifier friendIdentifier(Identifier("friend")); ///@todo Make this more efficient QList decls = classInternal->findLocalDeclarations(friendIdentifier.identifier()); foreach(Declaration* decl, decls) if(decl->indexedType() == _friend->indexedType()) return true; return false; } DUContext* getTemplateContext(DUContext* internal, const TopDUContext* source) { if(internal->type() == DUContext::Template) return internal; if(!source) source = internal->topContext(); foreach( const DUContext::Import &ctx, internal->importedParentContexts() ) { DUContext* c = ctx.context(source); if( c ) { if( c->type() == DUContext::Template ) return c; c = getTemplateContext(c, source); if(c) return c; } } return 0; } ///Returns the context assigned to the given declaration that contains the template-parameters, if available. Else zero. DUContext* getTemplateContext(Declaration* decl, const TopDUContext* source) { DUContext* internal = decl->internalContext(); if( !internal ) return 0; return getTemplateContext(internal, source); } QualifiedIdentifier stripPrefixes(DUContext* ctx, QualifiedIdentifier id) { if(!ctx) return id; QList imports = ctx->fullyApplyAliases(QualifiedIdentifier(), ctx->topContext()); if(imports.contains(id)) return QualifiedIdentifier(); ///The id is a namespace that is imported into the current context QList< Declaration* > basicDecls = ctx->findDeclarations(id, CursorInRevision::invalid(), AbstractType::Ptr(), 0, (DUContext::SearchFlags)(DUContext::NoSelfLookUp | DUContext::NoFiltering)); if(basicDecls.isEmpty()) return id; QualifiedIdentifier newId = id.mid(1); while(!newId.isEmpty()) { QList< Declaration* > foundDecls = ctx->findDeclarations(newId, CursorInRevision::invalid(), AbstractType::Ptr(), 0, (DUContext::SearchFlags)(DUContext::NoSelfLookUp | DUContext::NoFiltering)); if(foundDecls == basicDecls) id = newId; // must continue to find the shortest possible identifier // esp. for cases where nested namespaces are used (e.g. using namespace a::b::c;) newId = newId.mid(1); } return id; } const KDevelop::IndexedIdentifier& castIdentifier() { static const KDevelop::IndexedIdentifier id(Identifier("operator{...cast...}")); return id; } const KDevelop::IndexedIdentifier& unnamedNamespaceIdentifier() { static const KDevelop::IndexedIdentifier id(Identifier("unnamed{...namespace...}")); return id; } }