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
@ -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;

View File

@ -75,66 +75,62 @@ using IndexVarId = Id<IndexVar>;
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;
bool is_implicit = false;
Ref() {} // For serialization.
IndexFuncRef() {} // For serialization.
Ref(Id<T> id, Range loc) : id_(id), loc(loc) {}
Ref(Range loc) : id_(Id<T>((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<T>& 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<T>& other) { return !(*this == other); }
bool operator<(const Ref<T>& 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 <typename T>
bool operator==(const Ref<T>& a, const Ref<T>& 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 <typename T>
bool operator!=(const Ref<T>& a, const Ref<T>& b) {
inline bool operator!=(const IndexFuncRef& a, const IndexFuncRef& b) {
return !(a == b);
}
template<typename T>
void Reflect(Reader& visitor, Ref<T>& 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<T>(id);
value.id = IndexFuncId(id);
value.loc = Range(loc_string);
}
template<typename T>
void Reflect(Writer& visitor, Ref<T>& 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<IndexFunc>;
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.

View File

@ -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<QueryLocation> IdMap::ToQuery(optional<Range> 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);

View File

@ -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,

View File

@ -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,

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