mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-26 01:21:57 +00:00
Better symbol resolution (ie, goto definition) for macro arguments.
This commit is contained in:
parent
f8f4c06c20
commit
11af3986ba
@ -39,6 +39,7 @@ optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic) {
|
|||||||
|
|
||||||
lsDiagnostic ls_diagnostic;
|
lsDiagnostic ls_diagnostic;
|
||||||
|
|
||||||
|
// TODO: consider using clang_getDiagnosticRange
|
||||||
// TODO: ls_diagnostic.range is lsRange, we have Range. We should only be
|
// TODO: ls_diagnostic.range is lsRange, we have Range. We should only be
|
||||||
// storing Range types when inside the indexer so that index <-> buffer
|
// storing Range types when inside the indexer so that index <-> buffer
|
||||||
// remapping logic is applied.
|
// remapping logic is applied.
|
||||||
|
@ -2,17 +2,22 @@
|
|||||||
|
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
int GetOffsetForPosition(lsPosition position, const std::string& content) {
|
int GetOffsetForPosition(lsPosition position, const std::string& content) {
|
||||||
|
if (content.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
int remaining_lines = position.line;
|
int remaining_lines = position.line;
|
||||||
while (remaining_lines > 0) {
|
while (remaining_lines > 0 && offset < content.size()) {
|
||||||
if (content[offset] == '\n')
|
if (content[offset] == '\n')
|
||||||
--remaining_lines;
|
--remaining_lines;
|
||||||
++offset;
|
++offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset + position.character;
|
return std::min<int>(offset + position.character, content.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lsPosition CharPos(const std::string& search, char character, int character_offset) {
|
lsPosition CharPos(const std::string& search, char character, int character_offset) {
|
||||||
@ -204,6 +209,16 @@ bool SubstringMatch(const std::string& search, const std::string& content) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE("Offset");
|
||||||
|
|
||||||
|
TEST_CASE("over range") {
|
||||||
|
std::string content = "foo";
|
||||||
|
int offset = GetOffsetForPosition(lsPosition(10, 10), content);
|
||||||
|
REQUIRE(offset < content.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
||||||
|
|
||||||
TEST_SUITE("Substring");
|
TEST_SUITE("Substring");
|
||||||
|
|
||||||
TEST_CASE("match") {
|
TEST_CASE("match") {
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Computes roughly how long |range| is.
|
||||||
|
int ComputeRangeSize(const Range& range) {
|
||||||
|
if (range.start.line != range.end.line)
|
||||||
|
return INT_MAX;
|
||||||
|
return range.end.column - range.start.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryTypeId& id) {
|
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryTypeId& id) {
|
||||||
optional<QueryType>& type = db->types[id.id];
|
optional<QueryType>& type = db->types[id.id];
|
||||||
if (type)
|
if (type)
|
||||||
@ -550,12 +561,20 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file, QueryFil
|
|||||||
symbols.push_back(ref);
|
symbols.push_back(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Order function symbols first. This makes goto definition work better when
|
// Order shorter ranges first, since they are more detailed/precise. This is
|
||||||
// used on a constructor.
|
// important for macros which generate code so that we can resolving the
|
||||||
|
// macro argument takes priority over the entire macro body.
|
||||||
|
//
|
||||||
|
// Order functions before other types, which makes goto definition work
|
||||||
|
// better on constructors.
|
||||||
std::sort(symbols.begin(), symbols.end(), [](const SymbolRef& a, const SymbolRef& b) {
|
std::sort(symbols.begin(), symbols.end(), [](const SymbolRef& a, const SymbolRef& b) {
|
||||||
if (a.idx.kind != b.idx.kind && a.idx.kind == SymbolKind::Func)
|
int a_size = ComputeRangeSize(a.loc.range);
|
||||||
return 1;
|
int b_size = ComputeRangeSize(b.loc.range);
|
||||||
return 0;
|
|
||||||
|
if (a_size == b_size)
|
||||||
|
return a.idx.kind != b.idx.kind && a.idx.kind == SymbolKind::Func;
|
||||||
|
|
||||||
|
return a_size < b_size;
|
||||||
});
|
});
|
||||||
|
|
||||||
return symbols;
|
return symbols;
|
||||||
|
@ -404,6 +404,7 @@ TEST_CASE("auto-insert )") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("existing completion") {
|
TEST_CASE("existing completion") {
|
||||||
|
// TODO: remove trailing space in zz.asdf. Lexing doesn't work correctly if done at the end of input.
|
||||||
WorkingFile f("foo.cc", "zzz.asdf ");
|
WorkingFile f("foo.cc", "zzz.asdf ");
|
||||||
bool is_global_completion;
|
bool is_global_completion;
|
||||||
std::string existing_completion;
|
std::string existing_completion;
|
||||||
|
59
tests/macros/complex.cc
Normal file
59
tests/macros/complex.cc
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#define FOO(aaa, bbb) \
|
||||||
|
int a();\
|
||||||
|
int a() { return aaa + bbb; }
|
||||||
|
|
||||||
|
|
||||||
|
int make1() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
const int make2 = 5;
|
||||||
|
|
||||||
|
|
||||||
|
FOO(make1(), make2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
{
|
||||||
|
"funcs": [{
|
||||||
|
"id": 0,
|
||||||
|
"usr": "c:@F@make1#",
|
||||||
|
"short_name": "make1",
|
||||||
|
"detailed_name": "int make1()",
|
||||||
|
"definition_spelling": "6:5-6:10",
|
||||||
|
"definition_extent": "6:1-8:2",
|
||||||
|
"callers": ["1@12:5-12:10"]
|
||||||
|
}, {
|
||||||
|
"id": 1,
|
||||||
|
"usr": "c:@F@a#",
|
||||||
|
"short_name": "a",
|
||||||
|
"detailed_name": "int a()",
|
||||||
|
"declarations": [{
|
||||||
|
"spelling": "12:1-12:20",
|
||||||
|
"extent": "12:1-12:20",
|
||||||
|
"content": "int a();\n int a() { return aaa + bbb; }\n\n\n int make1() {\n return 3;\n }\n const int make2 = 5;\n\n\n FOO(make1(), make2)"
|
||||||
|
}],
|
||||||
|
"definition_spelling": "12:1-12:20",
|
||||||
|
"definition_extent": "12:1-12:20",
|
||||||
|
"callees": ["0@12:5-12:10"]
|
||||||
|
}],
|
||||||
|
"vars": [{
|
||||||
|
"id": 0,
|
||||||
|
"usr": "c:complex.cc@make2",
|
||||||
|
"short_name": "make2",
|
||||||
|
"detailed_name": "const int make2",
|
||||||
|
"definition_spelling": "9:11-9:16",
|
||||||
|
"definition_extent": "9:1-9:20",
|
||||||
|
"is_local": false,
|
||||||
|
"uses": ["9:11-9:16", "12:14-12:19"]
|
||||||
|
}, {
|
||||||
|
"id": 1,
|
||||||
|
"usr": "c:complex.cc@8@macro@FOO",
|
||||||
|
"short_name": "FOO",
|
||||||
|
"detailed_name": "FOO",
|
||||||
|
"definition_spelling": "1:9-1:12",
|
||||||
|
"definition_extent": "1:9-3:32",
|
||||||
|
"is_local": false,
|
||||||
|
"uses": ["1:9-1:12", "12:1-12:4"]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user