From 6a8537c2bfbe617d9c73a484677ee538986e423a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 24 Dec 2017 10:27:17 -0800 Subject: [PATCH] Handle function references in templates. fix #174 (#184) --- src/indexer.cc | 141 ++++++++++++++++++++----------- src/messages/workspace_symbol.cc | 2 +- 2 files changed, 91 insertions(+), 52 deletions(-) diff --git a/src/indexer.cc b/src/indexer.cc index a4f5a6a7..e4d47c1e 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -393,24 +393,43 @@ std::string GetDocumentContentInRange(CXTranslationUnit cx_tu, return result; } -ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, - ClangCursor parent, - void* client_data) { - switch (cursor.get_kind()) { - default: - cursor.VisitChildren(&TemplateVisitor, client_data); - /* fallthrough */ +bool IsFunctionCallContext(CXCursorKind kind) { + switch (kind) { + case CXCursor_FunctionDecl: + case CXCursor_CXXMethod: + case CXCursor_Constructor: + case CXCursor_Destructor: + case CXCursor_ConversionFunction: case CXCursor_FunctionTemplate: - case CXCursor_ClassTemplate: - return ClangCursor::VisitResult::Continue; - case CXCursor_OverloadedDeclRef: { - unsigned num_overloaded = clang_getNumOverloadedDecls(cursor.cx_cursor); - for (unsigned i = 0; i != num_overloaded; i++) { - // ClangCursor overloaded = clang_getOverloadedDecl(cursor.cx_cursor, i); - // TODO handle references - } - return ClangCursor::VisitResult::Continue; - } + case CXCursor_OverloadedDeclRef: + // TODO: we need to test lambdas + case CXCursor_LambdaExpr: + return true; + + default: + break; + } + + return false; +} + +void OnIndexReference_Function(IndexFile* db, + Range loc_spelling, + ClangCursor caller_cursor, + IndexFunc* called, + const std::string& called_usr, + bool is_implicit) { + if (IsFunctionCallContext(caller_cursor.get_kind())) { + IndexFuncId caller_id = db->ToFuncId(caller_cursor.cx_cursor); + IndexFunc* caller = db->Resolve(caller_id); + // Calling db->ToFuncId invalidates the FuncDef* ptrs. + + AddFuncRef(&caller->def.callees, + IndexFuncRef(called->id, loc_spelling, is_implicit)); + AddFuncRef(&called->callers, + IndexFuncRef(caller->id, loc_spelling, is_implicit)); + } else { + AddFuncRef(&called->callers, IndexFuncRef(loc_spelling, is_implicit)); } } @@ -1005,6 +1024,52 @@ ClangCursor::VisitResult VisitMacroDefinitionAndExpansions(ClangCursor cursor, return ClangCursor::VisitResult::Continue; } +namespace { + +struct TemplateVisitorData { + IndexFile* db; + ClangCursor container; +}; + +ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, + ClangCursor parent, + TemplateVisitorData* data) { + switch (cursor.get_kind()) { + default: + if (!IsFunctionCallContext(cursor.get_kind())) + cursor.VisitChildren(&TemplateVisitor, data); + /* fallthrough */ + // TODO Add other containers not covered by IsFunctionCallContext + case CXCursor_ClassTemplate: + return ClangCursor::VisitResult::Continue; + case CXCursor_OverloadedDeclRef: { + unsigned num_overloaded = clang_getNumOverloadedDecls(cursor.cx_cursor); + for (unsigned i = 0; i != num_overloaded; i++) { + ClangCursor overloaded = clang_getOverloadedDecl(cursor.cx_cursor, i); + switch (overloaded.get_kind()) { + default: + break; + case CXCursor_FunctionDecl: { + std::string ref_usr = overloaded.get_usr(); + IndexFuncId called_id = data->db->ToFuncId(ref_usr); + IndexFunc* called = data->db->Resolve(called_id); + OnIndexReference_Function(data->db, + ResolveSpelling(cursor.cx_cursor), + data->container, + called, + ref_usr, + /*implicit=*/ false); + break; + } + } + } + return ClangCursor::VisitResult::Continue; + } + } +} + +} // namespace + void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { if (!kIndexStdDeclarations && clang_Location_isInSystemHeader( @@ -1281,7 +1346,10 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { // references. if (decl->entityInfo->templateKind == CXIdxEntity_Template) { // TODO put db and caller into client data - decl_cursor.VisitChildren(&TemplateVisitor, (void*)0); + TemplateVisitorData data; + data.db = db; + data.container = decl_cursor; + decl_cursor.VisitChildren(&TemplateVisitor, &data); } // Add function usage information. We only want to do it once per @@ -1470,26 +1538,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { } } -bool IsFunctionCallContext(CXCursorKind kind) { - switch (kind) { - case CXCursor_FunctionDecl: - case CXCursor_CXXMethod: - case CXCursor_Constructor: - case CXCursor_Destructor: - case CXCursor_ConversionFunction: - case CXCursor_FunctionTemplate: - case CXCursor_OverloadedDeclRef: - // TODO: we need to test lambdas - case CXCursor_LambdaExpr: - return true; - - default: - break; - } - - return false; -} - void OnIndexReference(CXClientData client_data, const CXIdxEntityRefInfo* ref) { // TODO: Use clang_getFileUniqueID CXFile file; @@ -1592,19 +1640,10 @@ void OnIndexReference(CXClientData client_data, const CXIdxEntityRefInfo* ref) { !CursorSpellingContainsString(ref->cursor, param->tu->cx_tu, called->def.short_name))); - if (IsFunctionCallContext(ref->container->cursor.kind)) { - IndexFuncId caller_id = db->ToFuncId(ref->container->cursor); - IndexFunc* caller = db->Resolve(caller_id); - // Calling db->ToFuncId invalidates the FuncDef* ptrs. - called = db->Resolve(called_id); - - AddFuncRef(&caller->def.callees, - IndexFuncRef(called_id, loc_spelling, is_implicit)); - AddFuncRef(&called->callers, - IndexFuncRef(caller_id, loc_spelling, is_implicit)); - } else { - AddFuncRef(&called->callers, IndexFuncRef(loc_spelling, is_implicit)); - } + OnIndexReference_Function(db, loc_spelling, + ref->container->cursor, + called, + ref->referencedEntity->USR, is_implicit); // Checks if |str| starts with |start|. Ignores case. auto str_begin = [](const char* start, const char* str) { diff --git a/src/messages/workspace_symbol.cc b/src/messages/workspace_symbol.cc index e617ba65..623ef8c2 100644 --- a/src/messages/workspace_symbol.cc +++ b/src/messages/workspace_symbol.cc @@ -82,7 +82,7 @@ constexpr int kCamelScore = kWordStartScore + kGapScore - 1; enum class CharClass { Lower, Upper, Digit, NonWord }; -static enum CharClass GetCharClass(int c) { +static CharClass GetCharClass(int c) { if (islower(c)) return CharClass::Lower; if (isupper(c)) return CharClass::Upper; if (isdigit(c)) return CharClass::Digit;