Files
Sourcetrail/src/lib_parser/data/parser/cxx/ASTVisitor.cpp
T
malte_langkabel c61efbbcde logic: multithreaded parsing
* modified the TaskParseCxx to be able to run multiple times in parallel
* made FileRegister threadsafe and changed a lot of its mechanisms
* added TaskGroupParallel that runs all children in parallel
* added TaskParseWrapper that acts as a decorator to execute some code before and after parsing.
* implemented task setup in project with 4 parsing threads
* removed SimpleTask as it was only used as interface for the LambdaTask
2016-05-13 13:23:54 +02:00

1652 lines
52 KiB
C++

#include "data/parser/cxx/ASTVisitor.h"
#include <clang/AST/Type.h>
#include <clang/Lex/Preprocessor.h>
#include <llvm/ADT/SmallString.h>
#include <llvm/Support/Casting.h>
#include <string>
#include "data/parser/cxx/name_resolver/CxxTypeNameResolver.h"
#include "data/parser/cxx/utilityCxx.h"
#include "data/parser/ParseLocation.h"
#include "utility/file/FileManager.h"
#include "utility/ScopedSwitcher.h"
// TODO: For an array access, X[I], skip over the array-to-pointer decay. We
// currently record that X's address is taken, which is technically true, but
// the address does not generally escape.
// TODO: A struct assignment in C++ turns into an operator= call, even if the
// user did not define an operator= function. Consider handling operator=
// calls the same way that normal assignment operations are handled. Consider
// doing the same for the other overloadable operators.
// TODO: Unary ++ and --
// TODO: Fix local extern variables. e.g. void func() { extern int var; }
// Consider extern "C", functions nested within classes or namespaces.
///////////////////////////////////////////////////////////////////////////////
// Misc routines
ASTVisitor::ASTVisitor(clang::ASTContext* context, clang::Preprocessor* preprocessor, ParserClient* client, FileRegister* fileRegister)
: m_context(context)
, m_preprocessor(preprocessor)
, m_client(client)
, m_fileRegister(fileRegister)
, m_thisContext(0)
, m_childContext(0)
, m_typeContext(RT_Reference)
, m_contextAccess(ParserClient::ACCESS_NONE)
{
m_declNameCache = std::make_shared<DeclNameCache>([](const clang::NamedDecl* decl) -> NameHierarchy
{
if (decl)
{
return utility::getDeclNameHierarchy(decl);
}
return NameHierarchy("global");
});
m_typeNameCache = std::make_shared<TypeNameCache>([](const clang::Type* type) -> NameHierarchy
{
if (type)
{
CxxTypeNameResolver resolver;
return resolver.getTypeNameHierarchy(type);
}
return NameHierarchy("global");
});
}
ASTVisitor::~ASTVisitor()
{
}
bool ASTVisitor::VisitTranslationUnitDecl(clang::TranslationUnitDecl* decl)
{
// decl->dump();
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Dispatcher routines
// Set the "this" context to the "child" context and reset the child context
// to default.
bool ASTVisitor::TraverseStmt(clang::Stmt *stmt)
{
if (stmt == NULL)
return true;
ScopedSwitcher<Context> sw1(m_thisContext, m_childContext);
if (clang::Expr *e = llvm::dyn_cast<clang::Expr>(stmt)) {
if (e->isRValue())
m_thisContext &= ~(CF_AddressTaken | CF_Assigned | CF_Modified);
} else {
m_thisContext = 0;
}
ScopedSwitcher<Context> sw2(m_childContext, m_thisContext);
return base::TraverseStmt(stmt);
}
bool ASTVisitor::TraverseType(clang::QualType t)
{
ScopedSwitcher<Context> sw1(m_thisContext, 0);
ScopedSwitcher<Context> sw2(m_childContext, 0);
return base::TraverseType(t);
}
bool ASTVisitor::TraverseTypeLoc(clang::TypeLoc tl)
{
ScopedSwitcher<Context> sw1(m_thisContext, 0);
ScopedSwitcher<Context> sw2(m_childContext, 0);
return base::TraverseTypeLoc(tl);
}
bool ASTVisitor::TraverseDecl(clang::Decl *d)
{
ScopedSwitcher<Context> sw1(m_thisContext, 0);
ScopedSwitcher<Context> sw2(m_childContext, 0);
std::shared_ptr<ScopedSwitcher<std::shared_ptr<ContextNameGenerator>>> sw3;
if (d && clang::isa<clang::DeclContext>(d) && clang::isa<clang::NamedDecl>(d) && !clang::isa<clang::NamespaceDecl>(d))
{
clang::NamedDecl* nd = clang::dyn_cast<clang::NamedDecl>(d);
sw3 = std::make_shared<ScopedSwitcher<std::shared_ptr<ContextNameGenerator>>>(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(nd, m_declNameCache)
);
}
return base::TraverseDecl(d);
}
bool ASTVisitor::TraverseLambdaExpr(clang::LambdaExpr* e)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(e->getCallOperator(), m_declNameCache)
);
return base::TraverseLambdaExpr(e);
}
bool ASTVisitor::TraverseFunctionDecl(clang::FunctionDecl* d)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_childContextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
); // store context for template arguments of function specialitzation
return base::TraverseFunctionDecl(d);
}
bool ASTVisitor::TraverseTypedefDecl(clang::TypedefDecl *d)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
return base::TraverseTypedefDecl(d);
}
bool ASTVisitor::TraverseFieldDecl(clang::FieldDecl *d)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
return base::TraverseFieldDecl(d);
}
bool ASTVisitor::TraverseVarDecl(clang::VarDecl *d)
{
std::shared_ptr<ScopedSwitcher<std::shared_ptr<ContextNameGenerator>>> switcher;
NameHierarchy contextNameHierarchy = getContextName();
if (!(contextNameHierarchy.size() > 0 && contextNameHierarchy.back()->hasSignature())) // TODO: whle test if its a function. optimize this: remove requirement to get the name here!
{
switcher = std::make_shared<ScopedSwitcher<std::shared_ptr<ContextNameGenerator>>>(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
}
return base::TraverseVarDecl(d);
}
bool ASTVisitor::TraverseClassTemplateDecl(clang::ClassTemplateDecl* d)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
return base::TraverseClassTemplateDecl(d);
}
bool ASTVisitor::TraverseFunctionTemplateDecl(clang::FunctionTemplateDecl* d)
{
// we need to use the templated decl here because name resolving for FunctionTemplateDecl is not returning a correct signature yet.
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d->getTemplatedDecl(), m_declNameCache)
);
return base::TraverseFunctionTemplateDecl(d);
}
bool ASTVisitor::TraverseTemplateTypeParmDecl(clang::TemplateTypeParmDecl* d)
{
// same as base::TraverseTemplateTypeParmDecl(..) but we need to integrate the setter for the context info.
WalkUpFromTemplateTypeParmDecl(d);
if (d->hasDefaultArgument() && !d->defaultArgumentWasInherited())
{
ScopedSwitcher<RefType> sw1(m_typeContext, RT_TemplateDefaultArgument);
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> sw2(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
TraverseTypeLoc(d->getDefaultArgumentInfo()->getTypeLoc());
}
traverseDeclContextHelper(clang::dyn_cast<clang::DeclContext>(d));
return true;
}
bool ASTVisitor::TraverseTemplateTemplateParmDecl(clang::TemplateTemplateParmDecl* d)
{
// same as base::TraverseTemplateTemplateParmDecl(..) but we need to integrate the setter for the context info.
WalkUpFromTemplateTemplateParmDecl(d);
TraverseDecl(d->getTemplatedDecl());
if (d->hasDefaultArgument() && !d->defaultArgumentWasInherited())
{
ScopedSwitcher<RefType> sw1(m_typeContext, RT_TemplateDefaultArgument);
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> sw2(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
TraverseTemplateArgumentLoc(d->getDefaultArgument());
}
clang::TemplateParameterList* TPL = d->getTemplateParameters();
if (TPL)
{
for (clang::TemplateParameterList::iterator I = TPL->begin(), E = TPL->end(); I != E; ++I)
{
TraverseDecl(*I);
}
}
traverseDeclContextHelper(clang::dyn_cast<clang::DeclContext>(d));
return true;
}
bool ASTVisitor::TraverseClassTemplatePartialSpecializationDecl(clang::ClassTemplatePartialSpecializationDecl* d)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_childContextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
return base::TraverseClassTemplatePartialSpecializationDecl(d);
}
bool ASTVisitor::TraverseDeclRefExpr(clang::DeclRefExpr* e)
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_childContextNameGenerator, std::make_shared<ContextDeclNameGenerator>(e->getDecl(), m_declNameCache)
);
return base::TraverseDeclRefExpr(e);
}
bool ASTVisitor::TraverseTemplateSpecializationTypeLoc(clang::TemplateSpecializationTypeLoc loc)
{
const clang::Type* t = loc.getTypePtr();
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_childContextNameGenerator, std::make_shared<ContextTypeNameGenerator>(t, m_typeNameCache)
);
return base::TraverseTemplateSpecializationTypeLoc(loc);
}
bool ASTVisitor::TraverseUnresolvedLookupExpr(clang::UnresolvedLookupExpr* e) // TODO: do this for unresolved and dependent stuff
{
std::shared_ptr<ContextNameGenerator> clear;
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> sw(m_childContextNameGenerator, clear);
return base::TraverseUnresolvedLookupExpr(e);
}
bool ASTVisitor::TraverseTemplateArgumentLoc(const clang::TemplateArgumentLoc& loc)
{
std::shared_ptr<ScopedSwitcher<RefType>> sw1;
std::shared_ptr<ScopedSwitcher<std::shared_ptr<ContextNameGenerator>>> sw2;
if (m_typeContext != RT_TemplateDefaultArgument
&& m_childContextNameGenerator)
{
sw1 = std::make_shared<ScopedSwitcher<RefType>>(m_typeContext, RT_TemplateArgument);
sw2 = std::make_shared<ScopedSwitcher<std::shared_ptr<ContextNameGenerator>>>(
m_contextNameGenerator, m_childContextNameGenerator
);
}
clang::TemplateArgument::ArgKind kk = loc.getArgument().getKind();
if (kk == clang::TemplateArgument::Template)
{
RecordDeclRef(loc.getArgument().getAsTemplate().getAsTemplateDecl(), loc.getLocation(), m_typeContext);
}
return base::TraverseTemplateArgumentLoc(loc);
}
///////////////////////////////////////////////////////////////////////////////
// Expression context propagation
bool ASTVisitor::TraverseCallCommon(clang::CallExpr *call)
{
{
m_childContext = CF_Called;
TraverseStmt(call->getCallee());
}
for (unsigned int i = 0; i < call->getNumArgs(); ++i) {
clang::Expr *arg = call->getArg(i);
// If the child is really an lvalue, then this call is passing a
// reference. If the child is not an lvalue, then this address-taken
// flag will be masked out, and the rvalue will be assumed to be read.
m_childContext = CF_AddressTaken;
TraverseStmt(arg);
}
return true;
}
bool ASTVisitor::TraverseBinComma(clang::BinaryOperator *s)
{
{
m_childContext = 0;
TraverseStmt(s->getLHS());
}
{
m_childContext = m_thisContext;
TraverseStmt(s->getRHS());
}
return true;
}
bool ASTVisitor::TraverseAssignCommon(
clang::BinaryOperator *e,
ContextFlags lhsFlag)
{
{
m_childContext = m_thisContext | lhsFlag;
if (m_childContext & CF_Called) {
m_childContext ^= CF_Called;
m_childContext |= CF_Read;
}
TraverseStmt(e->getLHS());
}
{
m_childContext = 0;
TraverseStmt(e->getRHS());
}
return true;
}
bool ASTVisitor::VisitCastExpr(clang::CastExpr *e)
{
if (e->getCastKind() == clang::CK_ArrayToPointerDecay) {
// Note that e->getSubExpr() can be an rvalue array, in which case the
// CF_AddressTaken context will be masked away to 0.
m_childContext = CF_AddressTaken;
} else if (e->getCastKind() == clang::CK_ToVoid) {
m_childContext = 0;
} else if (e->getCastKind() == clang::CK_LValueToRValue) {
m_childContext = CF_Read;
}
return true;
}
bool ASTVisitor::VisitUnaryAddrOf(clang::UnaryOperator *e)
{
m_childContext = CF_AddressTaken;
return true;
}
bool ASTVisitor::VisitUnaryDeref(clang::UnaryOperator *e)
{
m_childContext = 0;
return true;
}
bool ASTVisitor::VisitDeclStmt(clang::DeclStmt *s)
{
// If a declaration is a reference to an lvalue initializer, then we
// record that that the initializer's address was taken. If it is
// an rvalue instead, then we'll see something else in the tree indicating
// what kind of reference to record (e.g. lval-to-rval cast, assignment,
// call, & operator) or assume the rvalue is being read.
m_childContext = CF_AddressTaken;
return true;
}
bool ASTVisitor::VisitReturnStmt(clang::ReturnStmt *s)
{
// See comment for VisitDeclStmt.
m_childContext = CF_AddressTaken;
return true;
}
bool ASTVisitor::VisitVarDecl(clang::VarDecl *d)
{
// See comment for VisitDeclStmt. Set the context for the variable's
// initializer. Also handle default arguments on parameter declarations.
m_childContext = CF_AddressTaken;
return true;
}
bool ASTVisitor::VisitInitListExpr(clang::InitListExpr *e)
{
// See comment for VisitDeclStmt. An initializer list can also bind
// references.
m_childContext = CF_AddressTaken;
return true;
}
bool ASTVisitor::TraverseConstructorInitializer(clang::CXXCtorInitializer *init)
{
if (init->getMember() != NULL)
{
RecordDeclRef(init->getMember(),
init->getMemberLocation(),
RT_Initialized,
SYMBOL_FIELD);
}
// See comment for VisitDeclStmt.
m_childContext = CF_AddressTaken;
return base::TraverseConstructorInitializer(init);
}
///////////////////////////////////////////////////////////////////////////////
// Expression reference recording
bool ASTVisitor::VisitLambdaExpr(clang::LambdaExpr* e)
{
RecordDeclRef(e->getCallOperator(), e->getLocStart(), RT_Definition, SYMBOL_FUNCTION);
return true;
}
bool ASTVisitor::VisitMemberExpr(clang::MemberExpr *e)
{
RecordDeclRefExpr(
e->getMemberDecl(),
e->getMemberLoc(),
e,
m_thisContext);
// Update the child context for the base sub-expression.
if (e->isArrow()) {
m_childContext = CF_Read;
} else {
m_childContext = 0;
if (m_thisContext & CF_AddressTaken)
m_childContext |= CF_AddressTaken;
if (m_thisContext & (CF_Assigned | CF_Modified))
m_childContext |= CF_Modified;
if (m_thisContext & CF_Read)
m_childContext |= CF_Read;
if (m_thisContext & CF_Called) {
// I'm not sure what the best behavior here is.
m_childContext |= CF_Read;
}
}
return true;
}
bool ASTVisitor::VisitDeclRefExpr(clang::DeclRefExpr *e)
{
RecordDeclRefExpr(
e->getDecl(),
e->getLocation(),
e,
m_thisContext);
return true;
}
bool ASTVisitor::VisitCXXConstructExpr(clang::CXXConstructExpr *e)
{
//if (e->getParenOrBraceRange().isValid()) {
// // XXX: This code is a kludge. Recording calls to constructors is
// // troublesome because there isn't an obvious location to associate the
// // call with. Consider:
// // A::A() : field(1, 2, 3) {}
// // new A<B>(1, 2, 3)
// // struct A { A(B); }; A f() { B b; return b; }
// // Implicit calls to conversion operator methods pose a similar
// // problem.
// //
// // Recording constructor calls is very useful, though, so, as a
// // temporary measure, when there are constructor arguments surrounded
// // by parentheses, associate the call with the right parenthesis.
// //
// // Perhaps the right fix is to associate the call with the line itself
// // or with a larger span which may have other references nested within
// // it. The fix may have implications for the navigator GUI.
// RecordDeclRefExpr(
// e->getConstructor(),
// e->getParenOrBraceRange().getEnd(),
// e,
// CF_Called);
//}
clang::SourceLocation loc;
clang::SourceLocation braceBeginLoc = e->getParenOrBraceRange().getBegin();
clang::SourceLocation nameBeginLoc = e->getSourceRange().getBegin();
if (braceBeginLoc.isValid())
{
if (braceBeginLoc == nameBeginLoc)
{
loc = nameBeginLoc;
}
else
{
loc = braceBeginLoc.getLocWithOffset(-1);
}
}
else
{
loc = e->getSourceRange().getEnd();
}
loc = clang::Lexer::GetBeginningOfToken(loc, m_context->getSourceManager(), m_context->getLangOpts());
RecordDeclRefExpr(
e->getConstructor(),
loc,
e,
CF_Called);
return true;
}
void ASTVisitor::RecordDeclRefExpr(clang::NamedDecl *d, clang::SourceLocation loc, clang::Expr *e, Context context)
{
SymbolType symbolType = SYMBOL_TYPE_MAX;
if (clang::isa<clang::VarDecl>(d))
{
if (llvm::isa<clang::ParmVarDecl>(d))
{
symbolType = SYMBOL_PARAMETER;
}
else if (d->getParentFunctionOrMethod() == NULL)
{
symbolType = SYMBOL_GLOBAL_VARIABLE;
}
else
{
symbolType = SYMBOL_LOCAL_VARIABLE;
}
}
if (clang::isa<clang::EnumConstantDecl>(d))
{
symbolType = SYMBOL_ENUM_CONSTANT;
}
else if (clang::isa<clang::FieldDecl>(d))
{
symbolType = SYMBOL_FIELD;
}
if (m_typeContext == RT_TemplateArgument)
{
RecordDeclRef(d, loc, m_typeContext, symbolType);
}
else
{
if (llvm::isa<clang::FunctionDecl>(*d))
{
// XXX: This code seems sloppy, but I suspect it will work well enough.
if (context & CF_Called)
RecordDeclRef(d, loc, RT_Called, symbolType);
if (!(context & CF_Called) || (context & (CF_Read | CF_AddressTaken)))
RecordDeclRef(d, loc, RT_AddressTaken, symbolType);
}
else
{
if (context & CF_Called)
RecordDeclRef(d, loc, RT_Called, symbolType);
if (context & CF_Read)
RecordDeclRef(d, loc, RT_Read, symbolType);
if (context & CF_AddressTaken)
RecordDeclRef(d, loc, RT_AddressTaken, symbolType);
if (context & CF_Assigned)
RecordDeclRef(d, loc, RT_Assigned, symbolType);
if (context & CF_Modified)
RecordDeclRef(d, loc, RT_Modified, symbolType);
if (context == 0)
RecordDeclRef(d, loc, e->isRValue() ? RT_Read : RT_Other, symbolType);
}
}
}
///////////////////////////////////////////////////////////////////////////////
// NestedNameSpecifier handling
bool ASTVisitor::TraverseNestedNameSpecifierLoc(
clang::NestedNameSpecifierLoc qualifier)
{
for (; qualifier; qualifier = qualifier.getPrefix()) {
clang::NestedNameSpecifier *nns = qualifier.getNestedNameSpecifier();
switch (nns->getKind()) {
case clang::NestedNameSpecifier::Namespace:
RecordDeclRef(nns->getAsNamespace(),
qualifier.getLocalBeginLoc(),
RT_Qualifier);
break;
case clang::NestedNameSpecifier::NamespaceAlias:
RecordDeclRef(nns->getAsNamespaceAlias(),
qualifier.getLocalBeginLoc(),
RT_Qualifier);
break;
case clang::NestedNameSpecifier::TypeSpec:
case clang::NestedNameSpecifier::TypeSpecWithTemplate:
if (const clang::TypedefType *tt = nns->getAsType()->getAs<clang::TypedefType>()) {
RecordDeclRef(tt->getDecl(),
qualifier.getLocalBeginLoc(),
RT_Qualifier);
} else if (const clang::RecordType *rt = nns->getAsType()->getAs<clang::RecordType>()) {
RecordDeclRef(rt->getDecl(),
qualifier.getLocalBeginLoc(),
RT_Qualifier);
} else if (const clang::TemplateSpecializationType *tst =
nns->getAsType()->getAs<clang::TemplateSpecializationType>()) {
if (clang::TemplateDecl *decl = tst->getTemplateName().getAsTemplateDecl()) {
if (clang::NamedDecl *templatedDecl = decl->getTemplatedDecl()) {
RecordDeclRef(templatedDecl,
qualifier.getLocalBeginLoc(),
RT_Qualifier);
}
}
}
break;
default: // case Global: case Super:
// do nothing for the remaining cases
break;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Declaration and TypeLoc handling
void ASTVisitor::traverseDeclContextHelper(clang::DeclContext *d)
{
if (!d)
return;
// Traverse children.
for (clang::DeclContext::decl_iterator it = d->decls_begin(),
itEnd = d->decls_end(); it != itEnd; ++it) {
// BlockDecls are traversed through BlockExprs.
if (!llvm::isa<clang::BlockDecl>(*it))
TraverseDecl(*it);
}
}
// Overriding TraverseCXXRecordDecl lets us mark the base-class references
// with the "Base-Class" kind.
bool ASTVisitor::TraverseCXXRecordDecl(clang::CXXRecordDecl *d)
{
if (d->isImplicit())
{
// do nothing
return true;
}
// Traverse qualifiers on the record decl.
TraverseNestedNameSpecifierLoc(d->getQualifierLoc());
// Visit the TagDecl to record its ref.
WalkUpFromCXXRecordDecl(d);
// Traverse base classes.
if (d->isThisDeclarationADefinition()) {
for (clang::CXXRecordDecl::base_class_iterator it = d->bases_begin();
it != d->bases_end();
++it) {
clang::CXXBaseSpecifier *baseSpecifier = it;
ScopedSwitcher<RefType> sw1(m_typeContext, RT_BaseClass);
ScopedSwitcher<ParserClient::AccessType> sw2(
m_contextAccess, convertAccessType(baseSpecifier->getAccessSpecifier())
);
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> sw3(
m_contextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
TraverseTypeLoc(baseSpecifier->getTypeSourceInfo()->getTypeLoc());
}
}
traverseDeclContextHelper(d);
return true;
}
bool ASTVisitor::TraverseClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl *d)
{
// base::TraverseClassTemplateSpecializationDecl calls TraverseTypeLoc,
// which then visits a clang::TemplateSpecializationTypeLoc. We want
// to mark the template specialization as Declaration or Definition,
// not Reference, so skip the TraverseTypeLoc call.
//
// The problem happens with code like this:
// template <>
// struct Vector<bool, void> {};
WalkUpFromClassTemplateSpecializationDecl(d);
if (clang::TypeSourceInfo* tsi = d->getTypeAsWritten())
{
ScopedSwitcher<std::shared_ptr<ContextNameGenerator>> switcher(
m_childContextNameGenerator, std::make_shared<ContextDeclNameGenerator>(d, m_declNameCache)
);
clang::TypeLoc tl = tsi->getTypeLoc();
clang::TemplateSpecializationTypeLoc tstl = tl.castAs<clang::TemplateSpecializationTypeLoc>();
for (unsigned I = 0, E = tstl.getNumArgs(); I != E; ++I)
{
TraverseTemplateArgumentLoc(tstl.getArgLoc(I));
}
}
traverseDeclContextHelper(d);
return true;
//base::TraverseClassTemplateSpecializationDecl(d);
}
bool ASTVisitor::TraverseNamespaceAliasDecl(clang::NamespaceAliasDecl *d)
{
// The base::TraverseNamespaceAliasDecl function avoids traversing the
// namespace decl itself because (I think) that would traverse the
// complete contents of the namespace. However, it fails to traverse
// the qualifiers on the target namespace, so we do that here.
TraverseNestedNameSpecifierLoc(d->getQualifierLoc());
return base::TraverseNamespaceAliasDecl(d);
}
void ASTVisitor::templateParameterListsHelper(clang::DeclaratorDecl *d)
{
for (unsigned i = 0, iEnd = d->getNumTemplateParameterLists();
i != iEnd; ++i) {
clang::TemplateParameterList *parmList =
d->getTemplateParameterList(i);
for (clang::NamedDecl *parm : *parmList)
TraverseDecl(parm);
}
}
bool ASTVisitor::VisitDecl(clang::Decl *d)
{
if (clang::NamedDecl *nd = llvm::dyn_cast<clang::NamedDecl>(d)) {
clang::SourceLocation loc = nd->getLocation();
if (clang::FunctionDecl *fd = llvm::dyn_cast<clang::FunctionDecl>(d)) {
//if (fd->getTemplateInstantiationPattern() != NULL) {
// // When Clang instantiates a function template, it seems to
// // create a FunctionDecl for the instantiation that returns
// // false for fd->isThisDeclarationADefinition(). The result
// // is that the template function definition's location is
// // marked as both a Declaration and a Definition. Fix this by
// // omitting the ref on the instantiation.
//} else {
#if 0
// This code recorded refs without appropriate qualifiers. For
// example, with the code
// template <typename A> void Vector<A>::clear() {}
// it would record the first A as "A", but it needs to record
// Vector::A.
templateParameterListsHelper(fd);
#endif
RefType refType;
refType = fd->isThisDeclarationADefinition() ?
RT_Definition : RT_Declaration;
SymbolType symbolType;
if (llvm::isa<clang::CXXMethodDecl>(fd))
{
symbolType = SYMBOL_METHOD;
}
else
{
symbolType = SYMBOL_FUNCTION;
}
RecordDeclRef(nd, loc, refType, symbolType);
if (fd->isFunctionTemplateSpecialization())
{
RecordDeclRef(nd, loc, RT_TemplateSpecialization, symbolType);
}
//}
} else if (clang::VarDecl *vd = llvm::dyn_cast<clang::VarDecl>(d)) {
// Don't record the parameter definitions in a function declaration
// (unless the function declaration is also a definition). A
// definition will be recorded at the function's definition, and
// recording two definitions is unhelpful. This code could record
// a different kind of reference, but recording the position of
// parameter names in declarations doesn't seem useful.
bool omitParamVar = false;
const bool isParam = llvm::isa<clang::ParmVarDecl>(vd);
if (isParam) {
clang::FunctionDecl *fd =
llvm::dyn_cast_or_null<clang::FunctionDecl>(
vd->getDeclContext());
if (fd && !fd->isThisDeclarationADefinition())
omitParamVar = true;
}
if (!omitParamVar) {
RefType refType;
if (vd->isThisDeclarationADefinition() == clang::VarDecl::DeclarationOnly)
refType = RT_Declaration;
else
refType = RT_Definition;
// TODO: Review for correctness. What about local extern?
SymbolType symbolType;
if (isParam)
{
symbolType = SYMBOL_PARAMETER;
}
else if (vd->getParentFunctionOrMethod() == NULL)
{
if (vd->getAccess() == clang::AS_none)
{
symbolType = SYMBOL_GLOBAL_VARIABLE;
}
else
{
symbolType = SYMBOL_FIELD;
}
}
else
{
symbolType = SYMBOL_LOCAL_VARIABLE;
}
RecordDeclRef(nd, loc, refType, symbolType);
}
} else if (clang::TagDecl *td = llvm::dyn_cast<clang::TagDecl>(d)) {
RefType refType;
refType = td->isThisDeclarationADefinition() ? RT_Definition : RT_Declaration;
// Mark an extern template declaration as a Declaration rather than
// a Definition. For example:
// template<typename T> class Foo {}; // Definition of Foo
// extern template class Foo<int>; // Declaration of Foo
if (clang::ClassTemplateSpecializationDecl *spec =
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(td)) {
if (spec->getTemplateSpecializationKind() !=
clang::TSK_ExplicitSpecialization)
refType = RT_Declaration;
}
SymbolType symbolType = SYMBOL_TYPE_MAX;
// TODO: Handle the C++11 fixed underlying type of enumeration
// declarations.
switch (td->getTagKind())
{
case clang::TTK_Struct: symbolType = SYMBOL_STRUCT; break;
case clang::TTK_Union: symbolType = SYMBOL_UNION; break;
case clang::TTK_Class: symbolType = SYMBOL_CLASS; break;
case clang::TTK_Enum: symbolType = SYMBOL_ENUM; break;
default: assert(false);
}
RecordDeclRef(nd, loc, refType, symbolType);
if (clang::isa<clang::ClassTemplateSpecializationDecl>(td))
{
RecordDeclRef(nd, loc, RT_TemplateSpecialization, symbolType);
}
} else if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast<clang::UsingDirectiveDecl>(d)) {
RecordDeclRef(
ud->getNominatedNamespaceAsWritten(),
loc, RT_UsingDirective);
} else if (clang::UsingDecl *usd = llvm::dyn_cast<clang::UsingDecl>(d)) {
for (auto it = usd->shadow_begin(), itEnd = usd->shadow_end();
it != itEnd; ++it) {
clang::UsingShadowDecl *shadow = *it;
RecordDeclRef(shadow->getTargetDecl(), loc, RT_Using);
}
} else if (clang::NamespaceAliasDecl *nad = llvm::dyn_cast<clang::NamespaceAliasDecl>(d)) {
RecordDeclRef(nad, loc, RT_Declaration, SYMBOL_NAMESPACE);
// not needed right now!
// RecordDeclRef(nad, loc, RT_Declaration, ST_Namespace);
// RecordDeclRef(nad->getAliasedNamespace(),
// nad->getTargetNameLoc(),
// RT_NamespaceAlias);
} else if (llvm::isa<clang::FunctionTemplateDecl>(d)) {
// TODO: use these cases for creating the connection between two undefined template things
// Do nothing. The function will be recorded when it appears as a
// FunctionDecl.
} else if (llvm::isa<clang::ClassTemplateDecl>(d)) {
// Do nothing. The class will be recorded when it appears as a
// RecordDecl.
} else if (llvm::isa<clang::FieldDecl>(d)) {
RecordDeclRef(nd, loc, RT_Declaration, SYMBOL_FIELD);
} else if (llvm::isa<clang::TypedefDecl>(d)) {
RecordDeclRef(nd, loc, RT_Declaration, SYMBOL_TYPEDEF);
} else if (llvm::isa<clang::NamespaceDecl>(d)) {
RecordDeclRef(nd, loc, RT_Declaration, SYMBOL_NAMESPACE);
} else if (llvm::isa<clang::EnumConstantDecl>(d)) {
RecordDeclRef(nd, loc, RT_Declaration, SYMBOL_ENUM_CONSTANT);
} else if (
llvm::isa<clang::NonTypeTemplateParmDecl>(d) ||
llvm::isa<clang::TemplateTypeParmDecl>(d) ||
llvm::isa<clang::TemplateTemplateParmDecl>(d)) {
RecordDeclRef(nd, loc, RT_Declaration, SYMBOL_TEMPLATE_PARAMETER);
} else {
RecordDeclRef(nd, loc, RT_Declaration);
}
}
return true;
}
//#include "data/parser/ParseFunction.h"
//#include "data/parser/cxx/name_resolver/CxxTypeNameResolver.h"
//#include "data/type/NamedDataType.h"
bool ASTVisitor::VisitTypeLoc(clang::TypeLoc tl)
{
if (!tl.getAs<clang::TagTypeLoc>().isNull())
{
const clang::TagTypeLoc &ttl = tl.castAs<clang::TagTypeLoc>();
RecordDeclRef(ttl.getDecl(),
tl.getBeginLoc(),
m_typeContext);
}
else if (!tl.getAs<clang::TypedefTypeLoc>().isNull())
{
const clang::TypedefTypeLoc &ttl = tl.castAs<clang::TypedefTypeLoc>();
RecordDeclRef(ttl.getTypedefNameDecl(),
tl.getBeginLoc(),
m_typeContext);
}
else if (!tl.getAs<clang::TemplateTypeParmTypeLoc>().isNull())
{
const clang::TemplateTypeParmTypeLoc &ttptl =
tl.castAs<clang::TemplateTypeParmTypeLoc>();
RecordDeclRef(ttptl.getDecl(),
tl.getBeginLoc(),
m_typeContext);
}
else if (!tl.getAs<clang::TemplateSpecializationTypeLoc>().isNull())
{
const clang::TemplateSpecializationTypeLoc &tstl =
tl.castAs<clang::TemplateSpecializationTypeLoc>();
const clang::TemplateSpecializationType &tst =
*tstl.getTypePtr()->getAs<clang::TemplateSpecializationType>();
if (tst.getAsCXXRecordDecl())
{
RecordDeclRef(tst.getAsCXXRecordDecl(),
tl.getBeginLoc(),
m_typeContext);
}
else // if template specialization cannot be resolved to a concrete declaration.
{ // this is the case when using a specialization of a template template parameter.
RecordTypeRef(tstl.getTypePtr(),
tl.getBeginLoc(),
m_typeContext);
}
}
else if (!tl.getAs<clang::DependentNameTypeLoc>().isNull())
{
const clang::DependentNameTypeLoc& dntl =
tl.castAs<clang::DependentNameTypeLoc>();
RecordTypeRef(dntl.getTypePtr(),
dntl.getNameLoc(),
m_typeContext);
}
else if (!tl.getAs<clang::BuiltinTypeLoc>().isNull())
{
const clang::BuiltinTypeLoc &btl =
tl.castAs<clang::BuiltinTypeLoc>();
RecordTypeRef(btl.getTypePtr(),
tl.getBeginLoc(),
m_typeContext);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Reference recording
// static inline bool isNamedDeclUnnamed(clang::NamedDecl *d)
// {
// return d->getDeclName().isIdentifier() && d->getIdentifier() == NULL;
// }
ParseLocation ASTVisitor::getDeclRefRange(clang::NamedDecl *decl, clang::SourceLocation loc)
{
ParseLocation parseLocation;
clang::SourceManager& sourceManager = m_context->getSourceManager();
clang::SourceLocation sloc = sourceManager.getSpellingLoc(loc);
{
clang::FileID fileId = sourceManager.getFileID(sloc);
if (!fileId.isInvalid())
{
const clang::FileEntry* fileEntry = sourceManager.getFileEntryForID(fileId);
if (fileEntry != NULL)
{
parseLocation.filePath = FilePath(fileEntry->getName()).canonical();
}
}
unsigned int offset = sourceManager.getFileOffset(sloc);
parseLocation.startLineNumber = sourceManager.getLineNumber(fileId, offset);
parseLocation.startColumnNumber = sourceManager.getColumnNumber(fileId, offset);
}
if (decl)
{
clang::DeclarationName name = decl->getDeclName();
clang::DeclarationName::NameKind nameKind = name.getNameKind();
// A C++ destructor name consists of two tokens, '~' and an identifier.
// Try to include both of them in the ref.
if (nameKind == clang::DeclarationName::CXXDestructorName) {
// Start by getting the destructor name, sans template arguments.
const clang::Type *nameType = name.getCXXNameType().getTypePtr();
assert(nameType != NULL);
llvm::StringRef className;
if (const clang::InjectedClassNameType *injectedNameType =
nameType->getAs<clang::InjectedClassNameType>()) {
className = injectedNameType->getDecl()->getName();
}
else if (const clang::RecordType *recordType =
nameType->getAs<clang::RecordType>()) {
className = recordType->getDecl()->getName();
}
if (!className.empty()) { // TODO: maybe its better to use tokens here.
// Scan the characters.
const char *const buffer = sourceManager.getCharacterData(sloc);
const char *p = buffer;
if (p != NULL && *p == '~') {
p++;
// Permit whitespace between the ~ and the class name.
// Technically there could be preprocessor tokens, comments,
// etc..
while (*p == ' ' || *p == '\t')
p++;
// Match the class name against the text in the source.
if (!strncmp(p, className.data(), className.size())) {
p += className.size();
if (!isalnum(*p) && *p != '_') {
parseLocation.endLineNumber = parseLocation.startLineNumber;
parseLocation.endColumnNumber = parseLocation.startColumnNumber;
parseLocation.endColumnNumber += p - buffer - 1;
return parseLocation;
}
}
}
}
}
// For references to C++ overloaded operators, try to include both the
// operator keyword and the operator name in the ref.
if (nameKind == clang::DeclarationName::CXXOperatorName) {
const char *spelling = clang::getOperatorSpelling(
name.getCXXOverloadedOperator());
if (spelling != NULL) {
const char *const buffer = sourceManager.getCharacterData(sloc);
const char *p = buffer;
if (p != NULL && !strncmp(p, "operator", 8)) {
p += 8;
// Skip whitespace between "operator" and the operator itself.
while (*p == ' ' || *p == '\t')
p++;
// Look for the operator name. This may be too restrictive for
// recognizing multi-token operators like operator[], operator
// delete[], or operator ->*.
if (!strncmp(p, spelling, strlen(spelling))) {
p += strlen(spelling);
parseLocation.endLineNumber = parseLocation.startLineNumber;
parseLocation.endColumnNumber = parseLocation.startColumnNumber;
parseLocation.endColumnNumber += p - buffer - 1;
return parseLocation;
}
}
}
}
}
// General case -- find the end of the token starting at loc.
{
clang::SourceLocation endSloc =
m_preprocessor->getLocForEndOfToken(sloc);
unsigned int offset = sourceManager.getFileOffset(endSloc);
clang::FileID fileId = sourceManager.getFileID(endSloc);
parseLocation.endLineNumber = sourceManager.getLineNumber(fileId, offset);
parseLocation.endColumnNumber = sourceManager.getColumnNumber(fileId, offset) - 1;
}
return parseLocation;
}
void ASTVisitor::RecordTypeRef(
const clang::Type* type,
clang::SourceLocation beginLoc,
RefType refType,
SymbolType symbolType)
{
if (isLocatedInUnparsedProjectFile(beginLoc))
{
ParseLocation parseLocation = getDeclRefRange(0, beginLoc);
NameHierarchy typeNameHierarchy = m_typeNameCache->getValue(type);
NameHierarchy contextNameHierarchy = getContextName();
if (refType == RT_TemplateArgument)
{
m_client->onTemplateArgumentTypeParsed(
parseLocation, typeNameHierarchy, contextNameHierarchy);
}
else if (refType == RT_BaseClass)
{
m_client->onInheritanceParsed(
parseLocation, contextNameHierarchy, typeNameHierarchy, m_contextAccess);
}
else if (refType == RT_TemplateDefaultArgument)
{
m_client->onTemplateDefaultArgumentTypeParsed(
parseLocation, typeNameHierarchy, contextNameHierarchy);
}
else
{
m_client->onTypeUsageParsed(
parseLocation, contextNameHierarchy, typeNameHierarchy);
}
}
}
void ASTVisitor::RecordDeclRef(
clang::NamedDecl* d,
clang::SourceLocation beginLoc,
RefType refType,
SymbolType symbolType)
{
bool declIsImplicit = isImplicit(d);
if (!d ||
(declIsImplicit && !isLocatedInProjectFile(beginLoc)) ||
(!declIsImplicit && !isLocatedInUnparsedProjectFile(beginLoc)))
{
return;
}
ParseLocation parseLocation = getDeclRefRange(d, beginLoc);
NameHierarchy declNameHierarchy = m_declNameCache->getValue(d);
bool fallback = false;
if (symbolType == SYMBOL_LOCAL_VARIABLE || symbolType == SYMBOL_PARAMETER)
{
if (!declIsImplicit)
{
if (clang::VarDecl* varDecl = clang::dyn_cast<clang::VarDecl>(d))
{
ParseLocation declLocation = getParseLocation(varDecl->getSourceRange());
std::string name =
declLocation.filePath.str() + "::" +
varDecl->getNameAsString() + "<" +
std::to_string(declLocation.startLineNumber) + ":" +
std::to_string(declLocation.startColumnNumber) + ">";
m_client->onLocalSymbolParsed(
name,
parseLocation
);
}
}
return;
}
switch (refType)
{
case RT_Declaration:
case RT_Definition:
{
switch (symbolType)
{
case SYMBOL_TYPEDEF:
if (clang::TypedefDecl* typedefDecl = clang::dyn_cast<clang::TypedefDecl>(d))
{
m_client->onTypedefParsed(
parseLocation,
declNameHierarchy,
convertAccessType(typedefDecl->getAccess()),
declIsImplicit);
}
break;
case SYMBOL_CLASS:
if (clang::RecordDecl* recordDecl = clang::dyn_cast<clang::RecordDecl>(d))
{
m_client->onClassParsed(
parseLocation,
declNameHierarchy,
convertAccessType(recordDecl->getAccess()),
(refType == RT_Definition ? getParseLocationOfRecordBody(recordDecl) : ParseLocation()),
declIsImplicit);
}
break;
case SYMBOL_STRUCT:
if (clang::RecordDecl* recordDecl = clang::dyn_cast<clang::RecordDecl>(d))
{
m_client->onStructParsed(
parseLocation,
declNameHierarchy,
convertAccessType(recordDecl->getAccess()),
(refType == RT_Definition ? getParseLocationOfRecordBody(recordDecl) : ParseLocation()),
declIsImplicit);
}
break;
case SYMBOL_GLOBAL_VARIABLE:
m_client->onGlobalVariableParsed(
parseLocation,
declNameHierarchy,
declIsImplicit);
break;
case SYMBOL_FIELD:
//if (clang::VarDecl* varDecl = clang::dyn_cast<clang::VarDecl>(d))
{
m_client->onFieldParsed(
parseLocation,
declNameHierarchy,
convertAccessType(d->getAccess()),
declIsImplicit);
}
break;
case SYMBOL_FUNCTION:
if (clang::FunctionDecl* functionDecl = clang::dyn_cast<clang::FunctionDecl>(d))
{
m_client->onFunctionParsed(
parseLocation,
declNameHierarchy,
(refType == RT_Definition ? getParseLocationOfFunctionBody(functionDecl) : ParseLocation()),
declIsImplicit);
}
break;
case SYMBOL_METHOD:
if (clang::CXXMethodDecl* methodDecl = clang::dyn_cast<clang::CXXMethodDecl>(d))
{
m_client->onMethodParsed(
parseLocation,
declNameHierarchy,
convertAccessType(methodDecl->getAccess()),
getAbstractionType(methodDecl),
(refType == RT_Definition ? getParseLocationOfFunctionBody(methodDecl) : ParseLocation()),
declIsImplicit);
for (clang::CXXMethodDecl::method_iterator it = methodDecl->begin_overridden_methods(); // iterate in traversal and use RT_Overridden or so..
it != methodDecl->end_overridden_methods(); it++)
{
m_client->onMethodOverrideParsed(
parseLocation,
m_declNameCache->getValue(*it),
declNameHierarchy);
}
clang::MemberSpecializationInfo* memberSpecializationInfo = methodDecl->getMemberSpecializationInfo();
if (memberSpecializationInfo)
{
clang::NamedDecl* specializedNamedDecl = memberSpecializationInfo->getInstantiatedFrom();
if (clang::isa<clang::FunctionDecl>(specializedNamedDecl))
{
m_client->onTemplateMemberFunctionSpecializationParsed(
parseLocation,
declNameHierarchy,
m_declNameCache->getValue(specializedNamedDecl));
}
}
}
break;
case SYMBOL_NAMESPACE:
if (clang::NamespaceDecl* namespaceDecl = clang::dyn_cast<clang::NamespaceDecl>(d))
{
m_client->onNamespaceParsed(
namespaceDecl->isAnonymousNamespace() ? ParseLocation() : parseLocation,
declNameHierarchy,
getParseLocation(namespaceDecl->getSourceRange()),
declIsImplicit);
}
else if (clang::NamespaceAliasDecl* namespaceAliasDecl = clang::dyn_cast<clang::NamespaceAliasDecl>(d))
{
m_client->onNamespaceParsed(
parseLocation,
declNameHierarchy,
getParseLocation(namespaceAliasDecl->getAliasedNamespace()->getSourceRange()),
declIsImplicit);
}
break;
case SYMBOL_ENUM:
if (clang::EnumDecl* enumDecl = clang::dyn_cast<clang::EnumDecl>(d))
{
m_client->onEnumParsed(
parseLocation,
declNameHierarchy,
convertAccessType(enumDecl->getAccess()),
getParseLocation(enumDecl->getSourceRange()),
declIsImplicit);
}
break;
case SYMBOL_ENUM_CONSTANT:
m_client->onEnumConstantParsed(
parseLocation,
declNameHierarchy,
declIsImplicit);
break;
case SYMBOL_TEMPLATE_PARAMETER:
if (!d->getName().empty()) // We don't create symbols for unnamed template parameters.
{
m_client->onTemplateParameterTypeParsed(
parseLocation,
declNameHierarchy,
declIsImplicit);
}
break;
default:
fallback = true;
break;
}
break;
}
case RT_TemplateSpecialization:
{
if (clang::ClassTemplateSpecializationDecl* classTemplateSpecializationDecl = clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(d))
{
clang::NamedDecl* specializedFromDecl;
llvm::PointerUnion<clang::ClassTemplateDecl*, clang::ClassTemplatePartialSpecializationDecl*> pu = classTemplateSpecializationDecl->getSpecializedTemplateOrPartial();
if (pu.is<clang::ClassTemplateDecl*>())
{
specializedFromDecl = pu.get<clang::ClassTemplateDecl*>();
}
else if (pu.is<clang::ClassTemplatePartialSpecializationDecl*>())
{
specializedFromDecl = pu.get<clang::ClassTemplatePartialSpecializationDecl*>();
}
m_client->onTemplateSpecializationParsed(
parseLocation,
declNameHierarchy,
m_declNameCache->getValue(specializedFromDecl)); // todo: use context and childcontext!!
}
else if (clang::FunctionDecl* functionDecl = clang::dyn_cast<clang::FunctionDecl>(d))
{
m_client->onTemplateSpecializationParsed(
parseLocation,
declNameHierarchy,
m_declNameCache->getValue(functionDecl->getPrimaryTemplate()->getTemplatedDecl())); // TODO: use context and childcontext!!
}
break;
}
case RT_TemplateArgument:
{
const NameHierarchy contextNameHierarchy = getContextName();
m_client->onTemplateArgumentTypeParsed(
parseLocation, declNameHierarchy, contextNameHierarchy);
break;
}
case RT_Called:
{
const NameHierarchy contextNameHierarchy = getContextName();
m_client->onCallParsed(
parseLocation, contextNameHierarchy, declNameHierarchy);
break;
}
case RT_Reference:
{
const NameHierarchy contextNameHierarchy = getContextName();
m_client->onTypeUsageParsed(
parseLocation, contextNameHierarchy, declNameHierarchy);
break;
}
case RT_TemplateDefaultArgument:
{
const NameHierarchy contextNameHierarchy = getContextName();
m_client->onTemplateDefaultArgumentTypeParsed(
parseLocation, declNameHierarchy, contextNameHierarchy);
break;
}
case RT_BaseClass:
{
const NameHierarchy contextNameHierarchy = getContextName();
m_client->onInheritanceParsed(
parseLocation, contextNameHierarchy, declNameHierarchy, m_contextAccess);
break;
}
case RT_Assigned:
case RT_Read:
case RT_Initialized:
case RT_Modified:
case RT_Other:
case RT_AddressTaken:
{
const NameHierarchy contextNameHierarchy = getContextName();
m_client->onUsageParsed(
parseLocation,
contextNameHierarchy,
symbolType,
declNameHierarchy
);
break;
}
case RT_Qualifier:
// Do nothing.
break;
default:
{
fallback = true;
break;
}
}
if (fallback)
{
std::string name = declNameHierarchy.back()->getName();
declNameHierarchy.pop();
declNameHierarchy.push(std::make_shared<NameElement>(name, NameElement::Signature("", "[" + std::to_string(symbolType) + "|" + std::to_string(refType) + "]")));
m_client->onFunctionParsed(
parseLocation,
declNameHierarchy,
parseLocation,
declIsImplicit
);
}
//beginLoc = m_indexerContext.sourceManager().getSpellingLoc(beginLoc);
//clang::FileID fileID;
//if (beginLoc.isValid())
// fileID = m_indexerContext.sourceManager().getFileID(beginLoc);
//IndexerFileContext &fileContext = m_indexerContext.fileContext(fileID);
//indexdb::ID symbolID = fileContext.getDeclSymbolID(d);
//// Pass the prepared data to the IndexBuilder to record.
//fileContext.builder().recordRef(
// symbolID,
// range.first,
// range.second,
// fileContext.getRefTypeID(refType));
//if (symbolType != ST_Max) {
// fileContext.builder().recordSymbol(
// symbolID,
// fileContext.getSymbolTypeID(symbolType));
// if (symbolType != ST_LocalVariable && symbolType != ST_Parameter) {
// fileContext.builder().recordGlobalSymbol(symbolID);
// }
//}
}
bool ASTVisitor::isImplicit(clang::Decl* d) const
{
if (!d)
{
return false;
}
if (d->isImplicit())
{
return true;
}
else if (clang::ClassTemplateSpecializationDecl* ctsd = clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(d))
{
if (!ctsd->isExplicitSpecialization())
{
return true;
}
}
else if (clang::FunctionDecl* fd = clang::dyn_cast_or_null<clang::FunctionDecl>(d))
{
if (fd->isTemplateInstantiation() && fd->getTemplateSpecializationKind() != clang::TSK_ExplicitSpecialization) // or undefined??
{
return true;
}
}
return isImplicit(clang::dyn_cast_or_null<clang::Decl>(d->getDeclContext()));
}
bool ASTVisitor::isLocatedInUnparsedProjectFile(clang::SourceLocation loc)
{
clang::SourceManager& sourceManager = m_context->getSourceManager();
clang::SourceLocation spellingLoc = sourceManager.getSpellingLoc(loc);
clang::FileID fileId;
if (spellingLoc.isValid())
{
fileId = sourceManager.getFileID(spellingLoc);
}
if (!fileId.isInvalid())
{
auto it = m_inUnparsedProjectFileMap.find(fileId);
if (it != m_inUnparsedProjectFileMap.end())
{
return it->second;
}
bool ret = false;
if (m_context->getSourceManager().isWrittenInMainFile(spellingLoc))
{
ret = true;
}
else
{
const clang::FileEntry* fileEntry = sourceManager.getFileEntryForID(fileId);
if (fileEntry != NULL)
{
std::string fileName = fileEntry->getName();
FilePath filePath = FilePath(fileName).canonical();
ret = m_fileRegister->includeFileIsParsed(filePath.str());
}
}
m_inUnparsedProjectFileMap[fileId] = ret;
return ret;
}
return false;
}
bool ASTVisitor::isLocatedInProjectFile(clang::SourceLocation loc)
{
clang::SourceManager& sourceManager = m_context->getSourceManager();
clang::SourceLocation spellingLoc = sourceManager.getSpellingLoc(loc);
clang::FileID fileId;
if (spellingLoc.isValid())
{
fileId = sourceManager.getFileID(spellingLoc);
}
if (!fileId.isInvalid())
{
auto it = m_inProjectFileMap.find(fileId);
if (it != m_inProjectFileMap.end())
{
return it->second;
}
const clang::FileEntry* fileEntry = sourceManager.getFileEntryForID(fileId);
if (fileEntry != NULL)
{
std::string fileName = fileEntry->getName();
FilePath filePath = FilePath(fileName).canonical();
bool ret = m_fileRegister->getFileManager()->hasFilePath(filePath.str());
m_inProjectFileMap[fileId] = ret;
return ret;
}
}
return false;
}
ParserClient::AccessType ASTVisitor::convertAccessType(clang::AccessSpecifier access) const
{
switch (access)
{
case clang::AS_public:
return ParserClient::ACCESS_PUBLIC;
case clang::AS_protected:
return ParserClient::ACCESS_PROTECTED;
case clang::AS_private:
return ParserClient::ACCESS_PRIVATE;
case clang::AS_none:
return ParserClient::ACCESS_NONE;
}
}
ParserClient::AbstractionType ASTVisitor::getAbstractionType(const clang::CXXMethodDecl* methodDecl) const
{
ParserClient::AbstractionType abstraction = ParserClient::ABSTRACTION_NONE;
if (methodDecl->isPure())
{
abstraction = ParserClient::ABSTRACTION_PURE_VIRTUAL;
}
else if (methodDecl->isVirtual())
{
abstraction = ParserClient::ABSTRACTION_VIRTUAL;
}
return abstraction;
}
ParseLocation ASTVisitor::getParseLocationOfRecordBody(clang::RecordDecl* decl) const
{
if (decl->isThisDeclarationADefinition())
{
clang::SourceRange range;
if (clang::CXXRecordDecl* cxxDecl = clang::dyn_cast_or_null<clang::CXXRecordDecl>(decl))
{
clang::ClassTemplateDecl* templateDecl = cxxDecl->getDescribedClassTemplate();
if (templateDecl)
{
range = templateDecl->getSourceRange();
}
}
if (range.isInvalid())
{
range = decl->getDefinition()->getSourceRange();
}
return getParseLocation(range);
}
return ParseLocation();
}
ParseLocation ASTVisitor::getParseLocationOfFunctionBody(const clang::FunctionDecl* decl) const
{
if (decl->hasBody() && decl->isThisDeclarationADefinition())
{
clang::SourceRange range;
clang::FunctionTemplateDecl* templateDecl = decl->getDescribedFunctionTemplate();
if (templateDecl)
{
range = templateDecl->getSourceRange();
}
else
{
range = decl->getSourceRange();
}
return getParseLocation(range);
}
return ParseLocation();
}
ParseLocation ASTVisitor::getParseLocation(const clang::SourceRange& sourceRange) const
{
if (sourceRange.isInvalid())
{
return ParseLocation();
}
const clang::SourceManager& sourceManager = m_context->getSourceManager();
const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(sourceRange.getBegin(), false);
const clang::PresumedLoc& presumedEnd = sourceManager.getPresumedLoc(sourceRange.getEnd(), false);
return ParseLocation(
presumedBegin.getFilename(),
presumedBegin.getLine(),
presumedBegin.getColumn(),
presumedEnd.getLine(),
presumedEnd.getColumn()
);
}
NameHierarchy ASTVisitor::getContextName() const
{
if (m_contextNameGenerator)
{
return m_contextNameGenerator->getName();
}
return NameHierarchy("global");
}