Skip to content

Commit 8cb52dc

Browse files
committed
Handle macros
1 parent 98027f7 commit 8cb52dc

File tree

6 files changed

+182
-38
lines changed

6 files changed

+182
-38
lines changed

include/Feature/Hover.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct Hover {
5757
std::vector<HoverItem> items;
5858

5959
/// Raw document in the source code.
60-
std::string document;
60+
std::optional<std::string> document;
6161

6262
/// The full qualified name of the declaration.
6363
std::string qualifier;

include/Protocol/Feature/Hover.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct Hover {
1717
/// An optional range is a range inside a text document
1818
/// that is used to visualize a hover, e.g. by changing the background color.
1919
/// FIXME: Range range;
20+
std::optional<Range> range;
2021
};
2122

2223
} // namespace clice::proto

src/Feature/Hover.cpp

Lines changed: 95 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
#include "Feature/Hover.h"
12
#include "AST/Selection.h"
23
#include "AST/Semantic.h"
34
#include "AST/Utility.h"
45
#include "Compiler/CompilationUnit.h"
56
#include "Index/Shared.h"
67
#include "Support/Compare.h"
78
#include "Support/Ranges.h"
8-
#include "Feature/Hover.h"
99
#include "Support/Logging.h"
10+
#include "Support/Struct.h"
11+
#include "Support/Doxygen.h"
1012
#include "llvm/Support/raw_ostream.h"
1113
#include "clang/Lex/Lexer.h"
1214
#include "clang/AST/ASTTypeTraits.h"
@@ -15,6 +17,16 @@ namespace clice::feature {
1517

1618
namespace {
1719

20+
static auto to_proto_range(clang::SourceManager& sm, clang::SourceRange range) -> proto::Range {
21+
auto range_b = range.getBegin();
22+
auto range_e = range.getEnd();
23+
auto begin = proto::Position{sm.getSpellingLineNumber(range_b) - 1,
24+
sm.getSpellingColumnNumber(range_b) - 1};
25+
auto end = proto::Position{sm.getSpellingLineNumber(range_e) - 1,
26+
sm.getSpellingColumnNumber(range_e) - 1};
27+
return {begin, end};
28+
};
29+
1830
static std::vector<HoverItem> get_hover_items(CompilationUnit& unit,
1931
const clang::NamedDecl* decl,
2032
const config::HoverOptions& opt) {
@@ -134,6 +146,7 @@ static std::optional<Hover> hover(CompilationUnit& unit,
134146
clice::logging::warn("Hit a typeloc");
135147
typeloc->dump(llvm::errs(), unit.context());
136148
auto ty = typeloc->getType();
149+
// FIXME: AutoTypeLoc / DecltypeTypeLoc
137150
return Hover{.kind = SymbolKind::Type, .name = ty.getAsString()};
138151
}
139152

@@ -150,6 +163,8 @@ static std::optional<Hover> hover(CompilationUnit& unit,
150163
kind_flag_def(Decl);
151164
kind_flag_def(Stmt);
152165
kind_flag_def(Type);
166+
kind_flag_def(AutoTypeLoc);
167+
kind_flag_def(DecltypeTypeLoc);
153168
kind_flag_def(OMPClause);
154169
kind_flag_def(TemplateArgument);
155170
kind_flag_def(TemplateArgumentLoc);
@@ -169,6 +184,15 @@ static std::optional<Hover> hover(CompilationUnit& unit,
169184
} else if(is_in_range(QualType, TypeLoc)) {
170185
// Typeloc
171186
clice::logging::warn("Hit a `TypeLoc`");
187+
// auto and decltype is specially processed
188+
if(is(AutoTypeLoc)) {
189+
clice::logging::warn("Hit a `AutoTypeLoc`");
190+
return std::nullopt;
191+
}
192+
if(is(DecltypeTypeLoc)) {
193+
clice::logging::warn("Hit a `DecltypeTypeLoc`");
194+
return std::nullopt;
195+
}
172196
if(auto typeloc = node->get<clang::TypeLoc>()) {
173197
return hover(unit, typeloc, opt);
174198
}
@@ -202,24 +226,29 @@ std::optional<Hover> hover(CompilationUnit& unit,
202226
const config::HoverOptions& opt) {
203227
auto& sm = unit.context().getSourceManager();
204228

205-
auto src_loc_in_main_file = [&sm](uint32_t off) -> std::optional<clang::SourceLocation> {
206-
return sm.getLocForStartOfFile(sm.getMainFileID()).getLocWithOffset(off);
229+
auto src_loc_in_main_file = [&sm, &unit](uint32_t off) -> std::optional<clang::SourceLocation> {
230+
auto fid = sm.getMainFileID();
231+
auto buf = sm.getBufferData(fid);
232+
if(off > buf.size()) {
233+
return std::nullopt;
234+
}
235+
return unit.create_location(fid, off);
207236
};
208237

238+
// SpellLoc
209239
auto loc = src_loc_in_main_file(offset);
210240
if(!loc.has_value()) {
211-
clice::logging::warn("Invalid file offset, cannot get location");
212241
return std::nullopt;
213242
}
214243

215244
// Handle includsions
216245
bool linenr_invalid = false;
217246
unsigned linenr = sm.getPresumedLineNumber(*loc, &linenr_invalid);
218247
if(linenr_invalid) {
219-
clice::logging::warn("Invalid location, cannot get linenr");
220248
return std::nullopt;
221249
}
222-
clice::logging::warn("Hover at linenr: {}", linenr);
250+
251+
// FIXME: Cannot handle pch: cannot find records when compiled with pch
223252
auto directive = unit.directives()[sm.getMainFileID()];
224253
for(auto& inclusion: directive.includes) {
225254
bool invalid = false;
@@ -237,9 +266,59 @@ std::optional<Hover> hover(CompilationUnit& unit,
237266
}
238267
}
239268

269+
// clice::logging::warn("Hit a macro");
270+
auto tokens_under_cursor = unit.spelled_tokens_touch(*loc);
271+
if(tokens_under_cursor.empty()) {
272+
clice::logging::warn("Cannot detect tokens");
273+
return std::nullopt;
274+
}
275+
auto hl_range = tokens_under_cursor.back().range(sm).toCharRange(sm).getAsRange();
276+
for(auto& token: tokens_under_cursor) {
277+
if(token.kind() == clang::tok::identifier) {
278+
for(auto& m: directive.macros) {
279+
if(token.location() == m.loc) {
280+
// TODO: Found macro
281+
auto name_range = token.range(sm).toCharRange(sm).getAsRange();
282+
auto macro_name = get_source_code(unit, name_range);
283+
macro_name.pop_back();
284+
Hover hi;
285+
hi.kind = SymbolKind::Macro;
286+
hi.name = macro_name;
287+
auto source = "#define " + get_source_code(unit,
288+
{m.macro->getDefinitionLoc(),
289+
m.macro->getDefinitionEndLoc()});
290+
if(m.kind == MacroRef::Ref) {
291+
// TODO: Expanded tokens
292+
if(auto expansion = unit.token_buffer().expansionStartingAt(&token)) {
293+
std::string expaned_source;
294+
for(const auto& expanded_tok: expansion->Expanded) {
295+
expaned_source += expanded_tok.text(sm);
296+
// TODO: Format code?
297+
// expaned_source += ' ';
298+
// TODO: Config field: expansion display size
299+
}
300+
if(!expaned_source.empty()) {
301+
source += "\n\n// Expands to:\n";
302+
source += expaned_source;
303+
source += '\n';
304+
}
305+
}
306+
}
307+
hi.source = source;
308+
hi.hl_range = to_proto_range(sm, hl_range);
309+
return hi;
310+
}
311+
}
312+
}
313+
}
314+
240315
auto tree = SelectionTree::create_right(unit, {offset, offset});
241316
if(auto node = tree.common_ancestor()) {
242-
return hover(unit, node, opt);
317+
if(auto info = hover(unit, node, opt)) {
318+
info->hl_range = to_proto_range(sm, hl_range);
319+
return info;
320+
}
321+
return std::nullopt;
243322
} else {
244323
clice::logging::warn("Not an ast node");
245324
}
@@ -259,15 +338,20 @@ std::optional<std::string> Hover::get_item_content(HoverItem::HoverKind kind) {
259338
std::optional<std::string> Hover::display(config::HoverOptions opt) {
260339
std::string content;
261340
llvm::raw_string_ostream os(content);
341+
// TODO: generate markdown
262342
os << std::format("{}: {}\n", this->kind.name(), this->name);
263343
os << std::format("Contains {} items\n", this->items.size());
264344
for(auto& hi: this->items) {
265345
os << std::format("- {}: {}\n", clice::refl::enum_name(hi.kind), hi.value);
266346
}
267-
os << "---\n";
268-
os << "Document:\n```text\n" << this->document << "\n```\n";
269-
os << "---\n";
270-
os << "Source code:\n```cpp\n" << this->source << "\n```\n";
347+
if(this->document) {
348+
os << "---\n";
349+
os << "Document:\n```text\n" << *this->document << "\n```\n";
350+
}
351+
if(!this->source.empty()) {
352+
os << "---\n";
353+
os << "Source code:\n```cpp\n" << this->source << "\n```\n";
354+
}
271355
return os.str();
272356
}
273357

src/Server/Feature.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,17 @@ auto Server::on_hover(proto::HoverParams params) -> Result {
6565
co_return co_await async::submit([kind = this->kind, offset, &ast, &opt] {
6666
proto::Hover result;
6767
result.contents.kind = "markdown";
68-
if(auto hover = feature::hover(*ast, offset, opt); hover) {
69-
if(auto info = hover->display(opt); info) {
68+
if(auto hover = feature::hover(*ast, offset, opt)) {
69+
if(auto info = hover->display(opt)) {
7070
result.contents.value = *info;
7171
} else {
7272
clice::logging::warn("Cannot display hover info");
7373
result.contents.value = "Cannot display hover info";
7474
}
75+
if(!hover->hl_range) {
76+
clice::logging::warn("Not available range");
77+
}
78+
result.range = hover->hl_range;
7579
} else {
7680
clice::logging::warn("Cannot get hover info");
7781
result.contents.value = "Cannot get hover info";

src/Server/Lifecycle.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ async::Task<json::Value> Server::on_initialize(proto::InitializeParams params) {
3131
/// Set server options.
3232
opening_files.set_capability(config.project.max_active_file);
3333

34-
/// Load compile commands.json
35-
database.load_compile_database(config.project.compile_commands_dirs, workspace);
34+
/// /// Load compile commands.json
35+
/// database.load_compile_database(config.project.compile_commands_dirs, workspace);
3636

3737
/// Load cache info.
3838
load_cache_info();

tests/unit/Feature/Hover.cpp

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ suite<"Hover"> hover = [] {
3333
};
3434

3535
test("Inclusion") = [&tester] {
36-
tester.clear();
37-
constexpr auto code = R"CODE(
36+
{
37+
tester.clear();
38+
constexpr auto code = R"CODE(
3839
$(inc1)#include <iostream>
3940
4041
#include $(inc2)<assert.h>
@@ -44,14 +45,71 @@ int main(void) {
4445
return 0;
4546
}
4647
)CODE";
48+
auto annotation = AnnotatedSource::from(code);
49+
tester.add_main("main.cpp", annotation.content);
50+
auto inc1_off = annotation.offsets["inc1"];
51+
auto inc2_off = annotation.offsets["inc2"];
52+
tester.compile_with_pch();
53+
expect(tester.unit.has_value());
54+
auto HI = clice::feature::hover(*tester.unit, inc1_off, {});
55+
HI = clice::feature::hover(*tester.unit, inc2_off, {});
56+
}
57+
58+
{
59+
tester.clear();
60+
constexpr auto code = R"CODE(
61+
#inc$(inc1)lude <assert.h>
62+
#inc$(inc2)lude <stdio.h>
63+
64+
#define max(a, b) ((a) > (b) ? (a) : (b))
65+
66+
int main(int argc, char **argv) {
67+
assert(argc <= 10);
68+
int x = max(114, 514);
69+
printf("%d", max(argc, x));
70+
return 0;
71+
}
72+
)CODE";
73+
auto annotation = AnnotatedSource::from(code);
74+
tester.add_main("main.cpp", annotation.content);
75+
auto inc1_off = annotation.offsets["inc1"];
76+
auto inc2_off = annotation.offsets["inc2"];
77+
tester.compile();
78+
expect(tester.unit.has_value());
79+
auto HI = clice::feature::hover(*tester.unit, inc1_off, {});
80+
HI = clice::feature::hover(*tester.unit, inc2_off, {});
81+
}
82+
};
83+
84+
test("Macros") = [&] {
85+
tester.clear();
86+
constexpr auto code = R"CODE(
87+
#include <iostream>
88+
89+
using namespace std;
90+
91+
#define m$(pos_0)ax(a, b) ((a) > (b) ? (a) : (b))
92+
#define P$(pos_1)I 3.14
93+
#define I$(pos_2)NF 1e18
94+
#define D$(pos_3)UMMY
95+
#define a$(pos_4)bs(a) ((a) >= 0 ? (a) : -(a))
96+
97+
int main(int argc, char **argv) {
98+
int x, y;
99+
cin >> x >> y;
100+
#define en$(pos_5)dl '\n'
101+
cout << x * P$(pos_6)I << endl;
102+
cout << (x < 0721 ? "ciallo" : "hello") << e$(pos_7)ndl;
103+
#undef endl
104+
cout << ma$(pos_8)x(x$(pos_9), y) << endl;
105+
cout << m$(pos_10)ax(a$(pos_11)bs(x), abs(y)) << '\n';
106+
constexpr auto X = INF;
107+
return 0;
108+
}
109+
)CODE";
47110
auto annotation = AnnotatedSource::from(code);
48111
tester.add_main("main.cpp", annotation.content);
49-
auto inc1_off = annotation.offsets["inc1"];
50-
auto inc2_off = annotation.offsets["inc2"];
51-
tester.compile();
52-
expect(tester.unit.has_value());
53-
auto HI = clice::feature::hover(*tester.unit, inc1_off, {});
54-
HI = clice::feature::hover(*tester.unit, inc2_off, {});
112+
for(unsigned idx = 0; idx < annotation.offsets.size(); ++idx) {}
55113
};
56114

57115
test("DeducedType_Auto") = [&tester] {
@@ -98,19 +156,20 @@ int main(void) {
98156
std::vector<unsigned> offsets;
99157
tester.compile();
100158
expect(tester.unit.has_value());
101-
for(auto offset: offsets) {
159+
const unsigned count = annotation.offsets.size();
160+
for(unsigned i = 0; i < count; ++i) {
161+
unsigned offset = annotation.offsets[std::format("pos_{}", i)];
102162
auto HI = clice::feature::hover(*tester.unit, offset, {});
103163
if(HI.has_value()) {
104164
auto msg = HI->display({});
105-
// std::println("{}", *msg);
165+
std::println("```\n{}```\n", *msg);
106166
} else {
107-
// std::println("No hover info");
167+
std::println("No hover info");
108168
}
109169
}
110170
};
111171

112172
test("DeducedType_Decltype") = [&] {
113-
std::println("==================================");
114173
tester.clear();
115174
constexpr auto code = R"CODE(
116175
#include <utility>
@@ -155,7 +214,7 @@ int main() {
155214
return ca;
156215
};
157216
declt$(pos_12)ype(auto) r1 = get_a(); // int&
158-
decltyppos_13)ype(auto) r2 = get_ca(); // const int
217+
decltyp$(pos_13)ype(auto) r2 = get_ca(); // const int
159218
160219
// auto vs decltype(auto)
161220
auto x1 = (a); // int
@@ -170,23 +229,19 @@ int main() {
170229

171230
auto annotation = AnnotatedSource::from(code);
172231
tester.add_main("main.cpp", annotation.content);
173-
std::vector<unsigned> offsets;
174232
tester.compile();
175233
expect(tester.unit.has_value());
176-
int counter = 0;
177-
for(auto offset: offsets) {
178-
std::println("Processing pos_{}", counter);
234+
const unsigned count = annotation.offsets.size();
235+
for(unsigned i = 0; i < count; ++i) {
236+
unsigned offset = annotation.offsets[std::format("pos_{}", i)];
179237
auto HI = clice::feature::hover(*tester.unit, offset, {});
180238
if(HI.has_value()) {
181239
auto msg = HI->display({});
182-
// std::println("{}", *msg);
240+
std::println("```\n{}```\n", *msg);
183241
} else {
184-
// std::println("No hover info");
242+
std::println("No hover info");
185243
}
186-
++counter;
187244
}
188-
189-
std::println("==================================");
190245
};
191246

192247
test("Namespace") = [&] {

0 commit comments

Comments
 (0)