mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
@ -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.
|
||||
|
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);
|
||||
}
|
||||
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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
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)",
|
||||
"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"]
|
||||
}]
|
||||
}
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user