Index implicit function calls.

This commit is contained in:
Jacob Dufault 2017-05-22 23:47:27 -07:00
parent 1598129d8b
commit 2e4c5474da
7 changed files with 157 additions and 55 deletions

View File

@ -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 } // namespace
@ -1373,18 +1409,25 @@ void indexEntityReference(CXClientData client_data,
// TODO: search full history? // TODO: search full history?
Range loc_spelling = ResolveSpelling(ref->cursor); Range loc_spelling = ResolveSpelling(ref->cursor);
// Note: be careful, calling db->ToFuncId invalidates the FuncDef* ptrs.
IndexFuncId called_id = db->ToFuncId(ref->referencedEntity->USR); 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)) { if (IsFunctionCallContext(ref->container->cursor.kind)) {
IndexFuncId caller_id = db->ToFuncId(ref->container->cursor); IndexFuncId caller_id = db->ToFuncId(ref->container->cursor);
IndexFunc* caller_def = db->Resolve(caller_id); 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(&caller_def->def.callees, IndexFuncRef(called_id, loc_spelling, is_implicit));
AddFuncRef(&called_def->callers, IndexFuncRef(caller_id, loc_spelling)); AddFuncRef(&called_def->callers, IndexFuncRef(caller_id, loc_spelling, is_implicit));
} else { } else {
IndexFunc* called_def = db->Resolve(called_id); AddFuncRef(&called_def->callers, IndexFuncRef(loc_spelling, is_implicit));
AddFuncRef(&called_def->callers, IndexFuncRef(loc_spelling));
} }
break; break;

View File

@ -75,66 +75,62 @@ using IndexVarId = Id<IndexVar>;
struct IdCache; struct IdCache;
template <typename T>
struct Ref {
Id<T> id() const {
assert(has_id());
return id_;
}
bool has_id() const {
return id_.id != -1;
}
Id<T> id_; struct IndexFuncRef {
IndexFuncId id;
Range loc; Range loc;
bool is_implicit = false;
Ref() {} // For serialization. IndexFuncRef() {} // For serialization.
Ref(Id<T> id, Range loc) : id_(id), loc(loc) {} IndexFuncRef(IndexFuncId id, Range loc, bool is_implicit) : id(id), loc(loc), is_implicit(is_implicit) {}
Ref(Range loc) : id_(Id<T>((size_t)-1)), loc(loc) {} IndexFuncRef(Range loc, bool is_implicit) : id(IndexFuncId((size_t)-1)), loc(loc), is_implicit(is_implicit) {}
bool operator==(const Ref<T>& other) { inline bool operator==(const IndexFuncRef& other) {
return id_ == other.id_ && loc == other.loc; return id == other.id && loc == other.loc;
} }
bool operator!=(const Ref<T>& other) { return !(*this == other); } inline bool operator!=(const IndexFuncRef& other) { return !(*this == other); }
bool operator<(const Ref<T>& other) const { inline bool operator<(const IndexFuncRef& other) const {
if (id_ < other.id) if (id < other.id)
return true; return true;
return id_ == other.id && loc < other.loc; return id == other.id && loc < other.loc;
} }
}; };
template <typename T> inline bool operator==(const IndexFuncRef& a, const IndexFuncRef& b) {
bool operator==(const Ref<T>& a, const Ref<T>& b) { return a.id == b.id && a.loc == b.loc;
return a.id_ == b.id_ && a.loc == b.loc;
} }
template <typename T> inline bool operator!=(const IndexFuncRef& a, const IndexFuncRef& b) {
bool operator!=(const Ref<T>& a, const Ref<T>& b) {
return !(a == b); return !(a == b);
} }
template<typename T> inline void Reflect(Reader& visitor, IndexFuncRef& value) {
void Reflect(Reader& visitor, Ref<T>& value) {
const char* str_value = visitor.GetString(); const char* str_value = visitor.GetString();
if (str_value[0] == '~') {
value.is_implicit = true;
++str_value;
}
uint64_t id = atol(str_value); uint64_t id = atol(str_value);
const char* loc_string = strchr(str_value, '@') + 1; const char* loc_string = strchr(str_value, '@') + 1;
value.id_ = Id<T>(id); value.id = IndexFuncId(id);
value.loc = Range(loc_string); value.loc = Range(loc_string);
} }
template<typename T> inline void Reflect(Writer& visitor, IndexFuncRef& value) {
void Reflect(Writer& visitor, Ref<T>& value) { std::string s;
if (value.id_.id == -1) {
std::string s = "-1@" + value.loc.ToString(); if (value.is_implicit)
visitor.String(s.c_str()); s += "~";
if (value.id.id == -1) { // id.id is unsigned, special case 0 value
s += "-1";
} }
else { else {
std::string s = std::to_string(value.id_.id) + "@" + value.loc.ToString(); s += std::to_string(value.id.id);
visitor.String(s.c_str());
} }
}
using IndexFuncRef = Ref<IndexFunc>; s += "@" + value.loc.ToString();
visitor.String(s.c_str());
}
// TODO: skip as much forward-processing as possible when |is_system_def| is // TODO: skip as much forward-processing as possible when |is_system_def| is
// set to false. // set to false.

