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

694 lines
26 KiB
C++

/*
Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
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 "abstractdeclarationnavigationcontext.h"
#include <QtGui/QTextDocument>
#include <klocale.h>
#include "../functiondeclaration.h"
#include "../functiondefinition.h"
#include "../classfunctiondeclaration.h"
#include "../namespacealiasdeclaration.h"
#include "../forwarddeclaration.h"
#include "../types/enumeratortype.h"
#include "../types/enumerationtype.h"
#include "../types/functiontype.h"
#include "../duchainutils.h"
#include "../types/pointertype.h"
#include "../types/referencetype.h"
#include "../types/typeutils.h"
#include "../persistentsymboltable.h"
#include <interfaces/icore.h>
#include <interfaces/idocumentationcontroller.h>
#include <duchain/types/typealiastype.h>
#include <duchain/types/structuretype.h>
#include <duchain/classdeclaration.h>
#include <typeinfo>
namespace KDevelop {
AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext( DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext)
: AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : 0)), previousContext), m_declaration(decl), m_fullBackwardSearch(false)
{
//Jump from definition to declaration if possible
FunctionDefinition* definition = dynamic_cast<FunctionDefinition*>(m_declaration.data());
if(definition && definition->declaration())
m_declaration = DeclarationPointer(definition->declaration());
}
QString AbstractDeclarationNavigationContext::name() const
{
if(m_declaration.data())
return prettyQualifiedIdentifier(m_declaration).toString();
else
return declarationName(m_declaration);
}
QString AbstractDeclarationNavigationContext::html(bool shorten)
{
clear();
m_shorten = shorten;
modifyHtml() += "<html><body><p>" + fontSizePrefix(shorten);
addExternalHtml(m_prefix);
if(!m_declaration.data()) {
modifyHtml() += i18n("<br /> lost declaration <br />");
return currentHtml();
}
if( m_previousContext ) {
QString link = createLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) );
modifyHtml() += navigationHighlight(i18n("Back to %1<br />", link));
}
KSharedPtr<IDocumentation> doc;
if( !shorten ) {
doc = ICore::self()->documentationController()->documentationForDeclaration(m_declaration.data());
const AbstractFunctionDeclaration* function = dynamic_cast<const AbstractFunctionDeclaration*>(m_declaration.data());
if( function ) {
htmlFunction();
} else if( m_declaration->isTypeAlias() || m_declaration->kind() == Declaration::Instance ) {
if( m_declaration->isTypeAlias() )
modifyHtml() += importantHighlight("typedef ");
if(m_declaration->type<EnumeratorType>())
modifyHtml() += i18n("enumerator ");
AbstractType::Ptr useType = m_declaration->abstractType();
if(m_declaration->isTypeAlias()) {
//Do not show the own name as type of typedefs
if(useType.cast<TypeAliasType>())
useType = useType.cast<TypeAliasType>()->type();
}
eventuallyMakeTypeLinks( useType );
modifyHtml() += ' ' + nameHighlight(Qt::escape(declarationName(m_declaration))) + "<br>";
}else{
if( m_declaration->kind() == Declaration::Type && m_declaration->abstractType().cast<StructureType>() ) {
htmlClass();
}
if ( m_declaration->kind() == Declaration::Namespace ) {
modifyHtml() += i18n("namespace %1 ", nameHighlight(Qt::escape(m_declaration->qualifiedIdentifier().toString())));
}
if(m_declaration->type<EnumerationType>()) {
EnumerationType::Ptr enumeration = m_declaration->type<EnumerationType>();
modifyHtml() += i18n("enumeration %1 ", Qt::escape(m_declaration->identifier().toString()) );
}
if(m_declaration->isForwardDeclaration()) {
ForwardDeclaration* forwardDec = static_cast<ForwardDeclaration*>(m_declaration.data());
Declaration* resolved = forwardDec->resolve(m_topContext.data());
if(resolved) {
modifyHtml() += i18n("( resolved forward-declaration: ");
makeLink(resolved->identifier().toString(), KDevelop::DeclarationPointer(resolved), NavigationAction::NavigateDeclaration );
modifyHtml() += i18n(") ");
}else{
modifyHtml() += i18n("(unresolved forward-declaration) ");
QualifiedIdentifier id = forwardDec->qualifiedIdentifier();
uint count;
const IndexedDeclaration* decls;
PersistentSymbolTable::self().declarations(id, count, decls);
for(uint a = 0; a < count; ++a) {
if(decls[a].isValid() && !decls[a].data()->isForwardDeclaration()) {
modifyHtml() += "<br />";
makeLink(i18n("possible resolution from"), KDevelop::DeclarationPointer(decls[a].data()), NavigationAction::NavigateDeclaration);
modifyHtml() += ' ' + decls[a].data()->url().str();
}
}
}
}
modifyHtml() += "<br />";
}
}else{
AbstractType::Ptr showType = m_declaration->abstractType();
if(showType && showType.cast<FunctionType>()) {
showType = showType.cast<FunctionType>()->returnType();
if(showType)
modifyHtml() += labelHighlight(i18n("Returns: "));
}else if(showType) {
modifyHtml() += labelHighlight(i18n("Type: "));
}
if(showType) {
eventuallyMakeTypeLinks(showType);
modifyHtml() += " ";
}
}
QualifiedIdentifier identifier = m_declaration->qualifiedIdentifier();
if( identifier.count() > 1 ) {
if( m_declaration->context() && m_declaration->context()->owner() )
{
Declaration* decl = m_declaration->context()->owner();
FunctionDefinition* definition = dynamic_cast<FunctionDefinition*>(decl);
if(definition && definition->declaration())
decl = definition->declaration();
if(decl->abstractType().cast<EnumerationType>())
modifyHtml() += labelHighlight(i18n("Enum: "));
else
modifyHtml() += labelHighlight(i18n("Container: "));
makeLink( declarationName(DeclarationPointer(decl)), DeclarationPointer(decl), NavigationAction::NavigateDeclaration );
modifyHtml() += " ";
} else {
QualifiedIdentifier parent = identifier;
parent.pop();
modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(Qt::escape(parent.toString()))));
}
}
if( shorten && !m_declaration->comment().isEmpty() ) {
QString comment = QString::fromUtf8(m_declaration->comment());
if( comment.length() > 60 ) {
comment.truncate(60);
comment += "...";
}
comment.replace('\n', " ");
comment.replace("<br />", " ");
comment.replace("<br/>", " ");
modifyHtml() += commentHighlight(Qt::escape(comment)) + " ";
}
QString access = stringFromAccess(m_declaration);
if( !access.isEmpty() )
modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(Qt::escape(access))));
///@todo Enumerations
QString detailsHtml;
QStringList details = declarationDetails(m_declaration);
if( !details.isEmpty() ) {
bool first = true;
foreach( const QString &str, details ) {
if( !first )
detailsHtml += ", ";
first = false;
detailsHtml += propertyHighlight(str);
}
}
QString kind = declarationKind(m_declaration);
if( !kind.isEmpty() ) {
if( !detailsHtml.isEmpty() )
modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(Qt::escape(kind)), detailsHtml));
else
modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(Qt::escape(kind))));
} else if( !detailsHtml.isEmpty() ) {
modifyHtml() += labelHighlight(i18n("Modifiers: %1 ", importantHighlight(Qt::escape(kind))));
}
modifyHtml() += "<br />";
if(!shorten)
htmlAdditionalNavigation();
if( !shorten ) {
if(dynamic_cast<FunctionDefinition*>(m_declaration.data()))
modifyHtml() += labelHighlight(i18n( "Def.: " ));
else
modifyHtml() += labelHighlight(i18n( "Decl.: " ));
makeLink( QString("%1 :%2").arg( KUrl(m_declaration->url().str()).fileName() ).arg( m_declaration->rangeInCurrentRevision().textRange().start().line()+1 ), m_declaration, NavigationAction::JumpToSource );
modifyHtml() += " ";
//modifyHtml() += "<br />";
if(!dynamic_cast<FunctionDefinition*>(m_declaration.data())) {
if( FunctionDefinition* definition = FunctionDefinition::definition(m_declaration.data()) ) {
modifyHtml() += labelHighlight(i18n( " Def.: " ));
makeLink( QString("%1 :%2").arg( KUrl(definition->url().str()).fileName() ).arg( definition->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource );
}
}
if( FunctionDefinition* definition = dynamic_cast<FunctionDefinition*>(m_declaration.data()) ) {
if(definition->declaration()) {
modifyHtml() += labelHighlight(i18n( " Decl.: " ));
makeLink( QString("%1 :%2").arg( KUrl(definition->declaration()->url().str()).fileName() ).arg( definition->declaration()->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource );
}
}
modifyHtml() += " "; //The action name _must_ stay "show_uses", since that is also used from outside
makeLink(i18n("Show uses"), "show_uses", NavigationAction(m_declaration, NavigationAction::NavigateUses));
}
if( !shorten && (!m_declaration->comment().isEmpty() || doc) ) {
modifyHtml() += "<br />";
QString comment = QString::fromUtf8(m_declaration->comment());
if(comment.isEmpty() && doc) {
comment = doc->description();
if(!comment.isEmpty()) {
connect(doc.data(), SIGNAL(descriptionChanged()), this, SIGNAL(contentsChanged()));
modifyHtml() += "<br />" + commentHighlight(comment);
}
} else if(!comment.isEmpty()) {
comment.replace(QRegExp("<br */>"), "\n"); //do not escape html newlines within the comment
comment = Qt::escape(comment);
comment.replace('\n', "<br />"); //Replicate newlines in html
modifyHtml() += commentHighlight(comment);
modifyHtml() += "<br />";
}
}
if(!shorten && doc) {
modifyHtml() += "<br />" + i18n("Show documentation for ");
makeLink( prettyQualifiedIdentifier(m_declaration).toString(), m_declaration, NavigationAction::ShowDocumentation );
}
//modifyHtml() += "<br />";
addExternalHtml(m_suffix);
modifyHtml() += fontSizeSuffix(shorten) + "</p></body></html>";
return currentHtml();
}
KDevelop::AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(KDevelop::AbstractType::Ptr type) {
return type;
}
void AbstractDeclarationNavigationContext::htmlFunction()
{
const AbstractFunctionDeclaration* function = dynamic_cast<const AbstractFunctionDeclaration*>(m_declaration.data());
Q_ASSERT(function);
const ClassFunctionDeclaration* classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(m_declaration.data());
const FunctionType::Ptr type = m_declaration->abstractType().cast<FunctionType>();
if( !type ) {
modifyHtml() += errorHighlight("Invalid type<br />");
return;
}
if( !classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor()) ) {
// only print return type for global functions and non-ctor/dtor methods
eventuallyMakeTypeLinks( type->returnType() );
}
modifyHtml() += ' ' + nameHighlight(Qt::escape(prettyIdentifier(m_declaration).toString()));
if( type->indexedArgumentsSize() == 0 )
{
modifyHtml() += "()";
} else {
modifyHtml() += "( ";
bool first = true;
int firstDefaultParam = type->indexedArgumentsSize() - function->defaultParametersSize();
int currentArgNum = 0;
QVector<Declaration*> decls;
if (KDevelop::DUContext* argumentContext = DUChainUtils::getArgumentContext(m_declaration.data())) {
decls = argumentContext->localDeclarations(m_topContext.data());
}
foreach(const AbstractType::Ptr& argType, type->arguments()) {
if( !first )
modifyHtml() += ", ";
first = false;
eventuallyMakeTypeLinks( argType );
if (currentArgNum < decls.size()) {
modifyHtml() += ' ' + nameHighlight(Qt::escape(decls[currentArgNum]->identifier().toString()));
}
if( currentArgNum >= firstDefaultParam )
modifyHtml() += " = " + Qt::escape(function->defaultParameters()[ currentArgNum - firstDefaultParam ].str());
++currentArgNum;
}
modifyHtml() += " )";
}
modifyHtml() += "<br />";
}
Identifier AbstractDeclarationNavigationContext::prettyIdentifier(DeclarationPointer decl) const
{
Identifier ret;
QualifiedIdentifier q = prettyQualifiedIdentifier(decl);
if(!q.isEmpty())
ret = q.last();
return ret;
}
QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(DeclarationPointer decl) const
{
if(decl)
return decl->qualifiedIdentifier();
else
return QualifiedIdentifier();
}
void AbstractDeclarationNavigationContext::htmlAdditionalNavigation()
{
///Check if the function overrides or hides another one
const ClassFunctionDeclaration* classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(m_declaration.data());
if(classFunDecl) {
Declaration* overridden = DUChainUtils::getOverridden(m_declaration.data());
if(overridden) {
modifyHtml() += i18n("Overrides a ");
makeLink(i18n("function"), QString("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), KDevelop::NavigationAction::NavigateDeclaration));
modifyHtml() += i18n(" from ");
makeLink(prettyQualifiedIdentifier(DeclarationPointer(overridden->context()->owner())).toString(), QString("jump_to_overridden_container"), NavigationAction(DeclarationPointer(overridden->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration));
modifyHtml() += "<br />";
}else{
//Check if this declarations hides other declarations
QList<Declaration*> decls;
foreach(const DUContext::Import &import, m_declaration->context()->importedParentContexts())
if(import.context(m_topContext.data()))
decls += import.context(m_topContext.data())->findDeclarations(QualifiedIdentifier(m_declaration->identifier()),
CursorInRevision::invalid(), AbstractType::Ptr(), m_topContext.data(), DUContext::DontSearchInParent);
uint num = 0;
foreach(Declaration* decl, decls) {
modifyHtml() += i18n("Hides a ");
makeLink(i18n("function"), QString("jump_to_hide_%1").arg(num), NavigationAction(DeclarationPointer(decl), KDevelop::NavigationAction::NavigateDeclaration));
modifyHtml() += i18n(" from ");
makeLink(prettyQualifiedIdentifier(DeclarationPointer(decl->context()->owner())).toString(), QString("jump_to_hide_container_%1").arg(num), NavigationAction(DeclarationPointer(decl->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration));
modifyHtml() += "<br />";
++num;
}
}
///Show all places where this function is overridden
if(classFunDecl->isVirtual()) {
Declaration* classDecl = m_declaration->context()->owner();
if(classDecl) {
uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10;
QList<Declaration*> overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps);
if(!overriders.isEmpty()) {
modifyHtml() += i18n("Overridden in ");
bool first = true;
foreach(Declaration* overrider, overriders) {
if(!first)
modifyHtml() += ", ";
first = false;
QString name = prettyQualifiedIdentifier(DeclarationPointer(overrider->context()->owner())).toString();
makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration));
}
modifyHtml() += "<br />";
}
if(maxAllowedSteps == 0)
createFullBackwardSearchLink(overriders.isEmpty() ? i18n("Overriders possible, show all") : i18n("More overriders possible, show all"));
}
}
}
///Show all classes that inherit this one
uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10;
QList<Declaration*> inheriters = DUChainUtils::getInheriters(m_declaration.data(), maxAllowedSteps);
if(!inheriters.isEmpty()) {
modifyHtml() += i18n("Inherited by ");
bool first = true;
foreach(Declaration* importer, inheriters) {
if(!first)
modifyHtml() += ", ";
first = false;
QString importerName = prettyQualifiedIdentifier(DeclarationPointer(importer)).toString();
makeLink(importerName, importerName, NavigationAction(DeclarationPointer(importer), KDevelop::NavigationAction::NavigateDeclaration));
}
modifyHtml() += "<br />";
}
if(maxAllowedSteps == 0)
createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n("More inheriters possible, show all"));
}
void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(QString string)
{
makeLink(string, "m_fullBackwardSearch=true", NavigationAction("m_fullBackwardSearch=true"));
modifyHtml() += "<br />";
}
NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction( QString key )
{
if(key == "m_fullBackwardSearch=true") {
m_fullBackwardSearch = true;
clear();
}
return NavigationContextPointer(this);
}
void AbstractDeclarationNavigationContext::htmlClass()
{
StructureType::Ptr klass = m_declaration->abstractType().cast<StructureType>();
Q_ASSERT(klass);
ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(klass->declaration(m_topContext.data()));
if(classDecl) {
switch ( classDecl->classType() ) {
case ClassDeclarationData::Class:
modifyHtml() += "class ";
break;
case ClassDeclarationData::Struct:
modifyHtml() += "struct ";
break;
case ClassDeclarationData::Union:
modifyHtml() += "union ";
break;
case ClassDeclarationData::Interface:
modifyHtml() += "interface ";
break;
case ClassDeclarationData::Trait:
modifyHtml() += "trait ";
break;
default:
modifyHtml() += "<unknown type> ";
break;
}
eventuallyMakeTypeLinks( klass.cast<AbstractType>() );
FOREACH_FUNCTION( const KDevelop::BaseClassInstance& base, classDecl->baseClasses ) {
modifyHtml() += ", " + stringFromAccess(base.access) + " " + (base.virtualInheritance ? QString("virtual") : QString()) + " ";
eventuallyMakeTypeLinks(base.baseClass.abstractType());
}
} else {
/// @todo How can we get here? and should this really be a class?
modifyHtml() += "class ";
eventuallyMakeTypeLinks( klass.cast<AbstractType>() );
}
modifyHtml() += " ";
}
void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType)
{
Q_ASSERT(type);
Q_ASSERT(idType);
if( Declaration* decl = idType->declaration(m_topContext.data()) ) {
//Remove the last template-identifiers, because we create those directly
QualifiedIdentifier id = prettyQualifiedIdentifier(DeclarationPointer(decl));
Identifier lastId = id.last();
id.pop();
lastId.clearTemplateIdentifiers();
id.push(lastId);
if(decl->context() && decl->context()->owner()) {
//Also create full type-links for the context around
AbstractType::Ptr contextType = decl->context()->owner()->abstractType();
IdentifiedType* contextIdType = dynamic_cast<IdentifiedType*>(contextType.unsafeData());
if(contextIdType && !contextIdType->equals(idType)) {
//Create full type information for the context
if(!id.isEmpty())
id = id.mid(id.count()-1);
htmlIdentifiedType(contextType, contextIdType);
modifyHtml() += Qt::escape("::");
}
}
//We leave out the * and & reference and pointer signs, those are added to the end
makeLink(id.toString() , DeclarationPointer(idType->declaration(m_topContext.data())), NavigationAction::NavigateDeclaration );
} else {
kDebug() << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_topContext->url().str();
modifyHtml() += typeHighlight(Qt::escape(type->toString()));
}
}
void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type )
{
type = typeToShow(type);
if( !type ) {
modifyHtml() += typeHighlight(Qt::escape("<no type>"));
return;
}
AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, m_topContext.data() );
const IdentifiedType* idType = dynamic_cast<const IdentifiedType*>( target.unsafeData() );
kDebug() << "making type-links for" << type->toString() << typeid(*type).name();
if( idType && idType->declaration(m_topContext.data()) ) {
///@todo This is C++ specific, move into subclass
if(target->modifiers() & AbstractType::ConstModifier)
modifyHtml() += typeHighlight("const ");
htmlIdentifiedType(target, idType);
//We need to exchange the target type, else template-parameters may confuse this
SimpleTypeExchanger exchangeTarget(target, AbstractType::Ptr());
AbstractType::Ptr exchanged = exchangeTarget.exchange(type);
if(exchanged) {
QString typeSuffixString = exchanged->toString();
QRegExp suffixExp("\\&|\\*");
int suffixPos = typeSuffixString.indexOf(suffixExp);
if(suffixPos != -1)
modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos));
}
} else {
if(idType) {
kDebug() << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect();
}
modifyHtml() += typeHighlight(Qt::escape(type->toString()));
}
}
DeclarationPointer AbstractDeclarationNavigationContext::declaration() const
{
return m_declaration;
}
QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access)
{
switch(access) {
case Declaration::Private:
return "private";
case Declaration::Protected:
return "protected";
case Declaration::Public:
return "public";
default:
break;
}
return "";
}
QString AbstractDeclarationNavigationContext::stringFromAccess(DeclarationPointer decl)
{
const ClassMemberDeclaration* memberDecl = dynamic_cast<const ClassMemberDeclaration*>(decl.data());
if( memberDecl ) {
return stringFromAccess(memberDecl->accessPolicy());
}
return QString();
}
QString AbstractDeclarationNavigationContext::declarationName( DeclarationPointer decl ) const
{
if( NamespaceAliasDeclaration* alias = dynamic_cast<NamespaceAliasDeclaration*>(decl.data()) ) {
if( alias->identifier().isEmpty() )
return "using namespace " + alias->importIdentifier().toString();
else
return "namespace " + alias->identifier().toString() + " = " + alias->importIdentifier().toString();
}
if( !decl )
return i18nc("A declaration that is unknown", "Unknown");
else
return prettyIdentifier(decl).toString();
}
QStringList AbstractDeclarationNavigationContext::declarationDetails(DeclarationPointer decl)
{
QStringList details;
const AbstractFunctionDeclaration* function = dynamic_cast<const AbstractFunctionDeclaration*>(decl.data());
const ClassMemberDeclaration* memberDecl = dynamic_cast<const ClassMemberDeclaration*>(decl.data());
if( memberDecl ) {
if( memberDecl->isMutable() )
details << "mutable";
if( memberDecl->isRegister() )
details << "register";
if( memberDecl->isStatic() )
details << "static";
if( memberDecl->isAuto() )
details << "auto";
if( memberDecl->isExtern() )
details << "extern";
if( memberDecl->isFriend() )
details << "friend";
}
if( decl->isDefinition() )
details << i18nc("tells if a declaration is defining the variable's value", "definition");
if( decl->isExplicitlyDeleted() )
details << "deleted";
if( memberDecl && memberDecl->isForwardDeclaration() )
details << i18nc("as in c++ forward declaration", "forward");
AbstractType::Ptr t(decl->abstractType());
if( t ) {
if( t->modifiers() & AbstractType::ConstModifier )
details << i18nc("a variable that won't change, const", "constant");
if( t->modifiers() & AbstractType::VolatileModifier )
details << "volatile";
}
if( function ) {
if( function->isInline() )
details << "inline";
if( function->isExplicit() )
details << "explicit";
if( function->isVirtual() )
details << "virtual";
const ClassFunctionDeclaration* classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(decl.data());
if( classFunDecl ) {
if( classFunDecl->isSignal() )
details << "signal";
if( classFunDecl->isSlot() )
details << "slot";
if( classFunDecl->isConstructor() )
details << "constructor";
if( classFunDecl->isDestructor() )
details << "destructor";
if( classFunDecl->isConversionFunction() )
details << "conversion-function";
if( classFunDecl->isAbstract() )
details << "abstract";
}
}
return details;
}
}