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:
malte_langkabel
2016-11-10 16:39:52 +01:00
parent cf8e8c7655
commit 9aed97eee2
7 changed files with 108 additions and 33 deletions
+67 -24
View File
@@ -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;
}
+6 -2
View File
@@ -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)
+1
View File
@@ -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();
+19 -1
View File
@@ -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
+2 -2
View File
@@ -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;