Better symbol resolution (ie, goto definition) for macro arguments.

This commit is contained in:
Jacob Dufault 2017-06-15 23:43:02 -07:00
parent f8f4c06c20
commit 11af3986ba
5 changed files with 103 additions and 8 deletions

View File

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

View File

@ -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") {

View File

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

View File

@ -404,7 +404,8 @@ TEST_CASE("auto-insert )") {
} }
TEST_CASE("existing completion") { TEST_CASE("existing completion") {
WorkingFile f("foo.cc", "zzz.asdf"); // 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 ");
bool is_global_completion; bool is_global_completion;
std::string existing_completion; std::string existing_completion;

59
tests/macros/complex.cc Normal file
View 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"]
}]
}
*/