logic: improved c++ indexer coverage
* revised check for visiting a referenced type or decl * calling client->onFileParsed everytime a file gets processed by the Preprocessor so we can create valid locations in that file * created test for detecting a usage of a member inside a CallExpression context * added method to consume a context based reference kind in the AstVisitor
This commit is contained in:
@@ -290,7 +290,7 @@ bool CxxAstVisitor::TraverseTemplateArgumentLoc(const clang::TemplateArgumentLoc
|
||||
|
||||
if (
|
||||
(loc.getArgument().getKind() == clang::TemplateArgument::Template) &&
|
||||
(isLocatedInUnparsedProjectFile(loc.getLocation())) // TODO: rather test if the context is implicit
|
||||
(shouldVisitReference(loc.getLocation(), getTopmostContextDecl()))
|
||||
){
|
||||
// TODO: maybe move this to VisitTemplateName
|
||||
m_client->recordReference(
|
||||
@@ -719,7 +719,7 @@ bool CxxAstVisitor::VisitNamedDecl(clang::NamedDecl* d)
|
||||
|
||||
bool CxxAstVisitor::VisitTypeLoc(clang::TypeLoc tl)
|
||||
{
|
||||
if ((isLocatedInUnparsedProjectFile(tl.getBeginLoc())) && // TODO: rather test if the context is implicit
|
||||
if ((shouldVisitReference(tl.getBeginLoc(), getTopmostContextDecl())) &&
|
||||
(!checkIgnoresTypeLoc(tl)))
|
||||
{
|
||||
clang::SourceLocation loc;
|
||||
@@ -746,7 +746,7 @@ bool CxxAstVisitor::VisitTypeLoc(clang::TypeLoc tl)
|
||||
bool CxxAstVisitor::VisitDeclRefExpr(clang::DeclRefExpr* s)
|
||||
{
|
||||
clang::ValueDecl* decl = s->getDecl();
|
||||
if (isLocatedInUnparsedProjectFile(s->getLocation())) // TODO: rather test if the context is implicit
|
||||
if (shouldVisitReference(s->getLocation(), getTopmostContextDecl()))
|
||||
{
|
||||
if ((clang::isa<clang::ParmVarDecl>(decl)) ||
|
||||
(clang::isa<clang::VarDecl>(decl) && decl->getParentFunctionOrMethod() != NULL)
|
||||
@@ -761,7 +761,7 @@ bool CxxAstVisitor::VisitDeclRefExpr(clang::DeclRefExpr* s)
|
||||
else
|
||||
{
|
||||
m_client->recordReference(
|
||||
m_typeRefContext == REFERENCE_TYPE_USAGE ? m_declRefContext : m_typeRefContext,
|
||||
consumeDeclRefContextKind(),
|
||||
m_declNameCache->getValue(s->getDecl()),
|
||||
getContextName(),
|
||||
getParseLocation(s->getLocation())
|
||||
@@ -774,10 +774,10 @@ bool CxxAstVisitor::VisitDeclRefExpr(clang::DeclRefExpr* s)
|
||||
|
||||
bool CxxAstVisitor::VisitMemberExpr(clang::MemberExpr* s)
|
||||
{
|
||||
if (isLocatedInUnparsedProjectFile(s->getMemberLoc())) // TODO: rather test if the context is implicit
|
||||
if (shouldVisitReference(s->getMemberLoc(), getTopmostContextDecl()))
|
||||
{
|
||||
m_client->recordReference(
|
||||
m_typeRefContext == REFERENCE_TYPE_USAGE ? m_declRefContext : m_typeRefContext,
|
||||
consumeDeclRefContextKind(),
|
||||
m_declNameCache->getValue(s->getMemberDecl()),
|
||||
getContextName(),
|
||||
getParseLocation(s->getMemberLoc())
|
||||
@@ -788,7 +788,7 @@ bool CxxAstVisitor::VisitMemberExpr(clang::MemberExpr* s)
|
||||
|
||||
bool CxxAstVisitor::VisitCXXConstructExpr(clang::CXXConstructExpr* s)
|
||||
{
|
||||
if (isLocatedInUnparsedProjectFile(s->getLocation())) // TODO: rather test if the context is implicit
|
||||
if (shouldVisitReference(s->getLocation(), getTopmostContextDecl()))
|
||||
{
|
||||
//if (e->getParenOrBraceRange().isValid()) {
|
||||
// // XXX: This code is a kludge. Recording calls to constructors is
|
||||
@@ -834,7 +834,7 @@ bool CxxAstVisitor::VisitCXXConstructExpr(clang::CXXConstructExpr* s)
|
||||
loc = clang::Lexer::GetBeginningOfToken(loc, m_astContext->getSourceManager(), m_astContext->getLangOpts());
|
||||
|
||||
m_client->recordReference(
|
||||
m_typeRefContext == REFERENCE_TYPE_USAGE ? m_declRefContext : m_typeRefContext,
|
||||
consumeDeclRefContextKind(),
|
||||
m_declNameCache->getValue(s->getConstructor()),
|
||||
getContextName(),
|
||||
getParseLocation(loc)
|
||||
@@ -862,7 +862,7 @@ bool CxxAstVisitor::VisitLambdaExpr(clang::LambdaExpr* s)
|
||||
|
||||
bool CxxAstVisitor::VisitConstructorInitializer(clang::CXXCtorInitializer* init)
|
||||
{
|
||||
if (isLocatedInUnparsedProjectFile(init->getMemberLocation())) // TODO: rather test if the context is implicit
|
||||
if (shouldVisitReference(init->getMemberLocation(), getTopmostContextDecl()))
|
||||
{
|
||||
// record the field usage here because it is not a DeclRefExpr
|
||||
if (clang::FieldDecl* memberDecl = init->getMember())
|
||||
@@ -878,7 +878,7 @@ bool CxxAstVisitor::VisitConstructorInitializer(clang::CXXCtorInitializer* init)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CxxAstVisitor::isImplicit(clang::Decl* d) const
|
||||
bool CxxAstVisitor::isImplicit(const clang::Decl* d) const
|
||||
{
|
||||
if (!d)
|
||||
{
|
||||
@@ -887,7 +887,7 @@ bool CxxAstVisitor::isImplicit(clang::Decl* d) const
|
||||
|
||||
if (d->isImplicit())
|
||||
{
|
||||
if (clang::RecordDecl* rd = clang::dyn_cast_or_null<clang::RecordDecl>(d))
|
||||
if (const clang::RecordDecl* rd = clang::dyn_cast_or_null<clang::RecordDecl>(d))
|
||||
{
|
||||
if (rd->isLambda())
|
||||
{
|
||||
@@ -896,14 +896,14 @@ bool CxxAstVisitor::isImplicit(clang::Decl* d) const
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (clang::ClassTemplateSpecializationDecl* ctsd = clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(d))
|
||||
else if (const 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))
|
||||
else if (const clang::FunctionDecl* fd = clang::dyn_cast_or_null<clang::FunctionDecl>(d))
|
||||
{
|
||||
if (fd->isTemplateInstantiation() && fd->getTemplateSpecializationKind() != clang::TSK_ExplicitSpecialization) // or undefined??
|
||||
{
|
||||
@@ -914,20 +914,35 @@ bool CxxAstVisitor::isImplicit(clang::Decl* d) const
|
||||
return isImplicit(clang::dyn_cast_or_null<clang::Decl>(d->getDeclContext()));
|
||||
}
|
||||
|
||||
bool CxxAstVisitor::shouldVisitDecl(clang::Decl* d)
|
||||
bool CxxAstVisitor::shouldVisitDecl(const clang::Decl* decl)
|
||||
{
|
||||
clang::SourceLocation loc = d->getLocation();
|
||||
|
||||
bool declIsImplicit = isImplicit(d);
|
||||
|
||||
if (!d ||
|
||||
(declIsImplicit && !isLocatedInProjectFile(loc)) ||
|
||||
(!declIsImplicit && !isLocatedInUnparsedProjectFile(loc)))
|
||||
if (decl)
|
||||
{
|
||||
return false;
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
if ((declIsImplicit && isLocatedInProjectFile(referenceLocation)) ||
|
||||
(!declIsImplicit && isLocatedInUnparsedProjectFile(referenceLocation)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CxxAstVisitor::isLocatedInUnparsedProjectFile(clang::SourceLocation loc)
|
||||
@@ -941,7 +956,7 @@ bool CxxAstVisitor::isLocatedInUnparsedProjectFile(clang::SourceLocation loc)
|
||||
{
|
||||
fileId = sourceManager.getFileID(spellingLoc);
|
||||
}
|
||||
if (!fileId.isInvalid())
|
||||
if (fileId.isValid())
|
||||
{
|
||||
auto it = m_inUnparsedProjectFileMap.find(fileId);
|
||||
if (it != m_inUnparsedProjectFileMap.end())
|
||||
@@ -1141,6 +1156,18 @@ SymbolKind CxxAstVisitor::convertTagKind(clang::TagTypeKind tagKind)
|
||||
}
|
||||
}
|
||||
|
||||
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 ++)
|
||||
{
|
||||
if (std::shared_ptr<CxxContextDecl> context = std::dynamic_pointer_cast<CxxContextDecl>(*it))
|
||||
{
|
||||
return context->getDecl();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NameHierarchy CxxAstVisitor::getContextName(const size_t skip) const
|
||||
{
|
||||
if (m_contextStack.size() <= skip)
|
||||
@@ -1164,3 +1191,19 @@ bool CxxAstVisitor::checkIgnoresTypeLoc(const clang::TypeLoc& tl)
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -114,8 +114,9 @@ public:
|
||||
|
||||
protected:
|
||||
// General helpers
|
||||
bool isImplicit(clang::Decl* d) const;
|
||||
bool shouldVisitDecl(clang::Decl* d);
|
||||
bool isImplicit(const clang::Decl* d) const;
|
||||
bool shouldVisitDecl(const clang::Decl* decl);
|
||||
bool shouldVisitReference(const clang::SourceLocation& referenceLocation, const clang::Decl* contextDecl);
|
||||
bool isLocatedInUnparsedProjectFile(clang::SourceLocation loc);
|
||||
bool isLocatedInProjectFile(clang::SourceLocation loc);
|
||||
|
||||
@@ -127,8 +128,11 @@ protected:
|
||||
SymbolKind convertTagKind(clang::TagTypeKind tagKind);
|
||||
|
||||
private:
|
||||
ReferenceKind consumeDeclRefContextKind();
|
||||
|
||||
typedef clang::RecursiveASTVisitor<CxxAstVisitor> base;
|
||||
|
||||
const clang::NamedDecl* getTopmostContextDecl() const;
|
||||
NameHierarchy getContextName(const size_t skip = 0) const;
|
||||
bool checkIgnoresTypeLoc(const clang::TypeLoc& tl);
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ NameHierarchy CxxContextDecl::getName()
|
||||
return m_nameCache->getValue(m_decl);
|
||||
}
|
||||
|
||||
const clang::NamedDecl* CxxContextDecl::getDecl()
|
||||
{
|
||||
return m_decl;
|
||||
}
|
||||
|
||||
CxxContextType::CxxContextType(const clang::Type* type, std::shared_ptr<TypeNameCache> nameCache)
|
||||
: m_type(type)
|
||||
, m_nameCache(nameCache)
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
CxxContextDecl(const clang::NamedDecl* decl, std::shared_ptr<DeclNameCache> nameCache);
|
||||
virtual ~CxxContextDecl();
|
||||
virtual NameHierarchy getName();
|
||||
const clang::NamedDecl* getDecl();
|
||||
|
||||
private:
|
||||
const clang::NamedDecl* m_decl;
|
||||
|
||||
@@ -29,16 +29,20 @@ void PreprocessorCallbacks::FileChanged(
|
||||
filePath = FilePath(fileEntry->getName()).canonical();
|
||||
}
|
||||
|
||||
if (!filePath.empty() && m_fileRegister->hasFilePath(filePath) && !m_fileRegister->fileIsParsed(filePath))
|
||||
const bool fileIsInProject = m_fileRegister->hasFilePath(filePath);
|
||||
if (!filePath.empty() && fileIsInProject)
|
||||
{
|
||||
m_currentPath = filePath;
|
||||
|
||||
m_client->onFileParsed(m_fileRegister->getFileInfo(filePath));
|
||||
if (reason == EnterFile && !m_fileRegister->includeFileIsParsed(filePath))
|
||||
{
|
||||
m_client->onFileParsed(m_fileRegister->getFileInfo(filePath));
|
||||
m_fileRegister->markIncludeFileParsing(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!filePath.empty() && fileIsInProject && !m_fileRegister->fileIsParsed(filePath))
|
||||
{
|
||||
m_currentPath = filePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentPath = FilePath();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
class CxxParserTestSuite: public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_cxx_parser_usage_of_field_in_function_call_arguments()
|
||||
void test_cxx_parser_finds_usage_of_field_in_function_call_arguments()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"class A\n"
|
||||
@@ -32,6 +32,24 @@ public:
|
||||
TS_ASSERT_EQUALS(client->usages[0], "void A::foo(int) -> A::bar <6:7 6:9>");
|
||||
}
|
||||
|
||||
void test_cxx_parser_usage_of_field_in_function_call_context()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"class A\n"
|
||||
"{\n"
|
||||
"public:\n"
|
||||
" void foo(int i)\n"
|
||||
" {\n"
|
||||
" a->foo(6);\n"
|
||||
" }\n"
|
||||
" A* a;\n"
|
||||
"};\n"
|
||||
);
|
||||
|
||||
TS_ASSERT_EQUALS(client->usages.size(), 1);
|
||||
TS_ASSERT_EQUALS(client->usages[0], "void A::foo(int) -> A::a <6:3 6:3>");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// test finding symbol definitions and declarations
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
|
||||
virtual void onFileParsed(const FileInfo& fileInfo)
|
||||
{
|
||||
files.push_back(fileInfo.path.str());
|
||||
files.insert(fileInfo.path.str());
|
||||
}
|
||||
|
||||
virtual void onCommentParsed(const ParseLocation& location)
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
std::vector<std::string> templateParameterTypes;
|
||||
std::vector<std::string> typeParameters;
|
||||
std::vector<std::string> localSymbols;
|
||||
std::vector<std::string> files;
|
||||
std::set<std::string> files;
|
||||
std::vector<std::string> comments;
|
||||
|
||||
std::vector<std::string> inheritances;
|
||||
|
||||
Reference in New Issue
Block a user