Files
Sourcetrail/src/lib_cxx/data/parser/cxx/CxxAstVisitor.cpp
T
malte_langkabel 9ba3cf165c logic: improved indexer coverage for using decls, using directives
* moved source code for solving names of NestedNameSpecifiers to new CxxSpecifierNameResolver.
* implemented recording of UsingDirectives.
* implemented using file as context for using directives and using directive decls if no other context is available.
* re-implemented recording of the auto keyword
2016-11-14 13:16:20 +01:00

1222 lines
32 KiB
C++

#include "data/parser/cxx/CxxAstVisitor.h"
#include <clang/Basic/SourceLocation.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Lex/Preprocessor.h>
#include "data/parser/ParseLocation.h"
#include "data/parser/ParserClient.h"
#include "utility/file/FileRegister.h"
#include "utility/ScopedFunctor.h"
#include "utility/ScopedSwitcher.h"
#include "data/parser/cxx/name_resolver/CxxDeclNameResolver.h"
#include "data/parser/cxx/name_resolver/CxxTypeNameResolver.h"
CxxAstVisitor::CxxAstVisitor(clang::ASTContext* astContext, clang::Preprocessor* preprocessor, ParserClient* client, FileRegister* fileRegister)
: m_astContext(astContext)
, m_preprocessor(preprocessor)
, m_client(client)
, m_fileRegister(fileRegister)
, m_typeRefContext(REFERENCE_TYPE_USAGE)
, m_declRefContext(REFERENCE_USAGE)
{
m_declNameCache = std::make_shared<DeclNameCache>([](const clang::NamedDecl* decl) -> NameHierarchy
{
if (decl)
{
CxxDeclNameResolver resolver(decl);
return resolver.getDeclNameHierarchy();
}
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");
});
}
CxxAstVisitor::~CxxAstVisitor()
{
}
void CxxAstVisitor::indexDecl(clang::Decl* d)
{
this->TraverseDecl(d);
}
bool CxxAstVisitor::shouldVisitTemplateInstantiations() const
{
return true;
}
bool CxxAstVisitor::shouldVisitImplicitCode() const
{
return true;
}
bool CxxAstVisitor::TraverseDecl(clang::Decl* d)
{
std::shared_ptr<ScopedFunctor> removeContextFunctor;
if (d &&
clang::isa<clang::NamedDecl>(d) &&
!clang::isa<clang::ParmVarDecl>(d) && // no parameter
!(clang::isa<clang::VarDecl>(d) && d->getParentFunctionOrMethod() != NULL) && // no local variable
!clang::isa<clang::UsingDirectiveDecl>(d) && // no using directive decl
!clang::isa<clang::UsingDecl>(d) && // no using decl
!clang::isa<clang::NamespaceDecl>(d) // no namespace
){
clang::NamedDecl* nd = clang::dyn_cast<clang::NamedDecl>(d);
m_contextStack.push_back(std::make_shared<CxxContextDecl>(nd, m_declNameCache));
removeContextFunctor = std::make_shared<ScopedFunctor>([this](){ m_contextStack.pop_back(); });
}
return base::TraverseDecl(d);
}
bool CxxAstVisitor::TraverseStmt(clang::Stmt* stmt)
{
return base::TraverseStmt(stmt);
}
bool CxxAstVisitor::TraverseType(clang::QualType t)
{
return base::TraverseType(t);
}
bool CxxAstVisitor::TraverseTypeLoc(clang::TypeLoc tl)
{
std::shared_ptr<ScopedFunctor> removeContextFunctor;
if (!checkIgnoresTypeLoc(tl))
{
m_contextStack.push_back(std::make_shared<CxxContextType>(tl.getTypePtr(), m_typeNameCache));
removeContextFunctor = std::make_shared<ScopedFunctor>([this](){ m_contextStack.pop_back(); });
}
return base::TraverseTypeLoc(tl);
}
// same as base::TraverseCXXRecordDecl(..) but we need to integrate the setter for the context info.
// additionally: skip implicit CXXRecordDecls (this does not skip template specializations).
bool CxxAstVisitor::TraverseCXXRecordDecl(clang::CXXRecordDecl *d)
{
if (isImplicit(d))
{
return true;
}
WalkUpFromCXXRecordDecl(d);
TraverseNestedNameSpecifierLoc(d->getQualifierLoc());
if (d->isCompleteDefinition())
{
ScopedSwitcher<ReferenceKind> switcher(m_typeRefContext, REFERENCE_INHERITANCE);
for (const auto& base : d->bases())
{
if (!TraverseTypeLoc(base.getTypeSourceInfo()->getTypeLoc()))
{
return false;
}
}
}
traverseDeclContextHelper(clang::dyn_cast<clang::DeclContext>(d));
return true;
}
// same as base::TraverseTemplateTypeParmDecl(..) but we need to integrate the setter for the context info.
bool CxxAstVisitor::TraverseTemplateTypeParmDecl(clang::TemplateTypeParmDecl* d)
{
WalkUpFromTemplateTypeParmDecl(d);
if (d->hasDefaultArgument() && !d->defaultArgumentWasInherited())
{
ScopedSwitcher<ReferenceKind> switcher(m_typeRefContext, REFERENCE_TEMPLATE_DEFAULT_ARGUMENT);
TraverseTypeLoc(d->getDefaultArgumentInfo()->getTypeLoc());
}
traverseDeclContextHelper(clang::dyn_cast<clang::DeclContext>(d));
return true;
}
// same as base::TraverseTemplateTemplateParmDecl(..) but we need to integrate the setter for the context info.
bool CxxAstVisitor::TraverseTemplateTemplateParmDecl(clang::TemplateTemplateParmDecl* d)
{
WalkUpFromTemplateTemplateParmDecl(d);
TraverseDecl(d->getTemplatedDecl());
if (d->hasDefaultArgument() && !d->defaultArgumentWasInherited())
{
ScopedSwitcher<ReferenceKind> switcher(m_typeRefContext, REFERENCE_TEMPLATE_DEFAULT_ARGUMENT);
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 CxxAstVisitor::TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc loc)
{
// just skip all qualifiers (for now)
return true;
}
bool CxxAstVisitor::TraverseConstructorInitializer(clang::CXXCtorInitializer* init)
{
if (!VisitConstructorInitializer(init))
{
return false;
}
return base::TraverseConstructorInitializer(init);
}
bool CxxAstVisitor::TraverseCallExpr(clang::CallExpr* s)
{
return TraverseCallCommon(s);
}
bool CxxAstVisitor::TraverseCXXMemberCallExpr(clang::CXXMemberCallExpr* s)
{
return TraverseCallCommon(s);
}
bool CxxAstVisitor::TraverseCXXOperatorCallExpr(clang::CXXOperatorCallExpr* s)
{
return TraverseCallCommon(s);
}
bool CxxAstVisitor::TraverseCXXConstructExpr(clang::CXXConstructExpr* s)
{
{
ScopedSwitcher<ReferenceKind> switcher(m_declRefContext, REFERENCE_CALL);
WalkUpFromCXXConstructExpr(s);
}
for (unsigned int i = 0; i < s->getNumArgs(); ++i) {
clang::Expr *arg = s->getArg(i);
TraverseStmt(arg);
}
return true;
}
bool CxxAstVisitor::TraverseCXXTemporaryObjectExpr(clang::CXXTemporaryObjectExpr* s)
{
ScopedSwitcher<ReferenceKind> switcher(m_declRefContext, REFERENCE_CALL);
return base::TraverseCXXTemporaryObjectExpr(s);
}
bool CxxAstVisitor::TraverseLambdaExpr(clang::LambdaExpr* s)
{
clang::CXXMethodDecl* methodDecl = s->getCallOperator();
m_contextStack.push_back(std::make_shared<CxxContextDecl>(methodDecl, m_declNameCache));
std::shared_ptr<ScopedFunctor> removeContextFunctor = std::make_shared<ScopedFunctor>([this](){ m_contextStack.pop_back(); });
return base::TraverseLambdaExpr(s);
}
bool CxxAstVisitor::TraverseFunctionDecl(clang::FunctionDecl* d)
{
ScopedSwitcher<std::shared_ptr<CxxContext>> switcher(
m_templateArgumentContext, std::make_shared<CxxContextDecl>(d, m_declNameCache)
);
return base::TraverseFunctionDecl(d);
}
bool CxxAstVisitor::TraverseClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl *d)
{
ScopedSwitcher<std::shared_ptr<CxxContext>> switcher(
m_templateArgumentContext, std::make_shared<CxxContextDecl>(d, m_declNameCache)
);
return base::TraverseClassTemplateSpecializationDecl(d);
}
bool CxxAstVisitor::TraverseClassTemplatePartialSpecializationDecl(clang::ClassTemplatePartialSpecializationDecl* d)
{
ScopedSwitcher<std::shared_ptr<CxxContext>> switcher(
m_templateArgumentContext, std::make_shared<CxxContextDecl>(d, m_declNameCache)
);
return base::TraverseClassTemplatePartialSpecializationDecl(d);
}
bool CxxAstVisitor::TraverseDeclRefExpr(clang::DeclRefExpr* s)
{
ScopedSwitcher<std::shared_ptr<CxxContext>> switcher(
m_templateArgumentContext, std::make_shared<CxxContextDecl>(s->getDecl(), m_declNameCache)
);
return base::TraverseDeclRefExpr(s);
}
bool CxxAstVisitor::TraverseTemplateSpecializationTypeLoc(clang::TemplateSpecializationTypeLoc loc)
{
const clang::Type* t = loc.getTypePtr();
ScopedSwitcher<std::shared_ptr<CxxContext>> switcher(
m_templateArgumentContext, std::make_shared<CxxContextType>(t, m_typeNameCache)
);
return base::TraverseTemplateSpecializationTypeLoc(loc);
}
bool CxxAstVisitor::TraverseUnresolvedLookupExpr(clang::UnresolvedLookupExpr* e) // TODO: do this for unresolved and dependent stuff
{
std::shared_ptr<CxxContext> clear;
ScopedSwitcher<std::shared_ptr<CxxContext>> sw(m_templateArgumentContext, clear);
return base::TraverseUnresolvedLookupExpr(e);
}
bool CxxAstVisitor::TraverseTemplateArgumentLoc(const clang::TemplateArgumentLoc& loc)
{
std::shared_ptr<ScopedSwitcher<ReferenceKind>> switcher;
std::shared_ptr<ScopedFunctor> removeContextFunctor;
if (m_typeRefContext != REFERENCE_TEMPLATE_DEFAULT_ARGUMENT && m_templateArgumentContext)
{
switcher = std::make_shared<ScopedSwitcher<ReferenceKind>>(m_typeRefContext, REFERENCE_TEMPLATE_ARGUMENT);
m_contextStack.push_back(m_templateArgumentContext);
removeContextFunctor = std::make_shared<ScopedFunctor>([this](){ m_contextStack.pop_back(); });
}
if (
(loc.getArgument().getKind() == clang::TemplateArgument::Template) &&
(shouldVisitReference(loc.getLocation(), getTopmostContextDecl()))
){
// TODO: maybe move this to VisitTemplateName
m_client->recordReference(
m_typeRefContext,
m_declNameCache->getValue(loc.getArgument().getAsTemplate().getAsTemplateDecl()),
getContextName(),
getParseLocation(loc.getLocation())
);
}
return base::TraverseTemplateArgumentLoc(loc);
}
void CxxAstVisitor::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);
}
}
bool CxxAstVisitor::TraverseCallCommon(clang::CallExpr* s)
{
{
ScopedSwitcher<ReferenceKind> switcher(m_declRefContext, REFERENCE_CALL);
TraverseStmt(s->getCallee());
}
for (unsigned int i = 0; i < s->getNumArgs(); ++i) {
clang::Expr *arg = s->getArg(i);
TraverseStmt(arg);
}
return true;
}
bool CxxAstVisitor::VisitTagDecl(clang::TagDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
convertTagKind(d->getTagKind()),
getParseLocation(d->getLocation()),
getParseLocationOfTagDeclBody(d),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl* d)
{
if (shouldVisitDecl(d))
{
clang::NamedDecl* specializedFromDecl;
// todo: use context and childcontext!!
llvm::PointerUnion<clang::ClassTemplateDecl*, clang::ClassTemplatePartialSpecializationDecl*> pu = d->getSpecializedTemplateOrPartial();
if (pu.is<clang::ClassTemplateDecl*>())
{
specializedFromDecl = pu.get<clang::ClassTemplateDecl*>();
}
else if (pu.is<clang::ClassTemplatePartialSpecializationDecl*>())
{
specializedFromDecl = pu.get<clang::ClassTemplatePartialSpecializationDecl*>();
}
m_client->recordReference(
REFERENCE_TEMPLATE_SPECIALIZATION_OF, // TODO: call this REFERENCE_TEMPLATE_SPECIALIZATION and reverse the following arguments
m_declNameCache->getValue(specializedFromDecl),
m_declNameCache->getValue(d),
getParseLocation(d->getLocation())
);
}
return true;
}
bool CxxAstVisitor::VisitFunctionDecl(clang::FunctionDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
clang::isa<clang::CXXMethodDecl>(d) ? SYMBOL_METHOD : SYMBOL_FUNCTION,
getParseLocation(d->getLocation()),
getParseLocationOfFunctionBody(d),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
if (d->isFunctionTemplateSpecialization())
{
m_client->recordReference(
REFERENCE_TEMPLATE_SPECIALIZATION_OF, // TODO: call this REFERENCE_TEMPLATE_SPECIALIZATION and reverse the following arguments
m_declNameCache->getValue(d->getPrimaryTemplate()->getTemplatedDecl()), // todo: use context and childcontext!!
m_declNameCache->getValue(d),
getParseLocation(d->getLocation())
);
}
}
return true;
}
bool CxxAstVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* d)
{
// Decl has been recorded in VisitFunctionDecl
if (shouldVisitDecl(d))
{
for (clang::CXXMethodDecl::method_iterator it = d->begin_overridden_methods(); // iterate in traversal and use RT_Overridden or so..
it != d->end_overridden_methods(); it++)
{
m_client->recordReference(
REFERENCE_OVERRIDE,
m_declNameCache->getValue(*it),
m_declNameCache->getValue(d),
getParseLocation(d->getLocation())
);
}
clang::MemberSpecializationInfo* memberSpecializationInfo = d->getMemberSpecializationInfo();
if (memberSpecializationInfo)
{
clang::NamedDecl* specializedNamedDecl = memberSpecializationInfo->getInstantiatedFrom();
if (clang::isa<clang::FunctionDecl>(specializedNamedDecl))
{
m_client->recordReference(
REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION_OF,
m_declNameCache->getValue(specializedNamedDecl),
m_declNameCache->getValue(d),
getParseLocation(d->getLocation())
);
}
}
}
return true;
}
bool CxxAstVisitor::VisitVarDecl(clang::VarDecl* d)
{
if (shouldVisitDecl(d))
{
SymbolKind symbolKind = SYMBOL_KIND_MAX;
if (llvm::isa<clang::ParmVarDecl>(d))
{
symbolKind = SYMBOL_PARAMETER;
}
else if (d->getParentFunctionOrMethod() == NULL)
{
if (d->getAccess() == clang::AS_none)
{
symbolKind = SYMBOL_GLOBAL_VARIABLE;
}
else
{
symbolKind = SYMBOL_FIELD;
}
}
else
{
symbolKind = SYMBOL_LOCAL_VARIABLE;
}
if (symbolKind == SYMBOL_LOCAL_VARIABLE || symbolKind == SYMBOL_PARAMETER)
{
if (!d->getNameAsString().empty()) // don't record anonymous parameters
{
ParseLocation declLocation = getParseLocation(d->getLocation());
std::string name =
declLocation.filePath.fileName() + "<" +
std::to_string(declLocation.startLineNumber) + ":" +
std::to_string(declLocation.startColumnNumber) + ">";
m_client->onLocalSymbolParsed(name, getParseLocation(d->getLocation()));
}
}
else
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
symbolKind,
getParseLocation(d->getLocation()),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
}
}
return true;
}
bool CxxAstVisitor::VisitFieldDecl(clang::FieldDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_FIELD,
getParseLocation(d->getLocation()),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitTypedefDecl(clang::TypedefDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_TYPEDEF,
getParseLocation(d->getLocation()),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitTypeAliasDecl(clang::TypeAliasDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_TYPEDEF,
getParseLocation(d->getLocation()),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitNamespaceDecl(clang::NamespaceDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_NAMESPACE,
d->isAnonymousNamespace() ? ParseLocation() : getParseLocation(d->getLocation()),
getParseLocation(d->getSourceRange()),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitNamespaceAliasDecl(clang::NamespaceAliasDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_NAMESPACE,
getParseLocation(d->getLocation()),
convertAccessSpecifier(d->getAccess()),
isImplicit(d)
);
m_client->recordReference(
REFERENCE_USAGE,
m_declNameCache->getValue(d->getAliasedNamespace()),
m_declNameCache->getValue(d),
getParseLocation(d->getTargetNameLoc())
);
}
return true;
}
bool CxxAstVisitor::VisitEnumConstantDecl(clang::EnumConstantDecl* d)
{
if (shouldVisitDecl(d))
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_ENUM_CONSTANT,
getParseLocation(d->getLocation()),
ACCESS_NONE,
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitUsingDirectiveDecl(clang::UsingDirectiveDecl* d)
{
if (shouldVisitDecl(d))
{
ParseLocation loc = getParseLocation(d->getLocation());
m_client->recordReference(
REFERENCE_USAGE,
m_declNameCache->getValue(d->getNominatedNamespaceAsWritten()),
getContextName(NameHierarchy(loc.filePath.fileName())),
loc
);
}
return true;
}
bool CxxAstVisitor::VisitUsingDecl(clang::UsingDecl* d)
{
if (shouldVisitDecl(d))
{
ParseLocation loc = getParseLocation(d->getLocation());
m_client->recordReference(
REFERENCE_USAGE,
m_declNameCache->getValue(d),
getContextName(NameHierarchy(loc.filePath.fileName())),
loc
);
}
return true;
}
bool CxxAstVisitor::VisitNonTypeTemplateParmDecl(clang::NonTypeTemplateParmDecl* d)
{
if (shouldVisitDecl(d) && !d->getName().empty()) // We don't create symbols for unnamed template parameters.
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_TEMPLATE_PARAMETER,
getParseLocation(d->getLocation()),
ACCESS_TEMPLATE_PARAMETER,
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitTemplateTypeParmDecl(clang::TemplateTypeParmDecl* d)
{
if (shouldVisitDecl(d) && !d->getName().empty()) // We don't create symbols for unnamed template parameters.
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_TEMPLATE_PARAMETER,
getParseLocation(d->getLocation()),
ACCESS_TEMPLATE_PARAMETER,
isImplicit(d)
);
}
return true;
}
bool CxxAstVisitor::VisitTemplateTemplateParmDecl(clang::TemplateTemplateParmDecl* d)
{
if (shouldVisitDecl(d) && !d->getName().empty()) // We don't create symbols for unnamed template parameters.
{
m_client->recordSymbol(
m_declNameCache->getValue(d),
SYMBOL_TEMPLATE_PARAMETER,
getParseLocation(d->getLocation()),
ACCESS_TEMPLATE_PARAMETER,
isImplicit(d)
);
}
return true;
}
/*
bool CxxAstVisitor::VisitNamedDecl(clang::NamedDecl* d)
{
if (!shouldVisitDecl(d))
{
return true;
}
//if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast<clang::UsingDirectiveDecl>(d))
//{
// m_client->recordReference(
// REFERENCE_USAGE,
// m_declNameCache->getValue(ud->getNominatedNamespaceAsWritten()),
// m_con
// return true;
// 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 (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::TypeAliasTemplateDecl>(d)) {
// Do nothing. The type alias will be recorded when it appears as a
// TypeAliasDecl.
}
return true;
}
*/
bool CxxAstVisitor::VisitTypeLoc(clang::TypeLoc tl)
{
if ((shouldVisitReference(tl.getBeginLoc(), getTopmostContextDecl())) &&
(!checkIgnoresTypeLoc(tl)))
{
clang::SourceLocation loc;
if (!tl.getAs<clang::DependentNameTypeLoc>().isNull())
{
const clang::DependentNameTypeLoc& dntl = tl.castAs<clang::DependentNameTypeLoc>();
loc = dntl.getNameLoc();
}
else
{
loc = tl.getBeginLoc();
}
m_client->recordReference(
m_typeRefContext,
m_typeNameCache->getValue(tl.getTypePtr()),
getContextName(1), // we skip the last element because it refers to this typeloc.
getParseLocation(loc)
);
}
return true;
}
bool CxxAstVisitor::VisitDeclRefExpr(clang::DeclRefExpr* s)
{
clang::ValueDecl* decl = s->getDecl();
if (shouldVisitReference(s->getLocation(), getTopmostContextDecl()))
{
if ((clang::isa<clang::ParmVarDecl>(decl)) ||
(clang::isa<clang::VarDecl>(decl) && decl->getParentFunctionOrMethod() != NULL)
) {
ParseLocation declLocation = getParseLocation(decl->getLocation());
std::string name = declLocation.filePath.fileName() + "<" +
std::to_string(declLocation.startLineNumber) + ":" +
std::to_string(declLocation.startColumnNumber) + ">";
m_client->onLocalSymbolParsed(name, getParseLocation(s->getLocation()));
}
else
{
m_client->recordReference(
consumeDeclRefContextKind(),
m_declNameCache->getValue(s->getDecl()),
getContextName(),
getParseLocation(s->getLocation())
);
}
}
return true;
}
bool CxxAstVisitor::VisitMemberExpr(clang::MemberExpr* s)
{
if (shouldVisitReference(s->getMemberLoc(), getTopmostContextDecl()))
{
m_client->recordReference(
consumeDeclRefContextKind(),
m_declNameCache->getValue(s->getMemberDecl()),
getContextName(),
getParseLocation(s->getMemberLoc())
);
}
return true;
}
bool CxxAstVisitor::VisitCXXConstructExpr(clang::CXXConstructExpr* s)
{
if (shouldVisitReference(s->getLocation(), getTopmostContextDecl()))
{
//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 = s->getParenOrBraceRange().getBegin();
clang::SourceLocation nameBeginLoc = s->getSourceRange().getBegin();
if (braceBeginLoc.isValid())
{
if (braceBeginLoc == nameBeginLoc)
{
loc = nameBeginLoc;
}
else
{
loc = braceBeginLoc.getLocWithOffset(-1);
}
}
else
{
loc = s->getSourceRange().getEnd();
}
loc = clang::Lexer::GetBeginningOfToken(loc, m_astContext->getSourceManager(), m_astContext->getLangOpts());
m_client->recordReference(
consumeDeclRefContextKind(),
m_declNameCache->getValue(s->getConstructor()),
getContextName(),
getParseLocation(loc)
);
}
return true;
}
bool CxxAstVisitor::VisitLambdaExpr(clang::LambdaExpr* s)
{
clang::CXXMethodDecl* methodDecl = s->getCallOperator();
if (shouldVisitDecl(methodDecl))
{
m_client->recordSymbol(
m_declNameCache->getValue(methodDecl),
SYMBOL_FUNCTION,
getParseLocation(s->getLocStart()),
getParseLocationOfFunctionBody(methodDecl),
ACCESS_NONE, // TODO: introduce AccessLambda
isImplicit(methodDecl)
);
}
return true;
}
bool CxxAstVisitor::VisitConstructorInitializer(clang::CXXCtorInitializer* init)
{
if (shouldVisitReference(init->getMemberLocation(), getTopmostContextDecl()))
{
// record the field usage here because it is not a DeclRefExpr
if (clang::FieldDecl* memberDecl = init->getMember())
{
m_client->recordReference(
REFERENCE_USAGE,
m_declNameCache->getValue(memberDecl),
getContextName(),
getParseLocation(init->getMemberLocation())
);
}
}
return true;
}
bool CxxAstVisitor::isImplicit(const clang::Decl* d) const
{
if (!d)
{
return false;
}
if (d->isImplicit())
{
if (const clang::RecordDecl* rd = clang::dyn_cast_or_null<clang::RecordDecl>(d))
{
if (rd->isLambda())
{
return isImplicit(clang::dyn_cast_or_null<clang::Decl>(d->getDeclContext()));
}
}
return true;
}
else if (const clang::ClassTemplateSpecializationDecl* ctsd = clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(d))
{
if (!ctsd->isExplicitSpecialization())
{
return true;
}
}
else if (const 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 CxxAstVisitor::shouldVisitDecl(const clang::Decl* decl)
{
if (decl)
{
clang::SourceLocation loc = decl->getLocation();
bool declIsImplicit = isImplicit(decl);
if ((declIsImplicit && isLocatedInProjectFile(loc)) ||
(!declIsImplicit && isLocatedInUnparsedProjectFile(loc)))
{
return true;
}
}
return false;
}
bool CxxAstVisitor::shouldVisitReference(const clang::SourceLocation& referenceLocation, const clang::Decl* contextDecl)
{
bool declIsImplicit = true; // default value is "true" to make sure that everything that should be visited gets visited.
if (contextDecl)
{
declIsImplicit = isImplicit(contextDecl);
}
if ((declIsImplicit && isLocatedInProjectFile(referenceLocation)) ||
(!declIsImplicit && isLocatedInUnparsedProjectFile(referenceLocation)))
{
return true;
}
return false;
}
bool CxxAstVisitor::isLocatedInUnparsedProjectFile(clang::SourceLocation loc)
{
clang::SourceManager& sourceManager = m_astContext->getSourceManager();
clang::SourceLocation spellingLoc = sourceManager.getSpellingLoc(loc);
clang::FileID fileId;
if (spellingLoc.isValid())
{
fileId = sourceManager.getFileID(spellingLoc);
}
if (fileId.isValid())
{
auto it = m_inUnparsedProjectFileMap.find(fileId);
if (it != m_inUnparsedProjectFileMap.end())
{
return it->second;
}
bool ret = false;
if (sourceManager.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();
if (m_fileRegister->hasIncludeFile(filePath))
{
ret = !(m_fileRegister->includeFileIsParsed(filePath));
}
}
}
m_inUnparsedProjectFileMap[fileId] = ret;
return ret;
}
return false;
}
bool CxxAstVisitor::isLocatedInProjectFile(clang::SourceLocation loc)
{
clang::SourceManager& sourceManager = m_astContext->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->hasFilePath(filePath.str());
m_inProjectFileMap[fileId] = ret;
return ret;
}
}
return false;
}
ParseLocation CxxAstVisitor::getParseLocationOfTagDeclBody(clang::TagDecl* 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 CxxAstVisitor::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 CxxAstVisitor::getParseLocation(const clang::SourceLocation& loc) const
{
ParseLocation parseLocation;
if (loc.isValid())
{
clang::SourceManager& sourceManager = m_astContext->getSourceManager();
clang::SourceLocation startLoc = sourceManager.getSpellingLoc(loc);
clang::FileID fileId = sourceManager.getFileID(startLoc);
// find the location file
if (!fileId.isInvalid())
{
const clang::FileEntry* fileEntry = sourceManager.getFileEntryForID(fileId);
if (fileEntry != NULL)
{
parseLocation.filePath = FilePath(fileEntry->getName()).canonical();
}
}
// find the start location
{
unsigned int offset = sourceManager.getFileOffset(startLoc);
parseLocation.startLineNumber = sourceManager.getLineNumber(fileId, offset);
parseLocation.startColumnNumber = sourceManager.getColumnNumber(fileId, offset);
}
// General case -- find the end of the token starting at loc.
{
clang::SourceLocation endSloc = m_preprocessor->getLocForEndOfToken(startLoc);
unsigned int offset = sourceManager.getFileOffset(endSloc);
parseLocation.endLineNumber = sourceManager.getLineNumber(fileId, offset);
parseLocation.endColumnNumber = sourceManager.getColumnNumber(fileId, offset) - 1;
}
}
return parseLocation;
}
ParseLocation CxxAstVisitor::getParseLocation(const clang::SourceRange& sourceRange) const
{
ParseLocation parseLocation;
if (sourceRange.isValid())
{
const clang::SourceManager& sourceManager = m_astContext->getSourceManager();
const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(sourceRange.getBegin(), false);
const clang::PresumedLoc& presumedEnd = sourceManager.getPresumedLoc(sourceRange.getEnd(), false);
parseLocation = ParseLocation(
presumedBegin.getFilename(),
presumedBegin.getLine(),
presumedBegin.getColumn(),
presumedEnd.getLine(),
presumedEnd.getColumn()
);
}
return parseLocation;
}
AccessKind CxxAstVisitor::convertAccessSpecifier(clang::AccessSpecifier access) const
{
switch (access)
{
case clang::AS_public:
return ACCESS_PUBLIC;
case clang::AS_protected:
return ACCESS_PROTECTED;
case clang::AS_private:
return ACCESS_PRIVATE;
case clang::AS_none:
return ACCESS_NONE;
}
}
SymbolKind CxxAstVisitor::convertTagKind(clang::TagTypeKind tagKind)
{
switch (tagKind)
{
case clang::TTK_Struct:
return SYMBOL_STRUCT;
case clang::TTK_Union:
return SYMBOL_UNION;
case clang::TTK_Class:
return SYMBOL_CLASS;
case clang::TTK_Enum:
return SYMBOL_ENUM;
case clang::TTK_Interface:
return SYMBOL_KIND_MAX;
}
}
const clang::NamedDecl* CxxAstVisitor::getTopmostContextDecl() const
{
for (std::vector<std::shared_ptr<CxxContext>>::const_reverse_iterator it = m_contextStack.rbegin(); it != m_contextStack.rend(); it ++)
{
const clang::NamedDecl* decl = (*it)->getDecl();
if (decl)
{
return decl;
}
}
return nullptr;
}
NameHierarchy CxxAstVisitor::getContextName(const size_t skip) const
{
if (m_contextStack.size() <= skip)
{
return m_declNameCache->getValue(nullptr);
}
return m_contextStack[m_contextStack.size() - 1 - skip]->getName(); // todo: performance optimize this
}
NameHierarchy CxxAstVisitor::getContextName(const NameHierarchy& fallback, const size_t skip) const
{
if (m_contextStack.size() <= skip)
{
return fallback;
}
return m_contextStack[m_contextStack.size() - 1 - skip]->getName(); // todo: performance optimize this
}
bool CxxAstVisitor::checkIgnoresTypeLoc(const clang::TypeLoc& tl)
{
if ((!tl.getAs<clang::TagTypeLoc>().isNull()) ||
(!tl.getAs<clang::TypedefTypeLoc>().isNull()) ||
(!tl.getAs<clang::TemplateTypeParmTypeLoc>().isNull()) ||
(!tl.getAs<clang::TemplateSpecializationTypeLoc>().isNull()) ||
(!tl.getAs<clang::DependentNameTypeLoc>().isNull()) ||
(!tl.getAs<clang::BuiltinTypeLoc>().isNull()) ||
(!tl.getAs<clang::AutoTypeLoc>().isNull())
){
return false;
}
return true;
}
ReferenceKind CxxAstVisitor::consumeDeclRefContextKind()
{
ReferenceKind refKind = REFERENCE_UNDEFINED;
if (m_typeRefContext == REFERENCE_TYPE_USAGE)
{
refKind = m_declRefContext;
m_declRefContext = REFERENCE_USAGE;
}
else
{
refKind = m_typeRefContext;
m_typeRefContext = REFERENCE_TYPE_USAGE;
}
return refKind;
}