View File

@ -339,7 +339,7 @@ QueryVarId IdMap::ToQuery(IndexVarId id) const {
return QueryVarId(cached_var_ids_.find(id)->second); return QueryVarId(cached_var_ids_.find(id)->second);
} }
QueryFuncRef IdMap::ToQuery(IndexFuncRef ref) const { QueryFuncRef IdMap::ToQuery(IndexFuncRef ref) const {
return QueryFuncRef(ToQuery(ref.id_), ToQuery(ref.loc)); return QueryFuncRef(ToQuery(ref.id), ToQuery(ref.loc));
} }
optional<QueryLocation> IdMap::ToQuery(optional<Range> range) const { optional<QueryLocation> IdMap::ToQuery(optional<Range> range) const {
@ -815,8 +815,8 @@ TEST_CASE("func callers") {
IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr")); IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr"));
IndexFunc* cf = current.Resolve(current.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(1, 0)), false /*is_implicit*/));
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)))); cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/));
IndexUpdate update = GetDelta(previous, current); IndexUpdate update = GetDelta(previous, current);
@ -856,10 +856,10 @@ TEST_CASE("apply delta") {
IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr")); IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr"));
IndexFunc* cf = current.Resolve(current.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(1, 0)), false /*is_implicit*/));
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)))); 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)))); 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)))); cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(5, 0)), false /*is_implicit*/));
QueryDatabase db; QueryDatabase db;
IdMap previous_map(&db, previous.id_cache); IdMap previous_map(&db, previous.id_cache);

View File

@ -30,7 +30,7 @@ OUTPUT:
"definition_spelling": "3:3-3:6", "definition_spelling": "3:3-3:6",
"definition_extent": "3:3-3:11", "definition_extent": "3:3-3:11",
"declaring_type": 0, "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, "id": 1,
"usr": "c:@F@foo#", "usr": "c:@F@foo#",
@ -38,7 +38,7 @@ OUTPUT:
"detailed_name": "void foo()", "detailed_name": "void foo()",
"definition_spelling": "6:6-6:9", "definition_spelling": "6:6-6:9",
"definition_extent": "6:1-9:2", "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": [{ "vars": [{
"id": 0, "id": 0,

View File

@ -35,7 +35,7 @@ OUTPUT:
"definition_spelling": "3:3-3:6", "definition_spelling": "3:3-3:6",
"definition_extent": "3:3-3:11", "definition_extent": "3:3-3:11",
"declaring_type": 0, "declaring_type": 0,
"callers": ["2@8:7-8:8"] "callers": ["~2@8:7-8:8"]
}, { }, {
"id": 1, "id": 1,
"usr": "c:@S@Foo@F@~Foo#", "usr": "c:@S@Foo@F@~Foo#",
@ -51,7 +51,7 @@ OUTPUT:
"detailed_name": "void foo()", "detailed_name": "void foo()",
"definition_spelling": "7:6-7:9", "definition_spelling": "7:6-7:9",
"definition_extent": "7:1-9:2", "definition_extent": "7:1-9:2",
"callees": ["0@8:7-8:8"] "callees": ["~0@8:7-8:8"]
}], }],
"vars": [{ "vars": [{
"id": 0, "id": 0,

View File

@ -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"]
}]
}
*/

View File

@ -28,7 +28,7 @@ OUTPUT:
"detailed_name": "void Wrapper::Wrapper(int)", "detailed_name": "void Wrapper::Wrapper(int)",
"declarations": ["2:3-2:10"], "declarations": ["2:3-2:10"],
"declaring_type": 0, "declaring_type": 0,
"callers": ["2@8:10-8:16"] "callers": ["~2@8:10-8:16"]
}, { }, {
"id": 1, "id": 1,
"usr": "c:@F@called#", "usr": "c:@F@called#",
@ -44,7 +44,7 @@ OUTPUT:
"detailed_name": "Wrapper caller()", "detailed_name": "Wrapper caller()",
"definition_spelling": "7:9-7:15", "definition_spelling": "7:9-7:15",
"definition_extent": "7:1-9:2", "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"]
}] }]
} }
*/ */