mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-26 01:21:57 +00:00
Index implicit function calls.
This commit is contained in:
parent
1598129d8b
commit
2e4c5474da
@ -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;
|
||||||
|
@ -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.
|
||||||
|
14
src/query.cc
14
src/query.cc
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
63
tests/constructors/implicit_constructor.cc
Normal file
63
tests/constructors/implicit_constructor.cc
Normal 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"]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
*/
|
@ -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"]
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user