diff --git a/kdevplatform/language/duchain/classmemberdeclaration.cpp b/kdevplatform/language/duchain/classmemberdeclaration.cpp
index 4d209b6271..3430d3b2d9 100644
--- a/kdevplatform/language/duchain/classmemberdeclaration.cpp
+++ b/kdevplatform/language/duchain/classmemberdeclaration.cpp
@@ -1,153 +1,200 @@
/* This is part of KDevelop
Copyright 2002-2005 Roberto Raggi
Copyright 2006 Adam Treat
Copyright 2006 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 "classmemberdeclaration.h"
#include "classmemberdeclarationdata.h"
#include "duchainregister.h"
#include
namespace KDevelop
{
ClassMemberDeclarationData::ClassMemberDeclarationData()
: m_accessPolicy(Declaration::Public)
+ , m_alignOfExponent(ClassMemberDeclarationData::MaxAlignOfExponent)
, m_isStatic(false)
, m_isAuto(false)
, m_isFriend(false)
, m_isRegister(false)
, m_isExtern(false)
, m_isMutable(false)
+ , m_sizeOf(-1)
+ , m_bitOffsetOf(-1)
{
}
ClassMemberDeclaration::ClassMemberDeclaration(const ClassMemberDeclaration& rhs) : Declaration(*new ClassMemberDeclarationData(*rhs.d_func())) {
}
REGISTER_DUCHAIN_ITEM(ClassMemberDeclaration);
Declaration* ClassMemberDeclaration::clonePrivate() const {
return new ClassMemberDeclaration(*this);
}
ClassMemberDeclaration::ClassMemberDeclaration(const RangeInRevision& range, DUContext* context)
: Declaration(*new ClassMemberDeclarationData, range )
{
d_func_dynamic()->setClassId(this);
if( context )
setContext( context );
}
ClassMemberDeclaration::ClassMemberDeclaration(ClassMemberDeclarationData& dd, const RangeInRevision& range )
: Declaration(dd, range)
{
}
ClassMemberDeclaration::ClassMemberDeclaration(ClassMemberDeclarationData& dd)
: Declaration(dd)
{
}
ClassMemberDeclaration::~ClassMemberDeclaration()
{
}
bool ClassMemberDeclaration::isStatic() const
{
return d_func()->m_isStatic;
}
void ClassMemberDeclaration::setStatic(bool isStatic)
{
d_func_dynamic()->m_isStatic = isStatic;
}
bool ClassMemberDeclaration::isAuto() const
{
return d_func()->m_isAuto;
}
void ClassMemberDeclaration::setAuto(bool isAuto)
{
d_func_dynamic()->m_isAuto = isAuto;
}
bool ClassMemberDeclaration::isFriend() const
{
return d_func()->m_isFriend;
}
void ClassMemberDeclaration::setFriend(bool isFriend)
{
d_func_dynamic()->m_isFriend = isFriend;
}
bool ClassMemberDeclaration::isRegister() const
{
return d_func()->m_isRegister;
}
void ClassMemberDeclaration::setRegister(bool isRegister)
{
d_func_dynamic()->m_isRegister = isRegister;
}
bool ClassMemberDeclaration::isExtern() const
{
return d_func()->m_isExtern;
}
void ClassMemberDeclaration::setExtern(bool isExtern)
{
d_func_dynamic()->m_isExtern = isExtern;
}
bool ClassMemberDeclaration::isMutable() const
{
return d_func()->m_isMutable;
}
void ClassMemberDeclaration::setMutable(bool isMutable)
{
d_func_dynamic()->m_isMutable = isMutable;
}
Declaration::AccessPolicy ClassMemberDeclaration::accessPolicy() const
{
return d_func()->m_accessPolicy;
}
void ClassMemberDeclaration::setAccessPolicy(Declaration::AccessPolicy accessPolicy)
{
d_func_dynamic()->m_accessPolicy = accessPolicy;
}
void ClassMemberDeclaration::setStorageSpecifiers(StorageSpecifiers specifiers)
{
DUCHAIN_D_DYNAMIC(ClassMemberDeclaration);
d->m_isStatic = specifiers & StaticSpecifier;
d->m_isAuto = specifiers & AutoSpecifier;
d->m_isFriend = specifiers & FriendSpecifier;
d->m_isRegister = specifiers & RegisterSpecifier;
d->m_isExtern = specifiers & ExternSpecifier;
d->m_isMutable = specifiers & MutableSpecifier;
}
+
+
+int64_t ClassMemberDeclaration::sizeOf() const
+{
+ return d_func()->m_sizeOf;
+}
+
+void ClassMemberDeclaration::setSizeOf(int64_t sizeOf)
+{
+ d_func_dynamic()->m_sizeOf = sizeOf;
+}
+
+int64_t ClassMemberDeclaration::bitOffsetOf() const
+{
+ return d_func()->m_bitOffsetOf;
+}
+
+void ClassMemberDeclaration::setBitOffsetOf(int64_t bitOffsetOf)
+{
+ d_func_dynamic()->m_bitOffsetOf = bitOffsetOf;
+}
+
+int64_t ClassMemberDeclaration::alignOf() const
+{
+ if (d_func()->m_alignOfExponent == ClassMemberDeclarationData::MaxAlignOfExponent) {
+ return -1;
+ } else {
+ return 1 << d_func()->m_alignOfExponent;
+ }
+}
+
+void ClassMemberDeclaration::setAlignOf(int64_t alignedTo)
+{
+ if (alignedTo <= 0) {
+ d_func_dynamic()->m_alignOfExponent = ClassMemberDeclarationData::MaxAlignOfExponent;
+ return;
+ }
+
+ unsigned int alignOfExponent = 0;
+ while (alignedTo >>= 1)
+ alignOfExponent++;
+ d_func_dynamic()->m_alignOfExponent = alignOfExponent;
+}
+
}
diff --git a/kdevplatform/language/duchain/classmemberdeclaration.h b/kdevplatform/language/duchain/classmemberdeclaration.h
index 7f70a97125..b4e34142f5 100644
--- a/kdevplatform/language/duchain/classmemberdeclaration.h
+++ b/kdevplatform/language/duchain/classmemberdeclaration.h
@@ -1,90 +1,122 @@
/* This file is part of KDevelop
Copyright 2002-2005 Roberto Raggi
Copyright 2006 Adam Treat
Copyright 2006 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.
*/
#ifndef KDEVPLATFORM_CLASSMEMBERDECLARATION_H
#define KDEVPLATFORM_CLASSMEMBERDECLARATION_H
#include "declaration.h"
namespace KDevelop
{
class ClassMemberDeclarationData;
/**
* Represents a single class member definition in a definition-use chain.
*/
class KDEVPLATFORMLANGUAGE_EXPORT ClassMemberDeclaration : public Declaration
{
public:
ClassMemberDeclaration(const ClassMemberDeclaration& rhs);
ClassMemberDeclaration(const RangeInRevision& range, DUContext* context);
explicit ClassMemberDeclaration(ClassMemberDeclarationData& dd);
~ClassMemberDeclaration() override;
AccessPolicy accessPolicy() const;
void setAccessPolicy(AccessPolicy accessPolicy);
enum StorageSpecifier {
StaticSpecifier = 0x1 /**< indicates static member */,
AutoSpecifier = 0x2 /**< indicates automatic determination of member access */,
FriendSpecifier = 0x4 /**< indicates friend member */,
ExternSpecifier = 0x8 /**< indicates external declaration */,
RegisterSpecifier = 0x10 /**< indicates register */,
MutableSpecifier = 0x20 /**< indicates a mutable member */
};
Q_DECLARE_FLAGS(StorageSpecifiers, StorageSpecifier)
void setStorageSpecifiers(StorageSpecifiers specifiers);
bool isStatic() const;
void setStatic(bool isStatic);
bool isAuto() const;
void setAuto(bool isAuto);
bool isFriend() const;
void setFriend(bool isFriend);
bool isRegister() const;
void setRegister(bool isRegister);
bool isExtern() const;
void setExtern(bool isExtern);
bool isMutable() const;
void setMutable(bool isMutable);
+ /**
+ * \returns The size in bytes or -1 if unknown.
+ */
+ int64_t sizeOf() const;
+
+ /**
+ * Set the size to given number of bytes. Use -1 to represent unknown size.
+ */
+ void setSizeOf(int64_t sizeOf);
+
+ /**
+ * \returns The offset of the field in bits or -1 if unknown or not applicable.
+ */
+ int64_t bitOffsetOf() const;
+
+ /**
+ * Set the offset to given number of bits. Use -1 to represent unknown offset.
+ */
+ void setBitOffsetOf(int64_t bitOffsetOf);
+
+ /**
+ * \returns The alignment in bytes or -1 if unknown.
+ */
+ int64_t alignOf() const;
+
+ /**
+ * Set the alignment to given number of bytes.
+ *
+ * The value must be non-negative power of 2 or -1 to represent unknown alignment.
+ */
+ void setAlignOf(int64_t alignedTo);
+
enum {
Identity = 9
};
protected:
ClassMemberDeclaration(ClassMemberDeclarationData& dd, const RangeInRevision& range);
DUCHAIN_DECLARE_DATA(ClassMemberDeclaration)
private:
Declaration* clonePrivate() const override;
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::ClassMemberDeclaration::StorageSpecifiers)
#endif // KDEVPLATFORM_CLASSMEMBERDECLARATION_H
diff --git a/kdevplatform/language/duchain/classmemberdeclarationdata.h b/kdevplatform/language/duchain/classmemberdeclarationdata.h
index e5cdd478ec..43a6493967 100644
--- a/kdevplatform/language/duchain/classmemberdeclarationdata.h
+++ b/kdevplatform/language/duchain/classmemberdeclarationdata.h
@@ -1,48 +1,62 @@
/* This is part of KDevelop
Copyright 2002-2005 Roberto Raggi
Copyright 2006 Adam Treat
Copyright 2006 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.
*/
#ifndef KDEVPLATFORM_CLASSMEMBERDECLARATIONDATA_H
#define KDEVPLATFORM_CLASSMEMBERDECLARATIONDATA_H
#include "declarationdata.h"
#include
namespace KDevelop
{
class KDEVPLATFORMLANGUAGE_EXPORT ClassMemberDeclarationData : public DeclarationData
{
public:
ClassMemberDeclarationData();
ClassMemberDeclarationData( const ClassMemberDeclarationData& rhs ) = default;
Declaration::AccessPolicy m_accessPolicy;
+
+ /**
+ * Since alignOf must be integral power of 2, we only need to store the power.
+ * The max value (63) represents unknown alignment.
+ */
+ unsigned m_alignOfExponent : 6;
+ static constexpr unsigned MaxAlignOfExponent = 63;
+
bool m_isStatic: 1;
bool m_isAuto: 1;
bool m_isFriend: 1;
bool m_isRegister: 1;
bool m_isExtern: 1;
bool m_isMutable: 1;
+
+ /// Stores sizeOf in bytes or -1 if unknown.
+ int64_t m_sizeOf;
+
+ /// Stores bitOffsetOf in bits or -1 if unknown.
+ int64_t m_bitOffsetOf;
};
}
#endif
diff --git a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
index 5ae249a9b8..29e33c6b6e 100644
--- a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
+++ b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
@@ -1,787 +1,824 @@
/*
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 "abstractdeclarationnavigationcontext.h"
#include
#include
#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 "../types/typesystem.h"
#include "../persistentsymboltable.h"
#include
#include
#include
#include
#include
#include
namespace KDevelop {
class AbstractDeclarationNavigationContextPrivate
{
public:
DeclarationPointer m_declaration;
bool m_fullBackwardSearch = false;
};
AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext(const DeclarationPointer& decl,
const TopDUContextPointer& topContext,
AbstractNavigationContext* previousContext)
: AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : nullptr)), previousContext)
, d(new AbstractDeclarationNavigationContextPrivate)
{
d->m_declaration = decl;
//Jump from definition to declaration if possible
FunctionDefinition* definition = dynamic_cast(d->m_declaration.data());
if(definition && definition->declaration())
d->m_declaration = DeclarationPointer(definition->declaration());
}
AbstractDeclarationNavigationContext::~AbstractDeclarationNavigationContext()
{
}
QString AbstractDeclarationNavigationContext::name() const
{
if(d->m_declaration.data())
return prettyQualifiedIdentifier(d->m_declaration).toString();
else
return declarationName(d->m_declaration);
}
QString AbstractDeclarationNavigationContext::html(bool shorten)
{
DUChainReadLocker lock(DUChain::lock(), 300);
if ( !lock.locked() ) {
return {};
}
clear();
AbstractNavigationContext::html(shorten);
modifyHtml() += QLatin1String("") + fontSizePrefix(shorten);
addExternalHtml(prefix());
if(!d->m_declaration.data()) {
modifyHtml() += i18n("
lost declaration
");
return currentHtml();
}
if(auto context = previousContext()) {
const QString link = createLink(context->name(), context->name(),
NavigationAction(context));
modifyHtml() += navigationHighlight(i18n("Back to %1
", link));
}
QExplicitlySharedDataPointer doc;
if( !shorten ) {
doc = ICore::self()->documentationController()->documentationForDeclaration(d->m_declaration.data());
const AbstractFunctionDeclaration* function = dynamic_cast(d->m_declaration.data());
if( function ) {
htmlFunction();
} else if( d->m_declaration->isTypeAlias() || d->m_declaration->type() || d->m_declaration->kind() == Declaration::Instance ) {
if( d->m_declaration->isTypeAlias() )
modifyHtml() += importantHighlight(QStringLiteral("typedef "));
if(d->m_declaration->type())
modifyHtml() += i18n("enumerator ");
AbstractType::Ptr useType = d->m_declaration->abstractType();
if(d->m_declaration->isTypeAlias()) {
//Do not show the own name as type of typedefs
if(useType.cast())
useType = useType.cast()->type();
}
eventuallyMakeTypeLinks( useType );
modifyHtml() += QLatin1Char(' ') + identifierHighlight(declarationName(d->m_declaration).toHtmlEscaped(), d->m_declaration);
if(auto integralType = d->m_declaration->type()) {
const QString plainValue = integralType->valueAsString();
if (!plainValue.isEmpty()) {
modifyHtml() += QStringLiteral(" = ") + plainValue;
}
}
modifyHtml() += QStringLiteral("
");
}else{
if( d->m_declaration->kind() == Declaration::Type && d->m_declaration->abstractType().cast() ) {
htmlClass();
}
if ( d->m_declaration->kind() == Declaration::Namespace ) {
modifyHtml() += i18n("namespace %1 ", identifierHighlight(d->m_declaration->qualifiedIdentifier().toString().toHtmlEscaped(), d->m_declaration));
} else if ( d->m_declaration->kind() == Declaration::NamespaceAlias ) {
modifyHtml() += identifierHighlight(declarationName(d->m_declaration).toHtmlEscaped(), d->m_declaration);
}
if(d->m_declaration->type()) {
EnumerationType::Ptr enumeration = d->m_declaration->type();
modifyHtml() += i18n("enumeration %1 ", identifierHighlight(d->m_declaration->identifier().toString().toHtmlEscaped(), d->m_declaration));
}
if(d->m_declaration->isForwardDeclaration()) {
ForwardDeclaration* forwardDec = static_cast(d->m_declaration.data());
Declaration* resolved = forwardDec->resolve(topContext().data());
if(resolved) {
modifyHtml() += i18n("(resolved forward-declaration: ");
makeLink(resolved->identifier().toString(), DeclarationPointer(resolved), NavigationAction::NavigateDeclaration );
modifyHtml() += i18n(") ");
}else{
modifyHtml() += i18n("(unresolved forward-declaration) ");
QualifiedIdentifier id = forwardDec->qualifiedIdentifier();
const auto& forwardDecFile = forwardDec->topContext()->parsingEnvironmentFile();
uint count;
const IndexedDeclaration* decls;
PersistentSymbolTable::self().declarations(id, count, decls);
for(uint a = 0; a < count; ++a) {
auto dec = decls[a].data();
if (!dec || dec->isForwardDeclaration()) {
continue;
}
const auto& decFile = forwardDec->topContext()->parsingEnvironmentFile();
if ((static_cast(decFile) != static_cast(forwardDecFile)) ||
(decFile && forwardDecFile && decFile->language() != forwardDecFile->language()))
{
// the language of the declarations must match
continue;
}
modifyHtml() += QStringLiteral("
");
makeLink(i18n("possible resolution from"), DeclarationPointer(dec), NavigationAction::NavigateDeclaration);
modifyHtml() += QLatin1Char(' ') + dec->url().str();
}
}
}
modifyHtml() += QStringLiteral("
");
}
}else{
AbstractType::Ptr showType = d->m_declaration->abstractType();
if(showType && showType.cast()) {
showType = showType.cast()->returnType();
if(showType)
modifyHtml() += labelHighlight(i18n("Returns: "));
}else if(showType) {
modifyHtml() += labelHighlight(i18n("Type: "));
}
if(showType) {
eventuallyMakeTypeLinks(showType);
modifyHtml() += QStringLiteral(" ");
}
}
QualifiedIdentifier identifier = d->m_declaration->qualifiedIdentifier();
if( identifier.count() > 1 ) {
if( d->m_declaration->context() && d->m_declaration->context()->owner() )
{
Declaration* decl = d->m_declaration->context()->owner();
FunctionDefinition* definition = dynamic_cast(decl);
if(definition && definition->declaration())
decl = definition->declaration();
if(decl->abstractType().cast())
modifyHtml() += labelHighlight(i18n("Enum: "));
else
modifyHtml() += labelHighlight(i18n("Container: "));
makeLink( declarationName(DeclarationPointer(decl)), DeclarationPointer(decl), NavigationAction::NavigateDeclaration );
modifyHtml() += QStringLiteral(" ");
} else {
QualifiedIdentifier parent = identifier;
parent.pop();
modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(parent.toString().toHtmlEscaped())));
}
}
if( shorten && !d->m_declaration->comment().isEmpty() ) {
QString comment = QString::fromUtf8(d->m_declaration->comment());
if( comment.length() > 60 ) {
comment.truncate(60);
comment += QLatin1String("...");
}
comment.replace(QLatin1Char('\n'), QLatin1Char(' '));
comment.replace(QLatin1String("
"), QLatin1String(" "));
comment.replace(QLatin1String("
"), QLatin1String(" "));
modifyHtml() += commentHighlight(comment.toHtmlEscaped()) + QLatin1String(" ");
}
QString access = stringFromAccess(d->m_declaration);
if( !access.isEmpty() )
modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(access.toHtmlEscaped())));
///@todo Enumerations
QString detailsHtml;
QStringList details = declarationDetails(d->m_declaration);
if( !details.isEmpty() ) {
bool first = true;
foreach( const QString &str, details ) {
if( !first )
detailsHtml += QLatin1String(", ");
first = false;
detailsHtml += propertyHighlight(str);
}
}
QString kind = declarationKind(d->m_declaration);
if( !kind.isEmpty() ) {
if( !detailsHtml.isEmpty() )
modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(kind.toHtmlEscaped()), detailsHtml));
else
modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(kind.toHtmlEscaped())));
}
if (d->m_declaration->isDeprecated()) {
modifyHtml() += labelHighlight(i18n("Status: %1 ", propertyHighlight(i18n("Deprecated"))));
}
modifyHtml() += QStringLiteral("
");
if(!shorten)
htmlAdditionalNavigation();
if( !shorten ) {
if(dynamic_cast(d->m_declaration.data()))
modifyHtml() += labelHighlight(i18n( "Def.: " ));
else
modifyHtml() += labelHighlight(i18n( "Decl.: " ));
makeLink( QStringLiteral("%1 :%2").arg( d->m_declaration->url().toUrl().fileName() ).arg( d->m_declaration->rangeInCurrentRevision().start().line()+1 ), d->m_declaration, NavigationAction::JumpToSource );
modifyHtml() += QStringLiteral(" ");
//modifyHtml() += "
";
if(!dynamic_cast(d->m_declaration.data())) {
if( FunctionDefinition* definition = FunctionDefinition::definition(d->m_declaration.data()) ) {
modifyHtml() += labelHighlight(i18n( " Def.: " ));
makeLink( QStringLiteral("%1 :%2").arg( definition->url().toUrl().fileName() ).arg( definition->rangeInCurrentRevision().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource );
}
}
if( FunctionDefinition* definition = dynamic_cast(d->m_declaration.data()) ) {
if(definition->declaration()) {
modifyHtml() += labelHighlight(i18n( " Decl.: " ));
makeLink( QStringLiteral("%1 :%2").arg( definition->declaration()->url().toUrl().fileName() ).arg( definition->declaration()->rangeInCurrentRevision().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource );
}
}
modifyHtml() += QStringLiteral(" "); //The action name _must_ stay "show_uses", since that is also used from outside
makeLink(i18n("Show uses"), QStringLiteral("show_uses"), NavigationAction(d->m_declaration, NavigationAction::NavigateUses));
}
QByteArray declarationComment = d->m_declaration->comment();
if( !shorten && (!declarationComment.isEmpty() || doc) ) {
modifyHtml() += QStringLiteral("");
if(doc) {
QString comment = doc->description();
connect(doc.data(), &IDocumentation::descriptionChanged, this, &AbstractDeclarationNavigationContext::contentsChanged);
if(!comment.isEmpty()) {
modifyHtml() += QLatin1String("
") + commentHighlight(comment) + QLatin1String("
");
}
}
QString comment = QString::fromUtf8(declarationComment);
if(!comment.isEmpty()) {
// if the first paragraph does not contain a tag, we assume that this is a plain-text comment
if (!Qt::mightBeRichText(comment)) {
// still might contain extra html tags for line breaks (this is the case for doxygen-style comments sometimes)
// let's protect them from being removed completely
comment.replace(QRegExp(QStringLiteral("
")), QStringLiteral("\n"));
comment = comment.toHtmlEscaped();
comment.replace(QLatin1Char('\n'), QLatin1String("
")); //Replicate newlines in html
}
modifyHtml() += commentHighlight(comment);
modifyHtml() += QStringLiteral("
");
}
}
+ if(!shorten) {
+ modifyHtml() += declarationSizeInformation(d->m_declaration);
+ }
+
if(!shorten && doc) {
modifyHtml() += QLatin1String("") + i18n("Show documentation for ");
makeLink(prettyQualifiedName(d->m_declaration),
d->m_declaration, NavigationAction::ShowDocumentation);
modifyHtml() += QStringLiteral("
");
}
//modifyHtml() += "
";
addExternalHtml(suffix());
modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("
");
return currentHtml();
}
AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(AbstractType::Ptr type) {
return type;
}
void AbstractDeclarationNavigationContext::htmlFunction()
{
const AbstractFunctionDeclaration* function = dynamic_cast(d->m_declaration.data());
Q_ASSERT(function);
const ClassFunctionDeclaration* classFunDecl = dynamic_cast(d->m_declaration.data());
const FunctionType::Ptr type = d->m_declaration->abstractType().cast();
if( !type ) {
modifyHtml() += errorHighlight(QStringLiteral("Invalid type
"));
return;
}
if( !classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor()) ) {
// only print return type for global functions and non-ctor/dtor methods
eventuallyMakeTypeLinks( type->returnType() );
}
modifyHtml() += QLatin1Char(' ') + identifierHighlight(prettyIdentifier(d->m_declaration).toString().toHtmlEscaped(), d->m_declaration);
if( type->indexedArgumentsSize() == 0 )
{
modifyHtml() += QStringLiteral("()");
} else {
modifyHtml() += QStringLiteral("( ");
bool first = true;
int firstDefaultParam = type->indexedArgumentsSize() - function->defaultParametersSize();
int currentArgNum = 0;
QVector decls;
if (DUContext* argumentContext = DUChainUtils::getArgumentContext(d->m_declaration.data())) {
decls = argumentContext->localDeclarations(topContext().data());
}
foreach(const AbstractType::Ptr& argType, type->arguments()) {
if( !first )
modifyHtml() += QStringLiteral(", ");
first = false;
eventuallyMakeTypeLinks( argType );
if (currentArgNum < decls.size()) {
modifyHtml() += QLatin1Char(' ') + identifierHighlight(decls[currentArgNum]->identifier().toString().toHtmlEscaped(), d->m_declaration);
}
if (currentArgNum >= firstDefaultParam) {
IndexedString defaultStr = function->defaultParameters()[currentArgNum - firstDefaultParam];
if (!defaultStr.isEmpty()) {
modifyHtml() += QLatin1String(" = ") + defaultStr.str().toHtmlEscaped();
}
}
++currentArgNum;
}
modifyHtml() += QStringLiteral(" )");
}
modifyHtml() += QStringLiteral("
");
}
Identifier AbstractDeclarationNavigationContext::prettyIdentifier(const 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();
}
QString AbstractDeclarationNavigationContext::prettyQualifiedName(const DeclarationPointer& decl) const
{
const auto qid = prettyQualifiedIdentifier(decl);
if (qid.isEmpty()) {
return i18nc("An anonymous declaration (class, function, etc.)", "");
}
return qid.toString();
}
void AbstractDeclarationNavigationContext::htmlAdditionalNavigation()
{
///Check if the function overrides or hides another one
const ClassFunctionDeclaration* classFunDecl = dynamic_cast(d->m_declaration.data());
if(classFunDecl) {
Declaration* overridden = DUChainUtils::getOverridden(d->m_declaration.data());
if(overridden) {
modifyHtml() += i18n("Overrides a ");
makeLink(i18n("function"), QStringLiteral("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), NavigationAction::NavigateDeclaration));
modifyHtml() += i18n(" from ");
makeLink(prettyQualifiedName(DeclarationPointer(overridden->context()->owner())),
QStringLiteral("jump_to_overridden_container"),
NavigationAction(DeclarationPointer(overridden->context()->owner()),
NavigationAction::NavigateDeclaration));
modifyHtml() += QStringLiteral("
");
}else{
//Check if this declarations hides other declarations
QList decls;
foreach(const DUContext::Import &import, d->m_declaration->context()->importedParentContexts())
if(import.context(topContext().data()))
decls += import.context(topContext().data())->findDeclarations(QualifiedIdentifier(d->m_declaration->identifier()),
CursorInRevision::invalid(), AbstractType::Ptr(), topContext().data(), DUContext::DontSearchInParent);
uint num = 0;
foreach(Declaration* decl, decls) {
modifyHtml() += i18n("Hides a ");
makeLink(i18n("function"), QStringLiteral("jump_to_hide_%1").arg(num),
NavigationAction(DeclarationPointer(decl),
NavigationAction::NavigateDeclaration));
modifyHtml() += i18n(" from ");
makeLink(prettyQualifiedName(DeclarationPointer(decl->context()->owner())),
QStringLiteral("jump_to_hide_container_%1").arg(num),
NavigationAction(DeclarationPointer(decl->context()->owner()),
NavigationAction::NavigateDeclaration));
modifyHtml() += QStringLiteral("
");
++num;
}
}
///Show all places where this function is overridden
if(classFunDecl->isVirtual()) {
Declaration* classDecl = d->m_declaration->context()->owner();
if(classDecl) {
uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint)-1 : 10;
QList overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps);
if(!overriders.isEmpty()) {
modifyHtml() += i18n("Overridden in ");
bool first = true;
foreach(Declaration* overrider, overriders) {
if(!first)
modifyHtml() += QStringLiteral(", ");
first = false;
const auto owner = DeclarationPointer(overrider->context()->owner());
const QString name = prettyQualifiedName(owner);
makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration));
}
modifyHtml() += QStringLiteral("
");
}
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 = d->m_fullBackwardSearch ? (uint)-1 : 10;
QList inheriters = DUChainUtils::getInheriters(d->m_declaration.data(), maxAllowedSteps);
if(!inheriters.isEmpty()) {
modifyHtml() += i18n("Inherited by ");
bool first = true;
foreach(Declaration* importer, inheriters) {
if(!first)
modifyHtml() += QStringLiteral(", ");
first = false;
const QString importerName = prettyQualifiedName(DeclarationPointer(importer));
makeLink(importerName, importerName,
NavigationAction(DeclarationPointer(importer), NavigationAction::NavigateDeclaration));
}
modifyHtml() += QStringLiteral("
");
}
if(maxAllowedSteps == 0)
createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n("More inheriters possible, show all"));
}
void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(const QString& string)
{
makeLink(string, QStringLiteral("m_fullBackwardSearch=true"), NavigationAction(QStringLiteral("m_fullBackwardSearch=true")));
modifyHtml() += QStringLiteral("
");
}
NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction( QString key )
{
if(key == QLatin1String("m_fullBackwardSearch=true")) {
d->m_fullBackwardSearch = true;
clear();
}
return NavigationContextPointer(this);
}
void AbstractDeclarationNavigationContext::htmlClass()
{
StructureType::Ptr klass = d->m_declaration->abstractType().cast();
Q_ASSERT(klass);
ClassDeclaration* classDecl = dynamic_cast(klass->declaration(topContext().data()));
if(classDecl) {
switch ( classDecl->classType() ) {
case ClassDeclarationData::Class:
modifyHtml() += QStringLiteral("class ");
break;
case ClassDeclarationData::Struct:
modifyHtml() += QStringLiteral("struct ");
break;
case ClassDeclarationData::Union:
modifyHtml() += QStringLiteral("union ");
break;
case ClassDeclarationData::Interface:
modifyHtml() += QStringLiteral("interface ");
break;
case ClassDeclarationData::Trait:
modifyHtml() += QStringLiteral("trait ");
break;
}
eventuallyMakeTypeLinks( klass.cast() );
FOREACH_FUNCTION( const BaseClassInstance& base, classDecl->baseClasses ) {
modifyHtml() += QLatin1String(", ") + stringFromAccess(base.access) + QLatin1Char(' ') + (base.virtualInheritance ? QStringLiteral("virtual") : QString()) + QLatin1Char(' ');
eventuallyMakeTypeLinks(base.baseClass.abstractType());
}
} else {
/// @todo How can we get here? and should this really be a class?
modifyHtml() += QStringLiteral("class ");
eventuallyMakeTypeLinks( klass.cast() );
}
modifyHtml() += QStringLiteral(" ");
}
void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType)
{
Q_ASSERT(type);
Q_ASSERT(idType);
if( Declaration* decl = idType->declaration(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(contextType.data());
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() += QStringLiteral("::").toHtmlEscaped();
}
}
//We leave out the * and & reference and pointer signs, those are added to the end
makeLink(id.toString() , DeclarationPointer(idType->declaration(topContext().data())), NavigationAction::NavigateDeclaration );
} else {
qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << topContext()->url().str();
modifyHtml() += typeHighlight(type->toString().toHtmlEscaped());
}
}
void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type )
{
type = typeToShow(type);
if( !type ) {
modifyHtml() += typeHighlight(QStringLiteral("").toHtmlEscaped());
return;
}
AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, topContext().data() );
const IdentifiedType* idType = dynamic_cast( target.data() );
qCDebug(LANGUAGE) << "making type-links for" << type->toString();
if( idType && idType->declaration(topContext().data()) ) {
///@todo This is C++ specific, move into subclass
if(target->modifiers() & AbstractType::ConstModifier)
modifyHtml() += typeHighlight(QStringLiteral("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(QStringLiteral("\\&|\\*"));
int suffixPos = typeSuffixString.indexOf(suffixExp);
if(suffixPos != -1)
modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos));
}
} else {
if(idType) {
qCDebug(LANGUAGE) << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect();
}
modifyHtml() += typeHighlight(type->toString().toHtmlEscaped());
}
}
DeclarationPointer AbstractDeclarationNavigationContext::declaration() const
{
return d->m_declaration;
}
QString AbstractDeclarationNavigationContext::identifierHighlight(const QString& identifier, const DeclarationPointer& decl) const
{
QString ret = nameHighlight(identifier);
if (!decl) {
return ret;
}
if (decl->isDeprecated()) {
ret = QStringLiteral("") + ret + QStringLiteral("");
}
return ret;
}
QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access)
{
switch(access) {
case Declaration::Private:
return QStringLiteral("private");
case Declaration::Protected:
return QStringLiteral("protected");
case Declaration::Public:
return QStringLiteral("public");
default:
break;
}
return QString();
}
QString AbstractDeclarationNavigationContext::stringFromAccess(const DeclarationPointer& decl)
{
const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data());
if( memberDecl ) {
return stringFromAccess(memberDecl->accessPolicy());
}
return QString();
}
QString AbstractDeclarationNavigationContext::declarationName( const DeclarationPointer& decl ) const
{
if( NamespaceAliasDeclaration* alias = dynamic_cast(decl.data()) ) {
if( alias->identifier().isEmpty() )
return QLatin1String("using namespace ") + alias->importIdentifier().toString();
else
return QLatin1String("namespace ") + alias->identifier().toString() + QLatin1String(" = ") + alias->importIdentifier().toString();
}
if( !decl )
return i18nc("A declaration that is unknown", "Unknown");
else
return prettyIdentifier(decl).toString();
}
QStringList AbstractDeclarationNavigationContext::declarationDetails(const DeclarationPointer& decl)
{
QStringList details;
const AbstractFunctionDeclaration* function = dynamic_cast(decl.data());
const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data());
if( memberDecl ) {
if( memberDecl->isMutable() )
details << QStringLiteral("mutable");
if( memberDecl->isRegister() )
details << QStringLiteral("register");
if( memberDecl->isStatic() )
details << QStringLiteral("static");
if( memberDecl->isAuto() )
details << QStringLiteral("auto");
if( memberDecl->isExtern() )
details << QStringLiteral("extern");
if( memberDecl->isFriend() )
details << QStringLiteral("friend");
}
if( decl->isDefinition() )
details << i18nc("tells if a declaration is defining the variable's value", "definition");
if( decl->isExplicitlyDeleted() )
details << QStringLiteral("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 << QStringLiteral("volatile");
}
if( function ) {
if( function->isInline() )
details << QStringLiteral("inline");
if( function->isExplicit() )
details << QStringLiteral("explicit");
if( function->isVirtual() )
details << QStringLiteral("virtual");
const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl.data());
if( classFunDecl ) {
if( classFunDecl->isSignal() )
details << QStringLiteral("signal");
if( classFunDecl->isSlot() )
details << QStringLiteral("slot");
if( classFunDecl->isFinal() )
details << QStringLiteral("final");
if( classFunDecl->isConstructor() )
details << QStringLiteral("constructor");
if( classFunDecl->isDestructor() )
details << QStringLiteral("destructor");
if( classFunDecl->isConversionFunction() )
details << QStringLiteral("conversion-function");
if( classFunDecl->isAbstract() )
details << QStringLiteral("abstract");
}
}
return details;
}
+QString AbstractDeclarationNavigationContext::declarationSizeInformation(const DeclarationPointer& decl)
+{
+ // Note that ClassMemberDeclaration also includes ClassDeclaration, which uses the sizeOf and alignOf fields,
+ // but normally leaves the bitOffsetOf unset (-1).
+ const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data());
+ if (memberDecl && (memberDecl->bitOffsetOf() > 0 || memberDecl->sizeOf() > 0 || memberDecl->alignOf() > 0)) {
+ QString sizeInfo = "";
+
+ if (memberDecl->bitOffsetOf() >= 0) {
+ const auto byteOffset = memberDecl->bitOffsetOf() / 8;
+ const auto bitOffset = memberDecl->bitOffsetOf() % 8;
+ const QString byteOffsetStr = i18np("1 Byte", "%1 Bytes", byteOffset);
+ const QString bitOffsetStr = bitOffset ? i18np("1 Bit", "%1 Bits", bitOffset) : QString();
+ sizeInfo += i18n("offset in parent: %1", bitOffset ? i18nc("%1: bytes, %2: bits", "%1, %2", byteOffsetStr, bitOffsetStr) : byteOffsetStr);
+ sizeInfo += "; ";
+ }
+
+ if (memberDecl->sizeOf() >= 0) {
+ sizeInfo += i18n("size: %1 Bytes", memberDecl->sizeOf());
+ sizeInfo += "; ";
+ }
+
+ if (memberDecl->alignOf() >= 0) {
+ sizeInfo += i18n("aligned to: %1 Bytes", memberDecl->alignOf());
+ }
+
+ sizeInfo += "
";
+
+ return sizeInfo;
+ }
+ return QString();
+}
+
}
diff --git a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.h b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.h
index d5e7e4a151..07e8141e9e 100644
--- a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.h
+++ b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.h
@@ -1,96 +1,97 @@
/*
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.
*/
#ifndef KDEVPLATFORM_ABSTRACTDECLARATIONNAVIGATIONCONTEXT_H
#define KDEVPLATFORM_ABSTRACTDECLARATIONNAVIGATIONCONTEXT_H
#include "abstractnavigationcontext.h"
#include "../declaration.h"
#include "../duchainpointer.h"
#include "../types/abstracttype.h"
namespace KDevelop {
class IdentifiedType;
class Identifier;
class QualifiedIdentifier;
class KDEVPLATFORMLANGUAGE_EXPORT AbstractDeclarationNavigationContext : public AbstractNavigationContext
{
Q_OBJECT
public:
AbstractDeclarationNavigationContext(const DeclarationPointer& decl, const TopDUContextPointer& topContext,
AbstractNavigationContext* previousContext = nullptr);
~AbstractDeclarationNavigationContext() override;
QString name() const override;
DeclarationPointer declaration() const;
///Execute an action. For example "show_uses" shows the uses of the declaration.
///Returns the context pointer for the new state.
NavigationContextPointer executeKeyAction(QString key) override;
QString html(bool shorten = false) override;
protected:
///Should returns a stripped version of the declarations qualified identifier, with all implicit/redundant parts removed
virtual QualifiedIdentifier prettyQualifiedIdentifier(DeclarationPointer decl) const;
///Returns a stripped version of the declarations identifier, using prettyQualifiedIdentifier
Identifier prettyIdentifier(const DeclarationPointer& decl) const;
/// @return String version of the qualified identifier of @p decl, "" on an invalid QID
QString prettyQualifiedName(const DeclarationPointer& decl) const;
/**
* Return a rich-text version of the identifier @p identifier representing the declaration @p decl
*
* @note In case @p declaration is deprecated, the resulting string will get a special formatting
*/
QString identifierHighlight(const QString& identifier, const DeclarationPointer& decl) const;
static QString stringFromAccess(Declaration::AccessPolicy access);
static QString stringFromAccess(const DeclarationPointer& decl);
QString declarationName( const DeclarationPointer& decl ) const;
static QStringList declarationDetails(const DeclarationPointer& decl);
+ static QString declarationSizeInformation(const DeclarationPointer& decl);
///This can be used for example to resolve typedefs within the type.
///All types that are visualized in the navigation-context are/should be mangled through this.
///The default-implementation returns the original type.
virtual AbstractType::Ptr typeToShow(AbstractType::Ptr type);
///Print the function-signature in a way that return-type and argument can be jumped to
virtual void htmlFunction();
///Navigation for additional less important links, like what function was overloaded etc.
virtual void htmlAdditionalNavigation();
virtual void htmlClass();
virtual void htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType);
///Creates and registers a link for the given type that jumps to its declaration and to the template-argument declarations
virtual void eventuallyMakeTypeLinks( KDevelop::AbstractType::Ptr type );
///Creates a link that triggers a recomputation of this context with m_fullBackwardSearch set to true
void createFullBackwardSearchLink(const QString& string);
private:
const QScopedPointer d;
};
}
#endif
diff --git a/plugins/clang/duchain/builder.cpp b/plugins/clang/duchain/builder.cpp
index f49b593a10..5d65cb9c54 100644
--- a/plugins/clang/duchain/builder.cpp
+++ b/plugins/clang/duchain/builder.cpp
@@ -1,1590 +1,1586 @@
/*
* This file is part of KDevelop
*
* Copyright 2013 Olivier de Gaalon
* Copyright 2015 Milian Wolff
*
* This library 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 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 "builder.h"
#include "util/clangdebug.h"
#include "templatehelpers.h"
#include "cursorkindtraits.h"
#include "clangducontext.h"
#include "macrodefinition.h"
#include "types/classspecializationtype.h"
#include "util/clangutils.h"
#include "util/clangtypes.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/// Turn on for debugging the declaration building
#define IF_DEBUG(x)
namespace {
// TODO: this is ugly, can we find a better alternative?
bool jsonTestRun()
{
static bool runningTest = qEnvironmentVariableIsSet("KDEV_CLANG_JSON_TEST_RUN");
return runningTest;
}
//BEGIN helpers
// HACK: current alias type template machinery is badly broken wrt spelling
// location, work around this by adjusting all references to point to child
// type alias node with proper location
// TODO: investigate upstream implementation of CXCursor_TypeAliasTemplateDecl
CXCursor findEmbeddedTypeAlias(CXCursor aliasTemplate)
{
auto result = clang_getNullCursor();
clang_visitChildren(aliasTemplate, [] (CXCursor cursor, CXCursor, CXClientData data) {
if (clang_getCursorKind(cursor) == CXCursor_TypeAliasDecl) {
auto res = reinterpret_cast(data);
*res = cursor;
return CXChildVisit_Break;
}
return CXChildVisit_Continue;
}, &result);
return result;
}
/**
* Find the cursor that cursor @p cursor references
*
* First tries to get the referenced cursor via clang_getCursorReferenced,
* and if that fails, tries to get them via clang_getOverloadedDecl
* (which returns the referenced cursor for CXCursor_OverloadedDeclRef, for example)
*
* @return Valid cursor on success, else null cursor
*/
CXCursor referencedCursor(CXCursor cursor)
{
auto referenced = clang_getCursorReferenced(cursor);
// HACK: see notes at getEmbeddedTypeAlias()
if (clang_getCursorKind(referenced) == CXCursor_TypeAliasTemplateDecl) {
return findEmbeddedTypeAlias(referenced);
}
if (!clang_equalCursors(cursor, referenced)) {
return referenced;
}
// get the first result for now
referenced = clang_getOverloadedDecl(cursor, 0);
if (!clang_Cursor_isNull(referenced)) {
return referenced;
}
return clang_getNullCursor();
}
Identifier makeId(CXCursor cursor)
{
if (CursorKindTraits::isClassTemplate(cursor.kind)) {
// TODO: how to handle functions here? We don't want to add the "real" function arguments here
// and there does not seem to be an API to get the template arguments for non-specializations easily
// NOTE: using the QString overload of the Identifier ctor here, so that the template name gets parsed
return Identifier(ClangString(clang_getCursorDisplayName(cursor)).toString());
}
return Identifier(ClangString(clang_getCursorSpelling(cursor)).toIndexed());
}
#if CINDEX_VERSION_MINOR >= 100 // FIXME https://bugs.llvm.org/show_bug.cgi?id=35333
QByteArray makeComment(CXComment comment)
{
if (Q_UNLIKELY(jsonTestRun())) {
auto kind = clang_Comment_getKind(comment);
if (kind == CXComment_Text)
return ClangString(clang_TextComment_getText(comment)).toByteArray();
QByteArray text;
int numChildren = clang_Comment_getNumChildren(comment);
for (int i = 0; i < numChildren; ++i)
text += makeComment(clang_Comment_getChild(comment, i));
return text;
}
return ClangString(clang_FullComment_getAsHTML(comment)).toByteArray();
}
#endif
AbstractType* createDelayedType(CXType type)
{
auto t = new DelayedType;
QString typeName = ClangString(clang_getTypeSpelling(type)).toString();
typeName.remove(QStringLiteral("const "));
typeName.remove(QStringLiteral("volatile "));
t->setIdentifier(IndexedTypeIdentifier(typeName));
return t;
}
void contextImportDecl(DUContext* context, const DeclarationPointer& decl)
{
auto top = context->topContext();
if (auto import = decl->logicalInternalContext(top)) {
context->addImportedParentContext(import);
context->topContext()->updateImportsCache();
}
}
//END helpers
CXChildVisitResult visitCursor(CXCursor cursor, CXCursor parent, CXClientData data);
//BEGIN IdType
template
struct IdType;
template
struct IdType::type>
{
typedef StructureType Type;
};
template
struct IdType::type>
{
typedef TypeAliasType Type;
};
template
struct IdType::type>
{
typedef TypeAliasType Type;
};
template
struct IdType::type>
{
typedef EnumerationType Type;
};
template
struct IdType::type>
{
typedef EnumeratorType Type;
};
//END IdType
//BEGIN DeclType
template
struct DeclType;
template
struct DeclType::type>
{
typedef Declaration Type;
};
template
struct DeclType::type>
{
typedef MacroDefinition Type;
};
template
struct DeclType::type>
{
typedef ForwardDeclaration Type;
};
template
struct DeclType::type>
{
typedef ClassDeclaration Type;
};
template
struct DeclType::type>
{
typedef ClassFunctionDeclaration Type;
};
template
struct DeclType::type>
{
typedef FunctionDeclaration Type;
};
template
struct DeclType::type>
{
typedef FunctionDefinition Type;
};
template
struct DeclType::type>
{
typedef NamespaceAliasDeclaration Type;
};
template
struct DeclType::type>
{
typedef ClassMemberDeclaration Type;
};
//END DeclType
//BEGIN CurrentContext
struct CurrentContext
{
CurrentContext(DUContext* context, QSet keepAliveContexts)
: context(context)
, keepAliveContexts(keepAliveContexts)
{
DUChainReadLocker lock;
previousChildContexts = context->childContexts();
previousChildDeclarations = context->localDeclarations();
}
~CurrentContext()
{
DUChainWriteLocker lock;
foreach (auto childContext, previousChildContexts) {
if (!keepAliveContexts.contains(childContext)) {
delete childContext;
}
}
qDeleteAll(previousChildDeclarations);
if (resortChildContexts) {
context->resortChildContexts();
}
if (resortLocalDeclarations) {
context->resortLocalDeclarations();
}
}
DUContext* context;
// when updating, this contains child contexts of the current parent context
QVector previousChildContexts;
// when updating, this contains contexts that must not be deleted
QSet keepAliveContexts;
// when updating, this contains child declarations of the current parent context
QVector previousChildDeclarations;
bool resortChildContexts = false;
bool resortLocalDeclarations = false;
};
//END CurrentContext
//BEGIN Visitor
struct Visitor
{
explicit Visitor(CXTranslationUnit tu, CXFile file,
const IncludeFileContexts& includes, const bool update);
AbstractType *makeType(CXType type, CXCursor parent);
AbstractType::Ptr makeAbsType(CXType type, CXCursor parent)
{
return AbstractType::Ptr(makeType(type, parent));
}
//BEGIN dispatch*
template = dummy>
CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent);
template = dummy>
CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent);
template = dummy>
CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent);
CXChildVisitResult dispatchTypeAliasTemplate(CXCursor cursor, CXCursor parent)
{
return CursorKindTraits::isClass(clang_getCursorKind(parent)) ? buildTypeAliasTemplateDecl(cursor)
: buildTypeAliasTemplateDecl(cursor);
}
template
AbstractType *dispatchType(CXType type, CXCursor cursor)
{
IF_DEBUG(clangDebug() << "TK:" << type.kind;)
auto kdevType = createType(type, cursor);
if (kdevType) {
setTypeModifiers(type, kdevType);
}
return kdevType;
}
//BEGIN dispatch*
//BEGIN build*
template
CXChildVisitResult buildDeclaration(CXCursor cursor);
template
CXChildVisitResult buildTypeAliasTemplateDecl(CXCursor cursor);
CXChildVisitResult buildUse(CXCursor cursor);
CXChildVisitResult buildMacroExpansion(CXCursor cursor);
template
CXChildVisitResult buildCompoundStatement(CXCursor cursor);
CXChildVisitResult buildCXXBaseSpecifier(CXCursor cursor);
CXChildVisitResult buildParmDecl(CXCursor cursor);
//END build*
//BEGIN create*
template
DeclType* createDeclarationCommon(CXCursor cursor, const Identifier& id)
{
auto range = ClangHelpers::cursorSpellingNameRange(cursor, id);
if (id.isEmpty()) {
// This is either an anonymous function parameter e.g.: void f(int);
// Or anonymous struct/class/union e.g.: struct {} anonymous;
// Set empty range for it
range.end = range.start;
}
// check if cursor is inside a macro expansion
auto clangRange = clang_Cursor_getSpellingNameRange(cursor, 0, 0);
unsigned int expansionLocOffset;
const auto spellingLocation = clang_getRangeStart(clangRange);
clang_getExpansionLocation(spellingLocation, nullptr, nullptr, nullptr, &expansionLocOffset);
if (m_macroExpansionLocations.contains(expansionLocOffset)) {
unsigned int spellingLocOffset;
clang_getSpellingLocation(spellingLocation, nullptr, nullptr, nullptr, &spellingLocOffset);
// Set empty ranges for declarations inside macro expansion
if (spellingLocOffset == expansionLocOffset) {
range.end = range.start;
}
}
if (m_update) {
const IndexedIdentifier indexedId(id);
DUChainWriteLocker lock;
auto it = m_parentContext->previousChildDeclarations.begin();
while (it != m_parentContext->previousChildDeclarations.end()) {
auto decl = dynamic_cast(*it);
if (decl && decl->indexedIdentifier() == indexedId) {
decl->setRange(range);
m_parentContext->resortLocalDeclarations = true;
setDeclData(cursor, decl);
m_cursorToDeclarationCache[cursor] = decl;
m_parentContext->previousChildDeclarations.erase(it);
return decl;
}
++it;
}
}
auto decl = new DeclType(range, nullptr);
decl->setIdentifier(id);
m_cursorToDeclarationCache[cursor] = decl;
setDeclData(cursor, decl);
{
DUChainWriteLocker lock;
decl->setContext(m_parentContext->context);
}
return decl;
}
template
Declaration* createDeclaration(CXCursor cursor, const Identifier& id, DUContext *context)
{
auto decl = createDeclarationCommon(cursor, id);
auto type = createType(cursor);
DUChainWriteLocker lock;
if (context)
decl->setInternalContext(context);
setDeclType(decl, type);
setDeclInCtxtData(cursor, decl);
return decl;
}
template
DUContext* createContext(CXCursor cursor, const QualifiedIdentifier& scopeId = {})
{
// wtf: why is the DUContext API requesting a QID when it needs a plain Id?!
// see: testNamespace
auto range = ClangRange(clang_getCursorExtent(cursor)).toRangeInRevision();
DUChainWriteLocker lock;
if (m_update) {
const IndexedQualifiedIdentifier indexedScopeId(scopeId);
auto it = m_parentContext->previousChildContexts.begin();
while (it != m_parentContext->previousChildContexts.end()) {
auto ctx = *it;
if (ctx->type() == Type && ctx->indexedLocalScopeIdentifier() == indexedScopeId) {
ctx->setRange(range);
m_parentContext->resortChildContexts = true;
m_parentContext->previousChildContexts.erase(it);
return ctx;
}
++it;
}
}
//TODO: (..type, id..) constructor for DUContext?
auto context = new ClangNormalDUContext(range, m_parentContext->context);
context->setType(Type);
context->setLocalScopeIdentifier(scopeId);
if (Type == DUContext::Other || Type == DUContext::Function)
context->setInSymbolTable(false);
if (CK == CXCursor_CXXMethod) {
CXCursor semParent = clang_getCursorSemanticParent(cursor);
// only import the semantic parent if it differs from the lexical parent
if (!clang_Cursor_isNull(semParent) && !clang_equalCursors(semParent, clang_getCursorLexicalParent(cursor))) {
auto semParentDecl = findDeclaration(semParent);
if (semParentDecl) {
contextImportDecl(context, semParentDecl);
}
}
}
return context;
}
template = dummy>
AbstractType *createType(CXType, CXCursor)
{
// TODO: would be nice to instantiate a ConstantIntegralType here and set a value if possible
// but unfortunately libclang doesn't offer API to that
// also see http://marc.info/?l=cfe-commits&m=131609142917881&w=2
return new IntegralType(CursorKindTraits::integralType(TK));
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto ptr = new PointerType;
ptr->setBaseType(makeAbsType(clang_getPointeeType(type), parent));
return ptr;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto arr = new ArrayType;
arr->setDimension((TK == CXType_IncompleteArray || TK == CXType_VariableArray || TK == CXType_DependentSizedArray) ? 0 : clang_getArraySize(type));
arr->setElementType(makeAbsType(clang_getArrayElementType(type), parent));
return arr;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto ref = new ReferenceType;
ref->setIsRValue(type.kind == CXType_RValueReference);
ref->setBaseType(makeAbsType(clang_getPointeeType(type), parent));
return ref;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto func = new FunctionType;
func->setReturnType(makeAbsType(clang_getResultType(type), parent));
const int numArgs = clang_getNumArgTypes(type);
for (int i = 0; i < numArgs; ++i) {
func->addArgument(makeAbsType(clang_getArgType(type, i), parent));
}
if (clang_isFunctionTypeVariadic(type)) {
auto type = new DelayedType;
static const auto id = IndexedTypeIdentifier(QStringLiteral("..."));
type->setIdentifier(id);
type->setKind(DelayedType::Unresolved);
func->addArgument(AbstractType::Ptr(type));
}
return func;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
DeclarationPointer decl = findDeclaration(clang_getTypeDeclaration(type));
DUChainReadLocker lock;
if (!decl) {
// probably a forward-declared type
decl = ClangHelpers::findForwardDeclaration(type, m_parentContext->context, parent);
}
if (clang_Type_getNumTemplateArguments(type) != -1) {
return createClassTemplateSpecializationType(type, decl);
}
auto t = new StructureType;
if (decl) {
t->setDeclaration(decl.data());
} else { // fallback, at least give the spelling to the user
t->setDeclarationId(DeclarationId(IndexedQualifiedIdentifier(QualifiedIdentifier(ClangString(clang_getTypeSpelling(type)).toString()))));
}
return t;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor)
{
auto t = new EnumerationType;
setIdTypeDecl(clang_getTypeDeclaration(type), t);
return t;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto t = new TypeAliasType;
CXCursor location = clang_getTypeDeclaration(type);
t->setType(makeAbsType(clang_getTypedefDeclUnderlyingType(location), parent));
setIdTypeDecl(location, t);
return t;
}
template = dummy>
AbstractType *createType(CXType, CXCursor /*parent*/)
{
auto t = new DelayedType;
static const IndexedTypeIdentifier id(QLatin1String(CursorKindTraits::delayedTypeName(TK)));
t->setIdentifier(id);
return t;
}
template = dummy>
AbstractType *createType(CXType type, CXCursor /*parent*/)
{
return createDelayedType(type);
}
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto numTA = clang_Type_getNumTemplateArguments(type);
// TODO: We should really expose more types to libclang!
if (numTA != -1 && ClangString(clang_getTypeSpelling(type)).toString().contains(QLatin1Char('<'))) {
return createClassTemplateSpecializationType(type);
}
// Maybe it's the ElaboratedType. E.g.: "struct Type foo();" or "NS::Type foo();" or "void foo(enum Enum e);" e.t.c.
auto oldType = type;
type = clang_getCanonicalType(type);
bool isElaboratedType = type.kind != CXType_FunctionProto && type.kind != CXType_FunctionNoProto && type.kind != CXType_Unexposed && type.kind != CXType_Invalid && type.kind != CXType_Record;
return !isElaboratedType ? createDelayedType(oldType) : makeType(type, parent);
}
template = dummy>
typename IdType::Type *createType(CXCursor)
{
return new typename IdType::Type;
}
template = dummy>
EnumeratorType *createType(CXCursor cursor)
{
auto type = new EnumeratorType;
type->setValue(clang_getEnumConstantDeclUnsignedValue(cursor));
return type;
}
template = dummy>
TypeAliasType *createType(CXCursor cursor)
{
auto type = new TypeAliasType;
type->setType(makeAbsType(clang_getTypedefDeclUnderlyingType(cursor), cursor));
return type;
}
template = dummy>
AbstractType* createType(CXCursor cursor)
{
auto clangType = clang_getCursorType(cursor);
#if CINDEX_VERSION_MINOR < 31
if (clangType.kind == CXType_Unexposed) {
// Clang sometimes can return CXType_Unexposed for CXType_FunctionProto kind. E.g. if it's AttributedType.
return dispatchType(clangType, cursor);
}
#endif
return makeType(clangType, cursor);
}
template = dummy>
AbstractType *createType(CXCursor)
{
auto t = new DelayedType;
static const IndexedTypeIdentifier id(QStringLiteral("Label"));
t->setIdentifier(id);
return t;
}
template = dummy>
AbstractType *createType(CXCursor cursor)
{
auto clangType = clang_getCursorType(cursor);
return makeType(clangType, cursor);
}
#if CINDEX_VERSION_MINOR >= 32
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto deducedType = clang_getCanonicalType(type);
bool isDeduced = deducedType.kind != CXType_Invalid && deducedType.kind != CXType_Auto;
return !isDeduced ? createDelayedType(type) : makeType(deducedType, parent);
}
#endif
#if CINDEX_VERSION_MINOR >= 34
template = dummy>
AbstractType *createType(CXType type, CXCursor parent)
{
auto underyingType = clang_Type_getNamedType(type);
return makeType(underyingType, parent);
}
#endif
/// @param declaration an optional declaration that will be associated with created type
AbstractType* createClassTemplateSpecializationType(CXType type, const DeclarationPointer& declaration = {})
{
auto numTA = clang_Type_getNumTemplateArguments(type);
Q_ASSERT(numTA != -1);
auto typeDecl = clang_getTypeDeclaration(type);
if (!declaration && typeDecl.kind == CXCursor_NoDeclFound) {
// clang_getTypeDeclaration doesn't handle all types, fall back to delayed type...
return createDelayedType(type);
}
QStringList typesStr;
QString tStr = ClangString(clang_getTypeSpelling(type)).toString();
ParamIterator iter(QStringLiteral("<>"), tStr);
while (iter) {
typesStr.append(*iter);
++iter;
}
auto cst = new ClassSpecializationType;
for (int i = 0; i < numTA; i++) {
auto argumentType = clang_Type_getTemplateArgumentAsType(type, i);
AbstractType::Ptr currentType;
if (argumentType.kind == CXType_Invalid) {
if(i >= typesStr.size()){
currentType = createDelayedType(argumentType);
} else {
auto t = new DelayedType;
t->setIdentifier(IndexedTypeIdentifier(typesStr[i]));
currentType = t;
}
} else {
currentType = makeType(argumentType, typeDecl);
}
if (currentType) {
cst->addParameter(currentType->indexed());
}
}
auto decl = declaration ? declaration : findDeclaration(typeDecl);
DUChainReadLocker lock;
if (decl) {
cst->setDeclaration(decl.data());
} else { // fallback, at least give the spelling to the user
Identifier id(tStr);
id.clearTemplateIdentifiers();
cst->setDeclarationId(DeclarationId(IndexedQualifiedIdentifier(QualifiedIdentifier(id))));
}
return cst;
}
//END create*
//BEGIN setDeclData
template
void setDeclData(CXCursor cursor, Declaration *decl, bool setComment = true) const;
template
void setDeclData(CXCursor cursor, MacroDefinition* decl) const;
template
void setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const;
template = dummy>
void setDeclData(CXCursor cursor, ClassDeclaration* decl) const;
template = dummy>
void setDeclData(CXCursor cursor, ClassDeclaration* decl) const;
template
void setDeclData(CXCursor cursor, AbstractFunctionDeclaration* decl) const;
template
void setDeclData(CXCursor cursor, ClassFunctionDeclaration* decl) const;
template
void setDeclData(CXCursor cursor, FunctionDeclaration *decl, bool setComment = true) const;
template
void setDeclData(CXCursor cursor, FunctionDefinition *decl) const;
template
void setDeclData(CXCursor cursor, NamespaceAliasDeclaration *decl) const;
//END setDeclData
//BEGIN setDeclInCtxtData
template
void setDeclInCtxtData(CXCursor, Declaration*)
{
//No-op
}
template
void setDeclInCtxtData(CXCursor cursor, ClassFunctionDeclaration *decl)
{
// HACK to retrieve function-constness
// This looks like a bug in Clang -- In theory setTypeModifiers should take care of setting the const modifier
// however, clang_isConstQualifiedType() for TK == CXType_FunctionProto always returns false
// TODO: Debug further
auto type = decl->abstractType();
if (type) {
if (clang_CXXMethod_isConst(cursor)) {
type->setModifiers(type->modifiers() | AbstractType::ConstModifier);
decl->setAbstractType(type);
}
}
}
template
void setDeclInCtxtData(CXCursor cursor, FunctionDefinition *def)
{
setDeclInCtxtData(cursor, static_cast(def));
const CXCursor canon = clang_getCanonicalCursor(cursor);
if (auto decl = findDeclaration(canon)) {
def->setDeclaration(decl.data());
}
}
//END setDeclInCtxtData
//BEGIN setDeclType
template
void setDeclType(Declaration *decl, typename IdType::Type *type)
{
setDeclType(decl, static_cast(type));
setDeclType(decl, static_cast(type));
}
template
void setDeclType(Declaration *decl, IdentifiedType *type)
{
type->setDeclaration(decl);
}
template
void setDeclType(Declaration *decl, AbstractType *type)
{
decl->setAbstractType(AbstractType::Ptr(type));
}
//END setDeclType
template
void setTypeModifiers(CXType type, AbstractType* kdevType) const;
const CXFile m_file;
const IncludeFileContexts &m_includes;
DeclarationPointer findDeclaration(CXCursor cursor) const;
void setIdTypeDecl(CXCursor typeCursor, IdentifiedType* idType) const;
std::unordered_map> m_uses;
/// At these location offsets (cf. @ref clang_getExpansionLocation) we encountered macro expansions
QSet m_macroExpansionLocations;
mutable QHash m_cursorToDeclarationCache;
CurrentContext *m_parentContext;
const bool m_update;
};
//BEGIN setTypeModifiers
template
void Visitor::setTypeModifiers(CXType type, AbstractType* kdevType) const
{
quint64 modifiers = 0;
if (clang_isConstQualifiedType(type)) {
modifiers |= AbstractType::ConstModifier;
}
if (clang_isVolatileQualifiedType(type)) {
modifiers |= AbstractType::VolatileModifier;
}
if (TK == CXType_Short || TK == CXType_UShort) {
modifiers |= AbstractType::ShortModifier;
}
if (TK == CXType_Long || TK == CXType_LongDouble || TK == CXType_ULong) {
modifiers |= AbstractType::LongModifier;
}
if (TK == CXType_LongLong || TK == CXType_ULongLong) {
modifiers |= AbstractType::LongLongModifier;
}
if (TK == CXType_SChar) {
modifiers |= AbstractType::SignedModifier;
}
if (TK == CXType_UChar || TK == CXType_UInt || TK == CXType_UShort
|| TK == CXType_UInt128 || TK == CXType_ULong || TK == CXType_ULongLong)
{
modifiers |= AbstractType::UnsignedModifier;
}
kdevType->setModifiers(modifiers);
}
//END setTypeModifiers
//BEGIN dispatchCursor
template>
CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent)
{
const bool decision = CursorKindTraits::isClass(clang_getCursorKind(parent));
return decision ?
dispatchCursor(cursor, parent) :
dispatchCursor(cursor, parent);
}
template>
CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent)
{
IF_DEBUG(clangDebug() << "IsInClass:" << IsInClass << "- isDefinition:" << IsDefinition;)
const bool isDefinition = clang_isCursorDefinition(cursor);
return isDefinition ?
dispatchCursor(cursor, parent) :
dispatchCursor(cursor, parent);
}
template>
CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent)
{
IF_DEBUG(clangDebug() << "IsInClass:" << IsInClass << "- isDefinition:" << IsDefinition;)
// We may end up visiting the same cursor twice in some cases
// see discussion on https://git.reviewboard.kde.org/r/119526/
// TODO: Investigate why this is happening in libclang
if ((CursorKindTraits::isClass(CK) || CK == CXCursor_EnumDecl) &&
clang_getCursorKind(parent) == CXCursor_VarDecl) {
return CXChildVisit_Continue;
}
constexpr bool isClassMember = IsInClass == Decision::True;
constexpr bool isDefinition = IsDefinition == Decision::True;
// always build a context for class templates and functions, otherwise we "leak"
// the function/template parameter declarations into the surrounding context,
// which can lead to interesting bugs, like https://bugs.kde.org/show_bug.cgi?id=368067
constexpr bool hasContext = isDefinition || CursorKindTraits::isFunction(CK) || CursorKindTraits::isClassTemplate(CK);
return buildDeclaration::Type, hasContext>(cursor);
}
//END dispatchCursor
//BEGIN setDeclData
template
void Visitor::setDeclData(CXCursor cursor, Declaration *decl, bool setComment) const
{
if (setComment)
#if CINDEX_VERSION_MINOR < 100 // FIXME https://bugs.llvm.org/show_bug.cgi?id=35333
decl->setComment(KDevelop::formatComment(ClangString(clang_Cursor_getRawCommentText(cursor)).toByteArray()));
#else
decl->setComment(makeComment(clang_Cursor_getParsedComment(cursor)));
#endif
if (CursorKindTraits::isAliasType(CK)) {
decl->setIsTypeAlias(true);
}
if (CK == CXCursor_Namespace)
decl->setKind(Declaration::Namespace);
if (CK == CXCursor_EnumDecl || CK == CXCursor_EnumConstantDecl || CursorKindTraits::isClass(CK) || CursorKindTraits::isAliasType(CK))
decl->setKind(Declaration::Type);
int isAlwaysDeprecated;
clang_getCursorPlatformAvailability(cursor, &isAlwaysDeprecated, nullptr, nullptr, nullptr, nullptr, 0);
decl->setDeprecated(isAlwaysDeprecated);
}
template
void Visitor::setDeclData(CXCursor cursor, MacroDefinition* decl) const
{
setDeclData(cursor, static_cast(decl));
if (m_update) {
decl->clearParameters();
}
auto unit = clang_Cursor_getTranslationUnit(cursor);
auto range = clang_getCursorExtent(cursor);
// TODO: Quite lacking API in libclang here.
// No way to find out if this macro is function-like or not
// cf. http://clang.llvm.org/doxygen/classclang_1_1MacroInfo.html
// And no way to get the actual definition text range
// Should be quite easy to expose that in libclang, though
// Let' still get some basic support for this and parse on our own, it's not that difficult
const QString contents = QString::fromUtf8(ClangUtils::getRawContents(unit, range));
const int firstOpeningParen = contents.indexOf(QLatin1Char('('));
const int firstWhitespace = contents.indexOf(QLatin1Char(' '));
const bool isFunctionLike = (firstOpeningParen != -1) && (firstOpeningParen < firstWhitespace);
decl->setFunctionLike(isFunctionLike);
// now extract the actual definition text
int start = -1;
if (isFunctionLike) {
const int closingParen = findClose(contents, firstOpeningParen);
if (closingParen != -1) {
start = closingParen + 2; // + ')' + ' '
// extract macro function parameters
const QString parameters = contents.mid(firstOpeningParen, closingParen - firstOpeningParen + 1);
ParamIterator paramIt(QStringLiteral("():"), parameters, 0);
while (paramIt) {
decl->addParameter(IndexedString(*paramIt));
++paramIt;
}
}
} else {
start = firstWhitespace + 1; // + ' '
}
if (start == -1) {
// unlikely: invalid macro definition, insert the complete #define statement
decl->setDefinition(IndexedString(QLatin1String("#define ") + contents));
} else if (start < contents.size()) {
decl->setDefinition(IndexedString(contents.mid(start)));
} // else: macro has no body => leave the definition text empty
}
template
void Visitor::setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const
{
setDeclData(cursor, static_cast(decl));
//A CXCursor_VarDecl in a class is static (otherwise it'd be a CXCursor_FieldDecl)
if (CK == CXCursor_VarDecl)
decl->setStatic(true);
decl->setAccessPolicy(CursorKindTraits::kdevAccessPolicy(clang_getCXXAccessSpecifier(cursor)));
#if CINDEX_VERSION_MINOR >= 32
decl->setMutable(clang_CXXField_isMutable(cursor));
#endif
#if CINDEX_VERSION_MINOR >= 30
- if (!jsonTestRun()) {
- auto offset = clang_Cursor_getOffsetOfField(cursor);
- if (offset >= 0) { // don't add this info to the json tests, it invalidates the comment structure
- auto type = clang_getCursorType(cursor);
- auto sizeOf = clang_Type_getSizeOf(type);
- auto alignedTo = clang_Type_getAlignOf(type);
- const auto byteOffset = offset / 8;
- const auto bitOffset = offset % 8;
- const QString byteOffsetStr = i18np("1 Byte", "%1 Bytes", byteOffset);
- const QString bitOffsetStr = bitOffset ? i18np("1 Bit", "%1 Bits", bitOffset) : QString();
- const QString offsetStr = bitOffset ? i18nc("%1: bytes, %2: bits", "%1, %2", byteOffsetStr, bitOffsetStr) : byteOffsetStr;
-
- decl->setComment(decl->comment()
- + i18n("offset in parent: %1; "
- "size: %2 Bytes; "
- "aligned to: %3 Bytes
", offsetStr, sizeOf, alignedTo).toUtf8());
- }
+ auto offset = clang_Cursor_getOffsetOfField(cursor);
+ if (offset >= 0) { // don't add this info to the json tests, it invalidates the comment structure
+ auto type = clang_getCursorType(cursor);
+ auto sizeOf = clang_Type_getSizeOf(type);
+ auto alignOf = clang_Type_getAlignOf(type);
+
+ if (sizeOf >= 0)
+ decl->setSizeOf(sizeOf);
+ if (offset >= 0)
+ decl->setBitOffsetOf(offset);
+ if (alignOf >= 0)
+ decl->setAlignOf(alignOf);
}
#endif
}
template>
void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const
{
CXCursorKind kind = clang_getTemplateCursorKind(cursor);
switch (kind) {
case CXCursor_UnionDecl: setDeclData(cursor, decl); break;
case CXCursor_StructDecl: setDeclData(cursor, decl); break;
case CXCursor_ClassDecl: setDeclData(cursor, decl); break;
default: Q_ASSERT(false); break;
}
}
template>
void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const
{
if (m_update) {
decl->clearBaseClasses();
}
setDeclData(cursor, static_cast(decl));
if (CK == CXCursor_UnionDecl)
decl->setClassType(ClassDeclarationData::Union);
if (CK == CXCursor_StructDecl)
decl->setClassType(ClassDeclarationData::Struct);
if (clang_isCursorDefinition(cursor)) {
decl->setDeclarationIsDefinition(true);
}
- if (!jsonTestRun()) { // don't add this info to the json tests, it invalidates the comment structure
- auto type = clang_getCursorType(cursor);
- auto sizeOf = clang_Type_getSizeOf(type);
- auto alignOf = clang_Type_getAlignOf(type);
- if (sizeOf >= 0 && alignOf >= 0) {
- decl->setComment(decl->comment()
- + i18n("size: %1 Bytes; "
- "aligned to: %2 Bytes
", sizeOf, alignOf).toUtf8());
- }
- }
+
+#if CINDEX_VERSION_MINOR >= 30
+ auto type = clang_getCursorType(cursor);
+ auto sizeOf = clang_Type_getSizeOf(type);
+ auto alignOf = clang_Type_getAlignOf(type);
+
+ if (sizeOf >= 0)
+ decl->setSizeOf(sizeOf);
+ if (alignOf >= 0)
+ decl->setAlignOf(alignOf);
+#endif
}
template
void Visitor::setDeclData(CXCursor cursor, AbstractFunctionDeclaration* decl) const
{
if (m_update) {
decl->clearDefaultParameters();
}
// No setDeclData(...) here: AbstractFunctionDeclaration is an interface
// TODO: Can we get the default arguments directly from Clang?
// also see http://clang-developers.42468.n3.nabble.com/Finding-default-value-for-function-argument-with-clang-c-API-td4036919.html
const QVector defaultArgs = ClangUtils::getDefaultArguments(cursor, ClangUtils::MinimumSize);
foreach (const QString& defaultArg, defaultArgs) {
decl->addDefaultParameter(IndexedString(defaultArg));
}
}
template
void Visitor::setDeclData(CXCursor cursor, ClassFunctionDeclaration* decl) const
{
setDeclData(cursor, static_cast(decl));
setDeclData(cursor, static_cast(decl));
decl->setIsAbstract(clang_CXXMethod_isPureVirtual(cursor));
decl->setStatic(clang_CXXMethod_isStatic(cursor));
decl->setVirtual(clang_CXXMethod_isVirtual(cursor));
// TODO: Set flags in one go? (needs new API in kdevplatform)
const auto attributes = ClangUtils::specialAttributes(cursor);
decl->setIsSignal(attributes & FunctionSignalFlag);
decl->setIsSlot(attributes & FunctionSlotFlag);
decl->setIsFinal(attributes & FinalFunctionFlag);
}
template
void Visitor::setDeclData(CXCursor cursor, FunctionDeclaration *decl, bool setComment) const
{
setDeclData(cursor, static_cast(decl));
setDeclData(cursor, static_cast(decl), setComment);
}
template
void Visitor::setDeclData(CXCursor cursor, FunctionDefinition *decl) const
{
bool setComment = clang_equalCursors(clang_getCanonicalCursor(cursor), cursor);
setDeclData(cursor, static_cast(decl), setComment);
}
template
void Visitor::setDeclData(CXCursor cursor, NamespaceAliasDeclaration *decl) const
{
setDeclData(cursor, static_cast(decl));
clang_visitChildren(cursor, [] (CXCursor cursor, CXCursor parent, CXClientData data) -> CXChildVisitResult {
if (clang_getCursorKind(cursor) == CXCursor_NamespaceRef) {
const auto id = QualifiedIdentifier(ClangString(clang_getCursorSpelling(cursor)).toString());
reinterpret_cast(data)->setImportIdentifier(id);
return CXChildVisit_Break;
} else {
return visitCursor(cursor, parent, data);
}
}, decl);
}
//END setDeclData
//BEGIN build*
template
CXChildVisitResult Visitor::buildDeclaration(CXCursor cursor)
{
auto id = makeId(cursor);
if (CK == CXCursor_UnexposedDecl && id.isEmpty()) {
// skip unexposed declarations that have no identifier set
// this is useful to skip e.g. friend declarations
return CXChildVisit_Recurse;
}
IF_DEBUG(clangDebug() << "id:" << id << "- CK:" << CK << "- DeclType:" << typeid(DeclType).name() << "- hasContext:" << hasContext;)
// Code path for class declarations that may be defined "out-of-line", e.g.
// "SomeNameSpace::SomeClass {};"
QScopedPointer helperContext;
if (CursorKindTraits::isClass(CK) || CursorKindTraits::isFunction(CK)) {
const auto lexicalParent = clang_getCursorLexicalParent(cursor);
const auto semanticParent = clang_getCursorSemanticParent(cursor);
const bool isOutOfLine = !clang_equalCursors(lexicalParent, semanticParent);
if (isOutOfLine) {
const QString scope = ClangUtils::getScope(cursor);
auto context = createContext(cursor, QualifiedIdentifier(scope));
helperContext.reset(new CurrentContext(context, m_parentContext->keepAliveContexts));
}
}
// if helperContext is null, this is a no-op
PushValue pushCurrent(m_parentContext, helperContext.isNull() ? m_parentContext : helperContext.data());
if (hasContext) {
auto context = createContext(cursor, QualifiedIdentifier(id));
createDeclaration(cursor, id, context);
CurrentContext newParent(context, m_parentContext->keepAliveContexts);
PushValue pushCurrent(m_parentContext, &newParent);
clang_visitChildren(cursor, &visitCursor, this);
return CXChildVisit_Continue;
}
createDeclaration(cursor, id, nullptr);
return CXChildVisit_Recurse;
}
CXChildVisitResult Visitor::buildParmDecl(CXCursor cursor)
{
return buildDeclaration::Type, false>(cursor);
}
CXChildVisitResult Visitor::buildUse(CXCursor cursor)
{
m_uses[m_parentContext->context].push_back(cursor);
return cursor.kind == CXCursor_DeclRefExpr || cursor.kind == CXCursor_MemberRefExpr ?
CXChildVisit_Recurse : CXChildVisit_Continue;
}
CXChildVisitResult Visitor::buildMacroExpansion(CXCursor cursor)
{
buildUse(cursor);
// cache that we encountered a macro expansion at this location
unsigned int offset;
clang_getSpellingLocation(clang_getCursorLocation(cursor), nullptr, nullptr, nullptr, &offset);
m_macroExpansionLocations << offset;
return CXChildVisit_Recurse;
}
template
CXChildVisitResult Visitor::buildCompoundStatement(CXCursor cursor)
{
if (CK == CXCursor_LambdaExpr || m_parentContext->context->type() == DUContext::Function)
{
auto context = createContext(cursor);
CurrentContext newParent(context, m_parentContext->keepAliveContexts);
PushValue pushCurrent(m_parentContext, &newParent);
clang_visitChildren(cursor, &visitCursor, this);
return CXChildVisit_Continue;
}
return CXChildVisit_Recurse;
}
CXChildVisitResult Visitor::buildCXXBaseSpecifier(CXCursor cursor)
{
auto currentContext = m_parentContext->context;
bool virtualInherited = clang_isVirtualBase(cursor);
Declaration::AccessPolicy access = CursorKindTraits::kdevAccessPolicy(clang_getCXXAccessSpecifier(cursor));
auto classDeclCursor = clang_getCursorReferenced(cursor);
auto decl = findDeclaration(classDeclCursor);
if (!decl) {
// this happens for templates with template-dependent base classes e.g. - dunno whether we can/should do more here
clangDebug() << "failed to find declaration for base specifier:" << clang_getCursorDisplayName(cursor);
return CXChildVisit_Recurse;
}
DUChainWriteLocker lock;
contextImportDecl(currentContext, decl);
auto classDecl = dynamic_cast(currentContext->owner());
Q_ASSERT(classDecl);
classDecl->addBaseClass({decl->indexedType(), access, virtualInherited});
return CXChildVisit_Recurse;
}
template
CXChildVisitResult Visitor::buildTypeAliasTemplateDecl(CXCursor cursor)
{
auto aliasDecl = findEmbeddedTypeAlias(cursor);
// NOTE: using aliasDecl here averts having to add a workaround to makeId()
auto id = makeId(aliasDecl);
// create template context to prevent leaking child template params
auto context = createContext(cursor, QualifiedIdentifier(id));
using DeclType = typename DeclType::Type;
createDeclaration(aliasDecl, id, context);
CurrentContext newParent(context, m_parentContext->keepAliveContexts);
PushValue pushCurrent(m_parentContext, &newParent);
clang_visitChildren(cursor, [] (CXCursor cursor, CXCursor parent, CXClientData data) {
// NOTE: immediately recurse into embedded alias decl
return clang_getCursorKind(cursor) == CXCursor_TypeAliasDecl ?
CXChildVisit_Recurse : visitCursor(cursor, parent, data);
}, this);
return CXChildVisit_Continue;
}
//END build*
DeclarationPointer Visitor::findDeclaration(CXCursor cursor) const
{
const auto it = m_cursorToDeclarationCache.constFind(cursor);
if (it != m_cursorToDeclarationCache.constEnd()) {
return *it;
}
// fallback, and cache result
auto decl = ClangHelpers::findDeclaration(cursor, m_includes);
m_cursorToDeclarationCache.insert(cursor, decl);
return decl;
}
void Visitor::setIdTypeDecl(CXCursor typeCursor, IdentifiedType* idType) const
{
DeclarationPointer decl = findDeclaration(typeCursor);
DUChainReadLocker lock;
if (decl) {
idType->setDeclaration(decl.data());
}
}
AbstractType *Visitor::makeType(CXType type, CXCursor parent)
{
#define UseKind(TypeKind) case TypeKind: return dispatchType(type, parent)
switch (type.kind) {
UseKind(CXType_Void);
UseKind(CXType_Bool);
UseKind(CXType_Short);
UseKind(CXType_UShort);
UseKind(CXType_Int);
UseKind(CXType_UInt);
UseKind(CXType_Long);
UseKind(CXType_ULong);
UseKind(CXType_LongLong);
UseKind(CXType_ULongLong);
UseKind(CXType_Float);
UseKind(CXType_LongDouble);
UseKind(CXType_Double);
UseKind(CXType_Char_U);
UseKind(CXType_Char_S);
UseKind(CXType_UChar);
UseKind(CXType_SChar);
UseKind(CXType_Char16);
UseKind(CXType_Char32);
UseKind(CXType_Pointer);
UseKind(CXType_BlockPointer);
UseKind(CXType_MemberPointer);
UseKind(CXType_ObjCObjectPointer);
UseKind(CXType_ConstantArray);
UseKind(CXType_VariableArray);
UseKind(CXType_IncompleteArray);
UseKind(CXType_DependentSizedArray);
UseKind(CXType_LValueReference);
UseKind(CXType_RValueReference);
UseKind(CXType_FunctionNoProto);
UseKind(CXType_FunctionProto);
UseKind(CXType_Record);
UseKind(CXType_Enum);
UseKind(CXType_Typedef);
UseKind(CXType_Int128);
UseKind(CXType_UInt128);
UseKind(CXType_Vector);
UseKind(CXType_Unexposed);
UseKind(CXType_WChar);
UseKind(CXType_ObjCInterface);
UseKind(CXType_ObjCId);
UseKind(CXType_ObjCClass);
UseKind(CXType_ObjCSel);
UseKind(CXType_NullPtr);
#if CINDEX_VERSION_MINOR >= 32
UseKind(CXType_Auto);
#endif
#if CINDEX_VERSION_MINOR >= 34
UseKind(CXType_Elaborated);
#endif
#if CINDEX_VERSION_MINOR >= 38
UseKind(CXType_Float128);
#endif
UseKind(CXType_Complex);
case CXType_Invalid:
return nullptr;
default:
qCWarning(KDEV_CLANG) << "Unhandled type:" << type.kind << clang_getTypeSpelling(type);
return nullptr;
}
#undef UseKind
}
RangeInRevision rangeInRevisionForUse(CXCursor cursor, CXCursorKind referencedCursorKind, CXSourceRange useRange, const QSet& macroExpansionLocations)
{
auto range = ClangRange(useRange).toRangeInRevision();
//TODO: Fix in clang, happens for operator<<, operator<, probably more
if (clang_Range_isNull(useRange)) {
useRange = clang_getCursorExtent(cursor);
range = ClangRange(useRange).toRangeInRevision();
}
if (referencedCursorKind == CXCursor_ConversionFunction) {
range.end = range.start;
range.start.column--;
}
// For uses inside macro expansions, create an empty use range at the spelling location
// the empty range is required in order to not "overlap" the macro expansion range
// and to allow proper navigation for the macro expansion
// also see JSON test 'macros.cpp'
if (clang_getCursorKind(cursor) != CXCursor_MacroExpansion) {
unsigned int expansionLocOffset;
const auto spellingLocation = clang_getRangeStart(useRange);
clang_getExpansionLocation(spellingLocation, nullptr, nullptr, nullptr, &expansionLocOffset);
if (macroExpansionLocations.contains(expansionLocOffset)) {
unsigned int spellingLocOffset;
clang_getSpellingLocation(spellingLocation, nullptr, nullptr, nullptr, &spellingLocOffset);
if (spellingLocOffset == expansionLocOffset) {
range.end = range.start;
}
}
} else {
// Workaround for wrong use range returned by clang for macro expansions
const auto contents = ClangUtils::getRawContents(clang_Cursor_getTranslationUnit(cursor), useRange);
const int firstOpeningParen = contents.indexOf('(');
if (firstOpeningParen != -1) {
range.end.column = range.start.column + firstOpeningParen;
range.end.line = range.start.line;
}
}
return range;
}
Visitor::Visitor(CXTranslationUnit tu, CXFile file,
const IncludeFileContexts& includes, const bool update)
: m_file(file)
, m_includes(includes)
, m_parentContext(nullptr)
, m_update(update)
{
CXCursor tuCursor = clang_getTranslationUnitCursor(tu);
auto top = includes[file];
// when updating, this contains child contexts that should be kept alive
// even when they are not part of the AST anymore
// this is required for some assistants, such as the signature assistant
QSet keepAliveContexts;
{
DUChainReadLocker lock;
foreach (const auto& problem, top->problems()) {
const auto& desc = problem->description();
if (desc.startsWith(QLatin1String("Return type of out-of-line definition of '"))
&& desc.endsWith(QLatin1String("' differs from that in the declaration"))) {
auto ctx = top->findContextAt(problem->range().start);
// keep the context and its parents alive
// this also keeps declarations in this context alive
while (ctx) {
keepAliveContexts << ctx;
ctx = ctx->parentContext();
}
}
}
}
CurrentContext parent(top, keepAliveContexts);
m_parentContext = &parent;
clang_visitChildren(tuCursor, &visitCursor, this);
if (m_update) {
DUChainWriteLocker lock;
top->deleteUsesRecursively();
}
for (const auto &contextUses : m_uses) {
for (const auto &cursor : contextUses.second) {
auto referenced = referencedCursor(cursor);
if (clang_Cursor_isNull(referenced)) {
continue;
}
// first, try the canonical referenced cursor
// this is important to get the correct function declaration e.g.
auto canonicalReferenced = clang_getCanonicalCursor(referenced);
auto used = findDeclaration(canonicalReferenced);
if (!used) {
// if the above failed, try the non-canonicalized version as a fallback
// this is required for friend declarations that occur before
// the real declaration. there, the canonical cursor points to
// the friend declaration which is not what we are looking for
used = findDeclaration(referenced);
}
if (!used) { // as a last resort, try to resolve the forward declaration
DUChainReadLocker lock;
DeclarationPointer decl = ClangHelpers::findForwardDeclaration(clang_getCursorType(referenced), contextUses.first, referenced);
used = decl;
if (!used) {
continue;
}
}
#if CINDEX_VERSION_MINOR >= 29
if (clang_Cursor_getNumTemplateArguments(referenced) >= 0) {
// Ideally, we don't need this, but for function templates clang_getCanonicalCursor returns a function definition
// See also the testUsesCreatedForDeclarations test
DUChainReadLocker lock;
used = DUChainUtils::declarationForDefinition(used.data());
}
#endif
const auto useRange = clang_getCursorReferenceNameRange(cursor, 0, 0);
const auto range = rangeInRevisionForUse(cursor, referenced.kind, useRange, m_macroExpansionLocations);
DUChainWriteLocker lock;
auto usedIndex = top->indexForUsedDeclaration(used.data());
contextUses.first->createUse(usedIndex, range);
}
}
}
//END Visitor
CXChildVisitResult visitCursor(CXCursor cursor, CXCursor parent, CXClientData data)
{
Visitor *visitor = static_cast(data);
const auto kind = clang_getCursorKind(cursor);
auto location = clang_getCursorLocation(cursor);
CXFile file;
clang_getFileLocation(location, &file, nullptr, nullptr, nullptr);
// don't skip MemberRefExpr with invalid location, see also:
// http://lists.cs.uiuc.edu/pipermail/cfe-dev/2015-May/043114.html
if (!ClangUtils::isFileEqual(file, visitor->m_file) && (file || kind != CXCursor_MemberRefExpr)) {
return CXChildVisit_Continue;
}
#define UseCursorKind(CursorKind, ...) case CursorKind: return visitor->dispatchCursor(__VA_ARGS__);
switch (kind)
{
UseCursorKind(CXCursor_UnexposedDecl, cursor, parent);
UseCursorKind(CXCursor_StructDecl, cursor, parent);
UseCursorKind(CXCursor_UnionDecl, cursor, parent);
UseCursorKind(CXCursor_ClassDecl, cursor, parent);
UseCursorKind(CXCursor_EnumDecl, cursor, parent);
UseCursorKind(CXCursor_FieldDecl, cursor, parent);
UseCursorKind(CXCursor_EnumConstantDecl, cursor, parent);
UseCursorKind(CXCursor_FunctionDecl, cursor, parent);
UseCursorKind(CXCursor_VarDecl, cursor, parent);
UseCursorKind(CXCursor_TypeAliasDecl, cursor, parent);
UseCursorKind(CXCursor_TypedefDecl, cursor, parent);
UseCursorKind(CXCursor_CXXMethod, cursor, parent);
UseCursorKind(CXCursor_Namespace, cursor, parent);
UseCursorKind(CXCursor_NamespaceAlias, cursor, parent);
UseCursorKind(CXCursor_Constructor, cursor, parent);
UseCursorKind(CXCursor_Destructor, cursor, parent);
UseCursorKind(CXCursor_ConversionFunction, cursor, parent);
UseCursorKind(CXCursor_TemplateTypeParameter, cursor, parent);
UseCursorKind(CXCursor_NonTypeTemplateParameter, cursor, parent);
UseCursorKind(CXCursor_TemplateTemplateParameter, cursor, parent);
UseCursorKind(CXCursor_FunctionTemplate, cursor, parent);
UseCursorKind(CXCursor_ClassTemplate, cursor, parent);
UseCursorKind(CXCursor_ClassTemplatePartialSpecialization, cursor, parent);
UseCursorKind(CXCursor_ObjCInterfaceDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCCategoryDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCProtocolDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCPropertyDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCIvarDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCInstanceMethodDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCClassMethodDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCImplementationDecl, cursor, parent);
UseCursorKind(CXCursor_ObjCCategoryImplDecl, cursor, parent);
UseCursorKind(CXCursor_MacroDefinition, cursor, parent);
UseCursorKind(CXCursor_LabelStmt, cursor, parent);
case CXCursor_TypeRef:
case CXCursor_TemplateRef:
case CXCursor_NamespaceRef:
case CXCursor_MemberRef:
case CXCursor_LabelRef:
case CXCursor_OverloadedDeclRef:
case CXCursor_VariableRef:
case CXCursor_DeclRefExpr:
case CXCursor_MemberRefExpr:
case CXCursor_ObjCClassRef:
return visitor->buildUse(cursor);
case CXCursor_MacroExpansion:
return visitor->buildMacroExpansion(cursor);
case CXCursor_CompoundStmt:
return visitor->buildCompoundStatement(cursor);
case CXCursor_LambdaExpr:
return visitor->buildCompoundStatement(cursor);
case CXCursor_CXXBaseSpecifier:
return visitor->buildCXXBaseSpecifier(cursor);
case CXCursor_ParmDecl:
return visitor->buildParmDecl(cursor);
// TODO: fix upstream and then just adapt this to UseCursorKind()
case CXCursor_TypeAliasTemplateDecl:
return visitor->dispatchTypeAliasTemplate(cursor, parent);
default:
return CXChildVisit_Recurse;
}
}
}
namespace Builder {
void visit(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update)
{
Visitor visitor(tu, file, includes, update);
}
}