kde-extraapps/kdevplatform/language/duchain/duchainutils.cpp
2015-07-26 14:23:17 +03:00

616 lines
19 KiB
C++

/*
* DUChain Utilities
*
* Copyright 2007 Hamish Rodda <rodda@kde.org>
* Copyright 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "duchainutils.h"
#include <kiconloader.h>
#include <interfaces/ilanguage.h>
#include <interfaces/icore.h>
#include <interfaces/ilanguagecontroller.h>
#include "../interfaces/ilanguagesupport.h"
#include "declaration.h"
#include "classfunctiondeclaration.h"
#include "ducontext.h"
#include "duchain.h"
#include "use.h"
#include "duchainlock.h"
#include "classmemberdeclaration.h"
#include "functiondefinition.h"
#include "specializationstore.h"
#include "persistentsymboltable.h"
#include "classdeclaration.h"
#include "parsingenvironment.h"
using namespace KDevelop;
using namespace KTextEditor;
CodeCompletionModel::CompletionProperties DUChainUtils::completionProperties(const Declaration* dec)
{
CodeCompletionModel::CompletionProperties p;
if(dec->context()->type() == DUContext::Class) {
if (const ClassMemberDeclaration* member = dynamic_cast<const ClassMemberDeclaration*>(dec)) {
switch (member->accessPolicy()) {
case Declaration::Public:
p |= CodeCompletionModel::Public;
break;
case Declaration::Protected:
p |= CodeCompletionModel::Protected;
break;
case Declaration::Private:
p |= CodeCompletionModel::Private;
break;
default:
break;
}
if (member->isStatic())
p |= CodeCompletionModel::Static;
if (member->isAuto())
{}//TODO
if (member->isFriend())
p |= CodeCompletionModel::Friend;
if (member->isRegister())
{}//TODO
if (member->isExtern())
{}//TODO
if (member->isMutable())
{}//TODO
}
}
if (const AbstractFunctionDeclaration* function = dynamic_cast<const AbstractFunctionDeclaration*>(dec)) {
p |= CodeCompletionModel::Function;
if (function->isVirtual())
p |= CodeCompletionModel::Virtual;
if (function->isInline())
p |= CodeCompletionModel::Inline;
if (function->isExplicit())
{}//TODO
}
if( dec->isTypeAlias() )
p |= CodeCompletionModel::TypeAlias;
if (dec->abstractType()) {
switch (dec->abstractType()->whichType()) {
case AbstractType::TypeIntegral:
p |= CodeCompletionModel::Variable;
break;
case AbstractType::TypePointer:
p |= CodeCompletionModel::Variable;
break;
case AbstractType::TypeReference:
p |= CodeCompletionModel::Variable;
break;
case AbstractType::TypeFunction:
p |= CodeCompletionModel::Function;
break;
case AbstractType::TypeStructure:
p |= CodeCompletionModel::Class;
break;
case AbstractType::TypeArray:
p |= CodeCompletionModel::Variable;
break;
case AbstractType::TypeEnumeration:
p |= CodeCompletionModel::Enum;
break;
case AbstractType::TypeEnumerator:
p |= CodeCompletionModel::Variable;
break;
case AbstractType::TypeAbstract:
case AbstractType::TypeDelayed:
case AbstractType::TypeUnsure:
case AbstractType::TypeAlias:
// TODO
break;
}
if( dec->abstractType()->modifiers() & AbstractType::ConstModifier )
p |= CodeCompletionModel::Const;
if( dec->kind() == Declaration::Instance && !dec->isFunctionDeclaration() )
p |= CodeCompletionModel::Variable;
}
if (dec->context()) {
if( dec->context()->type() == DUContext::Global )
p |= CodeCompletionModel::GlobalScope;
else if( dec->context()->type() == DUContext::Namespace )
p |= CodeCompletionModel::NamespaceScope;
else if( dec->context()->type() != DUContext::Class && dec->context()->type() != DUContext::Enum )
p |= CodeCompletionModel::LocalScope;
}
return p;
}
/**We have to construct the item from the pixmap, else the icon will be marked as "load on demand",
* and for some reason will be loaded every time it's used(this function returns a QIcon marked "load on demand"
* each time this is called). And the loading is very slow. Seems like a bug somewhere, it cannot be ment to be that slow.
*/
#define RETURN_CACHED_ICON(name) {static QIcon icon(KIcon(name).pixmap(QSize(16, 16))); return icon;}
QIcon DUChainUtils::iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p)
{
if( (p & CodeCompletionModel::Variable) )
if( (p & CodeCompletionModel::Protected) )
RETURN_CACHED_ICON("CVprotected_var")
else if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("CVprivate_var")
else
RETURN_CACHED_ICON("CVpublic_var")
else
if( (p & CodeCompletionModel::Union) && (p & CodeCompletionModel::Protected) )
RETURN_CACHED_ICON("protected_union")
else if( p & CodeCompletionModel::Enum )
if( p & CodeCompletionModel::Protected )
RETURN_CACHED_ICON("protected_enum")
else if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("private_enum")
else
RETURN_CACHED_ICON("enum")
else if( p & CodeCompletionModel::Struct )
if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("private_struct")
else
RETURN_CACHED_ICON("struct")
else if( p & CodeCompletionModel::Slot )
if( p & CodeCompletionModel::Protected )
RETURN_CACHED_ICON("CVprotected_slot")
else if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("CVprivate_slot")
else if(p & CodeCompletionModel::Public )
RETURN_CACHED_ICON("CVpublic_slot")
else RETURN_CACHED_ICON("slot")
else if( p & CodeCompletionModel::Signal )
if( p & CodeCompletionModel::Protected )
RETURN_CACHED_ICON("CVprotected_signal")
else
RETURN_CACHED_ICON("signal")
else if( p & CodeCompletionModel::Class )
if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Protected) )
RETURN_CACHED_ICON("protected_class")
else if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Private) )
RETURN_CACHED_ICON("private_class")
else
RETURN_CACHED_ICON("code-class")
else if( p & CodeCompletionModel::Union )
if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("private_union")
else
RETURN_CACHED_ICON("union")
else if( p & CodeCompletionModel::TypeAlias )
if ((p & CodeCompletionModel::Const) /*|| (p & CodeCompletionModel::Volatile)*/)
RETURN_CACHED_ICON("CVtypedef")
else
RETURN_CACHED_ICON("typedef")
else if( p & CodeCompletionModel::Function ) {
if( p & CodeCompletionModel::Protected )
RETURN_CACHED_ICON("protected_function")
else if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("private_function")
else
RETURN_CACHED_ICON("code-function")
}
if( p & CodeCompletionModel::Protected )
RETURN_CACHED_ICON("protected_field")
else if( p & CodeCompletionModel::Private )
RETURN_CACHED_ICON("private_field")
else
RETURN_CACHED_ICON("field")
return KIcon();
}
QIcon DUChainUtils::iconForDeclaration(const Declaration* dec)
{
return iconForProperties(completionProperties(dec));
}
TopDUContext* DUChainUtils::contentContextFromProxyContext(TopDUContext* top)
{
if(!top)
return 0;
if(top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->isProxyContext()) {
if(!top->importedParentContexts().isEmpty())
{
DUContext* ctx = top->importedParentContexts()[0].context(0);
if(!ctx)
return 0;
TopDUContext* ret = ctx->topContext();
if(!ret)
return 0;
if(ret->url() != top->url())
kDebug() << "url-mismatch between content and proxy:" << top->url().toUrl() << ret->url().toUrl();
if(ret->url() == top->url() && !ret->parsingEnvironmentFile()->isProxyContext())
return ret;
}
else {
kDebug() << "Proxy-context imports no content-context";
}
} else
return top;
return 0;
}
TopDUContext* DUChainUtils::standardContextForUrl(const KUrl& url, bool preferProxyContext) {
KDevelop::TopDUContext* chosen = 0;
QList<KDevelop::ILanguage*> languages = ICore::self()->languageController()->languagesForUrl(url);
foreach( KDevelop::ILanguage* language, languages)
{
if(!chosen)
{
if (language->languageSupport())
chosen = language->languageSupport()->standardContext(url, preferProxyContext);
}
}
if(!chosen)
chosen = DUChain::self()->chainForDocument(IndexedString(url.pathOrUrl()), preferProxyContext);
if(!chosen && preferProxyContext)
return standardContextForUrl(url, false); // Fall back to a normal context
return chosen;
}
Declaration* declarationUnderCursor(const CursorInRevision& c, DUContext* ctx)
{
foreach( Declaration* decl, ctx->localDeclarations() )
if( decl->range().contains(c, RangeInRevision::IncludeBackEdge) )
return decl;
//Search all collapsed sub-contexts. In C++, those can contain declarations that have ranges out of the context
foreach( DUContext* subCtx, ctx->childContexts() ) {
//This is a little hacky, but we need it in case of foreach macros and similar stuff
if(subCtx->range().isEmpty() || subCtx->range().start.line == c.line || subCtx->range().end.line == c.line) {
Declaration* decl = declarationUnderCursor(c, subCtx);
if(decl)
return decl;
}
}
return 0;
}
Declaration* DUChainUtils::itemUnderCursor(const KUrl& url, const KDevelop::SimpleCursor& _c)
{
KDevelop::TopDUContext* chosen = standardContextForUrl(url);
if( chosen )
{
CursorInRevision c = chosen->transformToLocalRevision(_c);
DUContext* ctx = chosen->findContextAt(c);
while( ctx ) {
//Try finding a declaration under the cursor
Declaration* decl = declarationUnderCursor(c, ctx);
if(decl)
return decl;
//Try finding a use under the cursor
for(int a = 0; a < ctx->usesCount(); ++a)
if( ctx->uses()[a].m_range.contains(c, RangeInRevision::IncludeBackEdge) )
return ctx->topContext()->usedDeclarationForIndex(ctx->uses()[a].m_declarationIndex);
ctx = ctx->parentContext(); //It may happen, for example in the case of function-declarations, that the use is one context higher.
}
}
return 0;
}
KTextEditor::Range DUChainUtils::itemRangeUnderCursor(const KUrl& url, const KDevelop::SimpleCursor& cursor)
{
KDevelop::TopDUContext* chosen = standardContextForUrl(url);
if( chosen ) {
CursorInRevision c = chosen->transformToLocalRevision(cursor);
DUContext* ctx = chosen->findContextAt(c);
if (ctx) {
Declaration* decl = declarationUnderCursor(c, ctx);
if (decl && decl->range().contains(c, RangeInRevision::IncludeBackEdge) ) {
return decl->rangeInCurrentRevision().textRange();
}
for(int a = 0; a < ctx->usesCount(); ++a) {
if( ctx->uses()[a].m_range.contains(c, RangeInRevision::IncludeBackEdge) ) {
return ctx->transformFromLocalRevision(ctx->uses()[a].m_range).textRange();
}
}
}
}
return KTextEditor::Range();
}
Declaration* DUChainUtils::declarationForDefinition(Declaration* definition, TopDUContext* topContext)
{
if(!definition)
return 0;
if(!topContext)
topContext = definition->topContext();
if(dynamic_cast<FunctionDefinition*>(definition)) {
Declaration* ret = static_cast<FunctionDefinition*>(definition)->declaration();
if(ret)
return ret;
}
return definition;
}
Declaration* DUChainUtils::declarationInLine(const KDevelop::SimpleCursor& _cursor, DUContext* ctx) {
if(!ctx)
return 0;
CursorInRevision cursor = ctx->transformToLocalRevision(_cursor);
foreach(Declaration* decl, ctx->localDeclarations()) {
if(decl->range().start.line == cursor.line)
return decl;
DUContext* funCtx = getFunctionContext(decl);
if(funCtx && funCtx->range().contains(cursor))
return decl;
}
foreach(DUContext* child, ctx->childContexts()){
Declaration* decl = declarationInLine(_cursor, child);
if(decl)
return decl;
}
return 0;
}
DUChainUtils::DUChainItemFilter::~DUChainItemFilter() {
}
void DUChainUtils::collectItems( DUContext* context, DUChainItemFilter& filter ) {
QVector<DUContext*> children = context->childContexts();
QVector<Declaration*> localDeclarations = context->localDeclarations();
QVector<DUContext*>::const_iterator childIt = children.constBegin();
QVector<Declaration*>::const_iterator declIt = localDeclarations.constBegin();
while(childIt != children.constEnd() || declIt != localDeclarations.constEnd()) {
DUContext* child = 0;
if(childIt != children.constEnd())
child = *childIt;
Declaration* decl = 0;
if(declIt != localDeclarations.constEnd())
decl = *declIt;
if(decl) {
if(child && child->range().start.line >= decl->range().start.line)
child = 0;
}
if(child) {
if(decl && decl->range().start >= child->range().start)
decl = 0;
}
if(decl) {
if( filter.accept(decl) ) {
//Action is done in the filter
}
++declIt;
continue;
}
if(child) {
if( filter.accept(child) )
collectItems(child, filter);
++childIt;
continue;
}
}
}
KDevelop::DUContext* DUChainUtils::getArgumentContext(KDevelop::Declaration* decl) {
DUContext* internal = decl->internalContext();
if( !internal )
return 0;
if( internal->type() == DUContext::Function )
return internal;
foreach( const DUContext::Import &ctx, internal->importedParentContexts() ) {
if( ctx.context(decl->topContext()) )
if( ctx.context(decl->topContext())->type() == DUContext::Function )
return ctx.context(decl->topContext());
}
return 0;
}
QList<IndexedDeclaration> DUChainUtils::collectAllVersions(Declaration* decl) {
QList<IndexedDeclaration> ret;
ret << IndexedDeclaration(decl);
if(decl->inSymbolTable())
{
uint count;
const IndexedDeclaration* allDeclarations;
PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations);
for(uint a = 0; a < count; ++a)
if(!(allDeclarations[a] == IndexedDeclaration(decl)))
ret << allDeclarations[a];
}
return ret;
}
///For a class, returns all classes that inherit it
QList<Declaration*> DUChainUtils::getInheriters(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions)
{
QList<Declaration*> ret;
if(!dynamic_cast<const ClassDeclaration*>(decl))
return ret;
if(maxAllowedSteps == 0)
return ret;
if(decl->internalContext() && decl->internalContext()->type() == DUContext::Class)
FOREACH_ARRAY(const IndexedDUContext& importer, decl->internalContext()->indexedImporters()) {
DUContext* imp = importer.data();
if(!imp)
continue;
if(imp->type() == DUContext::Class && imp->owner())
ret << imp->owner();
--maxAllowedSteps;
if(maxAllowedSteps == 0)
return ret;
}
if(maxAllowedSteps == 0)
return ret;
if(collectVersions && decl->inSymbolTable()) {
uint count;
const IndexedDeclaration* allDeclarations;
PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations);
for(uint a = 0; a < count; ++a) {
++maxAllowedSteps;
if(allDeclarations[a].data() && allDeclarations[a].data() != decl) {
ret += getInheriters(allDeclarations[a].data(), maxAllowedSteps, false);
}
if(maxAllowedSteps == 0)
return ret;
}
}
return ret;
}
QList<Declaration*> DUChainUtils::getOverriders(const Declaration* currentClass, const Declaration* overriddenDeclaration, uint& maxAllowedSteps) {
QList<Declaration*> ret;
if(maxAllowedSteps == 0)
return ret;
if(currentClass != overriddenDeclaration->context()->owner() && currentClass->internalContext())
ret += currentClass->internalContext()->findLocalDeclarations(overriddenDeclaration->identifier(), CursorInRevision::invalid(), currentClass->topContext(), overriddenDeclaration->abstractType());
foreach(Declaration* inheriter, getInheriters(currentClass, maxAllowedSteps))
ret += getOverriders(inheriter, overriddenDeclaration, maxAllowedSteps);
return ret;
}
static bool hasUse(DUContext* context, int usedDeclarationIndex) {
if(usedDeclarationIndex == std::numeric_limits<int>::max())
return false;
for(int a = 0; a < context->usesCount(); ++a)
if(context->uses()[a].m_declarationIndex == usedDeclarationIndex)
return true;
foreach(DUContext* child, context->childContexts())
if(hasUse(child, usedDeclarationIndex))
return true;
return false;
}
bool DUChainUtils::contextHasUse(DUContext* context, Declaration* declaration) {
return hasUse(context, context->topContext()->indexForUsedDeclaration(declaration, false));
}
static uint countUses(DUContext* context, int usedDeclarationIndex) {
if(usedDeclarationIndex == std::numeric_limits<int>::max())
return 0;
uint ret = 0;
for(int a = 0; a < context->usesCount(); ++a)
if(context->uses()[a].m_declarationIndex == usedDeclarationIndex)
++ret;
foreach(DUContext* child, context->childContexts())
ret += countUses(child, usedDeclarationIndex);
return ret;
}
uint DUChainUtils::contextCountUses(DUContext* context, Declaration* declaration) {
return countUses(context, context->topContext()->indexForUsedDeclaration(declaration, false));
}
Declaration* DUChainUtils::getOverridden(const Declaration* decl) {
const ClassFunctionDeclaration* classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(decl);
if(!classFunDecl || !classFunDecl->isVirtual())
return 0;
QList<Declaration*> decls;
foreach(const DUContext::Import &import, decl->context()->importedParentContexts()) {
DUContext* ctx = import.context(decl->topContext());
if(ctx)
decls += ctx->findDeclarations(QualifiedIdentifier(decl->identifier()),
CursorInRevision::invalid(), decl->abstractType(), decl->topContext(), DUContext::DontSearchInParent);
}
foreach(Declaration* found, decls) {
const ClassFunctionDeclaration* foundClassFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(found);
if(foundClassFunDecl && foundClassFunDecl->isVirtual())
return found;
}
return 0;
}
DUContext* DUChainUtils::getFunctionContext(Declaration* decl) {
DUContext* functionContext = decl->internalContext();
if(functionContext && functionContext->type() != DUContext::Function) {
foreach(const DUContext::Import& import, functionContext->importedParentContexts()) {
DUContext* ctx = import.context(decl->topContext());
if(ctx && ctx->type() == DUContext::Function)
functionContext = ctx;
}
}
if(functionContext && functionContext->type() == DUContext::Function)
return functionContext;
return 0;
}