diff --git a/src/lib_cxx/data/parser/cxx/CxxAstVisitor.cpp b/src/lib_cxx/data/parser/cxx/CxxAstVisitor.cpp index 3ed79d99..c4850575 100644 --- a/src/lib_cxx/data/parser/cxx/CxxAstVisitor.cpp +++ b/src/lib_cxx/data/parser/cxx/CxxAstVisitor.cpp @@ -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(decl)) || (clang::isa(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(d)) + if (const clang::RecordDecl* rd = clang::dyn_cast_or_null(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(d)) + else if (const clang::ClassTemplateSpecializationDecl* ctsd = clang::dyn_cast_or_null(d)) { if (!ctsd->isExplicitSpecialization()) { return true; } } - else if (clang::FunctionDecl* fd = clang::dyn_cast_or_null(d)) + else if (const clang::FunctionDecl* fd = clang::dyn_cast_or_null(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(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>::const_reverse_iterator it = m_contextStack.rbegin(); it != m_contextStack.rend(); it ++) + { + if (std::shared_ptr context = std::dynamic_pointer_cast(*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; +} diff --git a/src/lib_cxx/data/parser/cxx/CxxAstVisitor.h b/src/lib_cxx/data/parser/cxx/CxxAstVisitor.h index 139d3304..41732acc 100644 --- a/src/lib_cxx/data/parser/cxx/CxxAstVisitor.h +++ b/src/lib_cxx/data/parser/cxx/CxxAstVisitor.h @@ -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 base; + const clang::NamedDecl* getTopmostContextDecl() const; NameHierarchy getContextName(const size_t skip = 0) const; bool checkIgnoresTypeLoc(const clang::TypeLoc& tl); diff --git a/src/lib_cxx/data/parser/cxx/CxxContext.cpp b/src/lib_cxx/data/parser/cxx/CxxContext.cpp index 147fc9d9..8fc1f4eb 100644 --- a/src/lib_cxx/data/parser/cxx/CxxContext.cpp +++ b/src/lib_cxx/data/parser/cxx/CxxContext.cpp @@ -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 nameCache) : m_type(type) , m_nameCache(nameCache) diff --git a/src/lib_cxx/data/parser/cxx/CxxContext.h b/src/lib_cxx/data/parser/cxx/CxxContext.h index 32c28a20..1aaa339b 100644 --- a/src/lib_cxx/data/parser/cxx/CxxContext.h +++ b/src/lib_cxx/data/parser/cxx/CxxContext.h @@ -22,6 +22,7 @@ public: CxxContextDecl(const clang::NamedDecl* decl, std::shared_ptr nameCache); virtual ~CxxContextDecl(); virtual NameHierarchy getName(); + const clang::NamedDecl* getDecl(); private: const clang::NamedDecl* m_decl; diff --git a/src/lib_cxx/data/parser/cxx/PreprocessorCallbacks.cpp b/src/lib_cxx/data/parser/cxx/PreprocessorCallbacks.cpp index 9928c0e0..28c13436 100644 --- a/src/lib_cxx/data/parser/cxx/PreprocessorCallbacks.cpp +++ b/src/lib_cxx/data/parser/cxx/PreprocessorCallbacks.cpp @@ -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(); diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index 3e32379c..ac1b9a06 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -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 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 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 diff --git a/src/test/helper/TestParserClient.h b/src/test/helper/TestParserClient.h index 35e9ecfa..bfd7c482 100644 --- a/src/test/helper/TestParserClient.h +++ b/src/test/helper/TestParserClient.h @@ -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 templateParameterTypes; std::vector typeParameters; std::vector localSymbols; - std::vector files; + std::set files; std::vector comments; std::vector inheritances;