From 2e4c5474da8b7b990578867f3b868135b3f364c7 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Mon, 22 May 2017 23:47:27 -0700 Subject: [PATCH] Index implicit function calls. --- src/indexer.cc | 55 +++++++++++++++-- src/indexer.h | 68 ++++++++++------------ src/query.cc | 14 ++--- tests/constructors/constructor.cc | 4 +- tests/constructors/destructor.cc | 4 +- tests/constructors/implicit_constructor.cc | 63 ++++++++++++++++++++ tests/usage/func_called_implicit_ctor.cc | 4 +- 7 files changed, 157 insertions(+), 55 deletions(-) create mode 100644 tests/constructors/implicit_constructor.cc diff --git a/src/indexer.cc b/src/indexer.cc index 6eeb081c..e76729f2 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -145,6 +145,42 @@ bool IsLocalSemanticContainer(CXCursorKind kind) { } } +// Returns true if the given entity kind can be called implicitly, ie, without +// actually being written in the source code. +bool CanBeCalledImplicitly(CXIdxEntityKind kind) { + switch (kind) { + case CXIdxEntity_CXXConstructor: + case CXIdxEntity_CXXConversionFunction: + case CXIdxEntity_CXXDestructor: + return true; + default: + return false; + } +} + +// Returns true if the cursor spelling contains the given string. This is +// useful to check for implicit function calls. +bool CursorSpellingContainsString(CXCursor cursor, CXTranslationUnit cx_tu, std::string scanning_for) { + CXSourceRange range = clang_Cursor_getSpellingNameRange(cursor, 0, 0); + CXToken* tokens; + unsigned int num_tokens; + clang_tokenize(cx_tu, range, &tokens, &num_tokens); + + bool result = false; + + for (size_t i = 0; i < num_tokens; ++i) { + CXString name = clang_getTokenSpelling(cx_tu, tokens[i]); + if (strcmp(clang_getCString(name), scanning_for.c_str()) == 0) { + result = true; + break; + } + clang_disposeString(name); + } + + clang_disposeTokens(cx_tu, tokens, num_tokens); + return result; +} + } // namespace @@ -1373,18 +1409,25 @@ void indexEntityReference(CXClientData client_data, // TODO: search full history? Range loc_spelling = ResolveSpelling(ref->cursor); - // Note: be careful, calling db->ToFuncId invalidates the FuncDef* ptrs. IndexFuncId called_id = db->ToFuncId(ref->referencedEntity->USR); + IndexFunc* called_def = db->Resolve(called_id); + + // libclang doesn't provide a nice api to check if the given function + // call is implicit. ref->kind should probably work (it's either direct + // or implicit), but libclang only supports implicit for objective-c. + bool is_implicit = CanBeCalledImplicitly(ref->referencedEntity->kind) && + !CursorSpellingContainsString(ref->cursor, param->tu->cx_tu, called_def->def.short_name); + if (IsFunctionCallContext(ref->container->cursor.kind)) { IndexFuncId caller_id = db->ToFuncId(ref->container->cursor); IndexFunc* caller_def = db->Resolve(caller_id); - IndexFunc* called_def = db->Resolve(called_id); + // Calling db->ToFuncId invalidates the FuncDef* ptrs. + called_def = db->Resolve(called_id); - AddFuncRef(&caller_def->def.callees, IndexFuncRef(called_id, loc_spelling)); - AddFuncRef(&called_def->callers, IndexFuncRef(caller_id, loc_spelling)); + AddFuncRef(&caller_def->def.callees, IndexFuncRef(called_id, loc_spelling, is_implicit)); + AddFuncRef(&called_def->callers, IndexFuncRef(caller_id, loc_spelling, is_implicit)); } else { - IndexFunc* called_def = db->Resolve(called_id); - AddFuncRef(&called_def->callers, IndexFuncRef(loc_spelling)); + AddFuncRef(&called_def->callers, IndexFuncRef(loc_spelling, is_implicit)); } break; diff --git a/src/indexer.h b/src/indexer.h index c7533935..5c238df0 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -75,66 +75,62 @@ using IndexVarId = Id; struct IdCache; -template -struct Ref { - Id id() const { - assert(has_id()); - return id_; - } - bool has_id() const { - return id_.id != -1; - } - Id id_; +struct IndexFuncRef { + IndexFuncId id; Range loc; + bool is_implicit = false; - Ref() {} // For serialization. + IndexFuncRef() {} // For serialization. - Ref(Id id, Range loc) : id_(id), loc(loc) {} - Ref(Range loc) : id_(Id((size_t)-1)), loc(loc) {} + IndexFuncRef(IndexFuncId id, Range loc, bool is_implicit) : id(id), loc(loc), is_implicit(is_implicit) {} + IndexFuncRef(Range loc, bool is_implicit) : id(IndexFuncId((size_t)-1)), loc(loc), is_implicit(is_implicit) {} - bool operator==(const Ref& other) { - return id_ == other.id_ && loc == other.loc; + inline bool operator==(const IndexFuncRef& other) { + return id == other.id && loc == other.loc; } - bool operator!=(const Ref& other) { return !(*this == other); } - bool operator<(const Ref& other) const { - if (id_ < other.id) + inline bool operator!=(const IndexFuncRef& other) { return !(*this == other); } + inline bool operator<(const IndexFuncRef& other) const { + if (id < other.id) return true; - return id_ == other.id && loc < other.loc; + return id == other.id && loc < other.loc; } }; -template -bool operator==(const Ref& a, const Ref& b) { - return a.id_ == b.id_ && a.loc == b.loc; +inline bool operator==(const IndexFuncRef& a, const IndexFuncRef& b) { + return a.id == b.id && a.loc == b.loc; } -template -bool operator!=(const Ref& a, const Ref& b) { +inline bool operator!=(const IndexFuncRef& a, const IndexFuncRef& b) { return !(a == b); } -template -void Reflect(Reader& visitor, Ref& value) { +inline void Reflect(Reader& visitor, IndexFuncRef& value) { const char* str_value = visitor.GetString(); + if (str_value[0] == '~') { + value.is_implicit = true; + ++str_value; + } uint64_t id = atol(str_value); const char* loc_string = strchr(str_value, '@') + 1; - value.id_ = Id(id); + value.id = IndexFuncId(id); value.loc = Range(loc_string); } -template -void Reflect(Writer& visitor, Ref& value) { - if (value.id_.id == -1) { - std::string s = "-1@" + value.loc.ToString(); - visitor.String(s.c_str()); +inline void Reflect(Writer& visitor, IndexFuncRef& value) { + std::string s; + + if (value.is_implicit) + s += "~"; + if (value.id.id == -1) { // id.id is unsigned, special case 0 value + s += "-1"; } else { - std::string s = std::to_string(value.id_.id) + "@" + value.loc.ToString(); - visitor.String(s.c_str()); + s += std::to_string(value.id.id); } -} -using IndexFuncRef = Ref; + s += "@" + value.loc.ToString(); + visitor.String(s.c_str()); +} // TODO: skip as much forward-processing as possible when |is_system_def| is // set to false. diff --git a/src/query.cc b/src/query.cc index f45d2e53..fdba0ed9 100644 --- a/src/query.cc +++ b/src/query.cc @@ -339,7 +339,7 @@ QueryVarId IdMap::ToQuery(IndexVarId id) const { return QueryVarId(cached_var_ids_.find(id)->second); } QueryFuncRef IdMap::ToQuery(IndexFuncRef ref) const { - return QueryFuncRef(ToQuery(ref.id_), ToQuery(ref.loc)); + return QueryFuncRef(ToQuery(ref.id), ToQuery(ref.loc)); } optional IdMap::ToQuery(optional range) const { @@ -815,8 +815,8 @@ TEST_CASE("func callers") { IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr")); IndexFunc* cf = current.Resolve(current.ToFuncId("usr")); - pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)))); - cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)))); + pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)), false /*is_implicit*/)); + cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/)); IndexUpdate update = GetDelta(previous, current); @@ -856,10 +856,10 @@ TEST_CASE("apply delta") { IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr")); IndexFunc* cf = current.Resolve(current.ToFuncId("usr")); - pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)))); - pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)))); - cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(4, 0)))); - cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(5, 0)))); + pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)), false /*is_implicit*/)); + pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/)); + cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(4, 0)), false /*is_implicit*/)); + cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(5, 0)), false /*is_implicit*/)); QueryDatabase db; IdMap previous_map(&db, previous.id_cache); diff --git a/tests/constructors/constructor.cc b/tests/constructors/constructor.cc index 57fb1dc3..a1aabd0a 100644 --- a/tests/constructors/constructor.cc +++ b/tests/constructors/constructor.cc @@ -30,7 +30,7 @@ OUTPUT: "definition_spelling": "3:3-3:6", "definition_extent": "3:3-3:11", "declaring_type": 0, - "callers": ["1@7:7-7:8", "1@8:17-8:20"] + "callers": ["~1@7:7-7:8", "1@8:17-8:20"] }, { "id": 1, "usr": "c:@F@foo#", @@ -38,7 +38,7 @@ OUTPUT: "detailed_name": "void foo()", "definition_spelling": "6:6-6:9", "definition_extent": "6:1-9:2", - "callees": ["0@7:7-7:8", "0@8:17-8:20"] + "callees": ["~0@7:7-7:8", "0@8:17-8:20"] }], "vars": [{ "id": 0, diff --git a/tests/constructors/destructor.cc b/tests/constructors/destructor.cc index bbe4e416..1a19ad2d 100644 --- a/tests/constructors/destructor.cc +++ b/tests/constructors/destructor.cc @@ -35,7 +35,7 @@ OUTPUT: "definition_spelling": "3:3-3:6", "definition_extent": "3:3-3:11", "declaring_type": 0, - "callers": ["2@8:7-8:8"] + "callers": ["~2@8:7-8:8"] }, { "id": 1, "usr": "c:@S@Foo@F@~Foo#", @@ -51,7 +51,7 @@ OUTPUT: "detailed_name": "void foo()", "definition_spelling": "7:6-7:9", "definition_extent": "7:1-9:2", - "callees": ["0@8:7-8:8"] + "callees": ["~0@8:7-8:8"] }], "vars": [{ "id": 0, diff --git a/tests/constructors/implicit_constructor.cc b/tests/constructors/implicit_constructor.cc new file mode 100644 index 00000000..2f3d1543 --- /dev/null +++ b/tests/constructors/implicit_constructor.cc @@ -0,0 +1,63 @@ +struct Type { + Type() {} +}; + +void Make() { + Type foo; + auto foo = Type(); +} + +/* +OUTPUT: +{ + "types": [{ + "id": 0, + "usr": "c:@S@Type", + "short_name": "Type", + "detailed_name": "Type", + "definition_spelling": "1:8-1:12", + "definition_extent": "1:1-3:2", + "funcs": [0], + "instances": [0], + "uses": ["1:8-1:12", "2:3-2:7", "6:3-6:7"] + }], + "funcs": [{ + "id": 0, + "usr": "c:@S@Type@F@Type#", + "short_name": "Type", + "detailed_name": "void Type::Type()", + "definition_spelling": "2:3-2:7", + "definition_extent": "2:3-2:12", + "declaring_type": 0, + "callers": ["~1@6:8-6:11"] + }, { + "id": 1, + "usr": "c:@F@Make#", + "short_name": "Make", + "detailed_name": "void Make()", + "definition_spelling": "5:6-5:10", + "definition_extent": "5:1-8:2", + "callees": ["~0@6:8-6:11"] + }], + "vars": [{ + "id": 0, + "usr": "c:implicit_constructor.cc@51@F@Make#@foo", + "short_name": "foo", + "detailed_name": "Type foo", + "definition_spelling": "6:8-6:11", + "definition_extent": "6:3-6:11", + "variable_type": 0, + "is_local": true, + "uses": ["6:8-6:11"] + }, { + "id": 1, + "usr": "c:implicit_constructor.cc@64@F@Make#@foo", + "short_name": "foo", + "detailed_name": "auto foo", + "definition_spelling": "7:8-7:11", + "definition_extent": "7:3-7:11", + "is_local": true, + "uses": ["7:8-7:11"] + }] +} +*/ diff --git a/tests/usage/func_called_implicit_ctor.cc b/tests/usage/func_called_implicit_ctor.cc index 1ae098ac..aa254030 100644 --- a/tests/usage/func_called_implicit_ctor.cc +++ b/tests/usage/func_called_implicit_ctor.cc @@ -28,7 +28,7 @@ OUTPUT: "detailed_name": "void Wrapper::Wrapper(int)", "declarations": ["2:3-2:10"], "declaring_type": 0, - "callers": ["2@8:10-8:16"] + "callers": ["~2@8:10-8:16"] }, { "id": 1, "usr": "c:@F@called#", @@ -44,7 +44,7 @@ OUTPUT: "detailed_name": "Wrapper caller()", "definition_spelling": "7:9-7:15", "definition_extent": "7:1-9:2", - "callees": ["0@8:10-8:16", "1@8:10-8:16"] + "callees": ["~0@8:10-8:16", "1@8:10-8:16"] }] } */