Skip to content

Commit 295dc86

Browse files
committed
chore: Improve AST traversal
Signed-off-by: Roberto Raggi <[email protected]>
1 parent cb65365 commit 295dc86

File tree

6 files changed

+139
-36
lines changed

6 files changed

+139
-36
lines changed

src/frontend/cxx/cxx_document.cc

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,45 @@ struct Diagnostics final : cxx::DiagnosticsClient {
6666
}
6767
};
6868

69+
struct Visit {
70+
CxxDocument* doc_;
71+
std::function<bool(AST*)> preVisit;
72+
std::function<void(AST*)> postVisit;
73+
ASTSlot slotInfo;
74+
75+
Visit(CxxDocument* doc, std::function<bool(AST*)> preVisit,
76+
std::function<void(AST*)> postVisit)
77+
: doc_(doc),
78+
preVisit(std::move(preVisit)),
79+
postVisit(std::move(postVisit)) {}
80+
81+
void operator()(AST* ast) {
82+
if (!ast) return;
83+
84+
if (preVisit(ast)) {
85+
// do a pre-visit using the low-level AST API
86+
87+
const auto slotCount = slotInfo(ast, 0).slotCount;
88+
89+
for (int index = 0; index < slotCount; ++index) {
90+
const auto childInfo = slotInfo(ast, index);
91+
92+
if (childInfo.kind == ASTSlotKind::kNode) {
93+
auto child = reinterpret_cast<AST*>(childInfo.handle);
94+
if (child) operator()(child);
95+
} else if (childInfo.kind == ASTSlotKind::kNodeList) {
96+
auto list = reinterpret_cast<List<AST*>*>(childInfo.handle);
97+
for (auto node : ListView{list}) {
98+
operator()(node);
99+
}
100+
}
101+
}
102+
}
103+
104+
if (postVisit) postVisit(ast);
105+
}
106+
};
107+
69108
} // namespace
70109

71110
struct CxxDocument::Private {
@@ -209,43 +248,34 @@ auto CxxDocument::translationUnit() const -> TranslationUnit* {
209248
return &d->unit;
210249
}
211250

212-
namespace {
213-
214-
struct Visit {
215-
CxxDocument* doc_;
216-
std::function<bool(AST*)> visitor;
217-
ASTSlot slotInfo;
218-
219-
void preVisit(AST* ast) {
220-
if (!ast) return;
251+
auto CxxDocument::textOf(AST* ast) -> std::optional<std::string_view> {
252+
return textInRange(ast->firstSourceLocation(), ast->lastSourceLocation());
253+
}
221254

222-
if (visitor(ast)) return;
255+
auto CxxDocument::textInRange(SourceLocation start, SourceLocation end)
256+
-> std::optional<std::string_view> {
257+
auto& unit = d->unit;
258+
auto preprocessor = unit.preprocessor();
223259

224-
// do a pre-visit using the low-level AST API
260+
const auto startToken = unit.tokenAt(start);
261+
const auto endToken = unit.tokenAt(end.previous());
225262

226-
const auto slotCount = slotInfo(ast, 0).slotCount;
263+
if (startToken.fileId() != endToken.fileId()) {
264+
return std::nullopt;
265+
}
227266

228-
for (int index = 0; index < slotCount; ++index) {
229-
const auto childInfo = slotInfo(ast, index);
267+
std::string_view source = preprocessor->source(startToken.fileId());
230268

231-
if (childInfo.kind == ASTSlotKind::kNode) {
232-
auto child = reinterpret_cast<AST*>(childInfo.handle);
233-
if (child) preVisit(child);
234-
} else if (childInfo.kind == ASTSlotKind::kNodeList) {
235-
auto list = reinterpret_cast<List<AST*>*>(childInfo.handle);
236-
for (auto node : ListView{list}) {
237-
preVisit(node);
238-
}
239-
}
240-
}
241-
}
242-
};
269+
const auto offset = startToken.offset();
270+
const auto length = endToken.offset() + endToken.length() - offset;
243271

244-
} // namespace
272+
return source.substr(offset, length);
273+
}
245274

246-
void CxxDocument::preVisit(std::function<bool(AST*)> visitor) {
275+
void CxxDocument::traverse(std::function<bool(AST*)> preVisit,
276+
std::function<void(AST*)> postVisit) {
247277
auto ast = d->unit.ast();
248-
Visit{this, std::move(visitor)}.preVisit(ast);
278+
std::invoke(Visit{this, std::move(preVisit), std::move(postVisit)}, ast);
249279
}
250280

251281
} // namespace cxx::lsp

src/frontend/cxx/cxx_document.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ class CxxDocument {
4343

4444
[[nodiscard]] auto translationUnit() const -> TranslationUnit*;
4545

46-
void preVisit(std::function<bool(AST*)> visitor);
46+
[[nodiscard]] auto textOf(AST* ast) -> std::optional<std::string_view>;
47+
48+
[[nodiscard]] auto textInRange(SourceLocation start, SourceLocation end)
49+
-> std::optional<std::string_view>;
50+
51+
void traverse(std::function<bool(AST*)> preVisit,
52+
std::function<void(AST*)> postVisit = {});
4753

4854
private:
4955
struct Private;

src/frontend/cxx/lsp_server.cc

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@
2020

2121
#include "lsp_server.h"
2222

23+
#include <cxx/ast.h>
2324
#include <cxx/lsp/enums.h>
2425
#include <cxx/lsp/requests.h>
2526
#include <cxx/lsp/types.h>
27+
#include <cxx/preprocessor.h>
2628
#include <utf8/unchecked.h>
2729

30+
#ifdef __GNUC__
31+
#include <cxxabi.h>
32+
#endif
33+
2834
#include <format>
2935
#include <iostream>
3036
#include <unordered_map>
@@ -433,12 +439,41 @@ void Server::operator()(DocumentSymbolRequest request) {
433439

434440
auto uri = request.params().textDocument().uri();
435441
auto doc = latestDocument(uri);
442+
auto id = request.id();
436443

437-
withUnsafeJson([&](json storage) {
438-
DocumentSymbolResponse response(storage);
439-
response.id(request.id());
440-
(void)response.result();
441-
sendToClient(response);
444+
run([=, this] {
445+
// sanity check
446+
447+
int depth = 0;
448+
449+
auto preVisit = [&](AST* ast) -> bool {
450+
#if false
451+
auto node = typeid(*ast).name();
452+
#ifdef __GNUC__
453+
node = abi::__cxa_demangle(node, nullptr, nullptr, nullptr);
454+
#endif
455+
std::cerr << std::format("{:{}}{}\n", "", depth * 2, node);
456+
#endif
457+
++depth;
458+
return true;
459+
};
460+
461+
auto postVisit = [&](AST*) { --depth; };
462+
463+
if (doc) {
464+
doc->traverse(preVisit, postVisit);
465+
}
466+
467+
if (depth != 0) {
468+
lsp_runtime_error("Unexpected depth at the end of the traversal");
469+
}
470+
471+
withUnsafeJson([&](json storage) {
472+
DocumentSymbolResponse response(storage);
473+
response.id(id);
474+
(void)response.result();
475+
sendToClient(response);
476+
});
442477
});
443478
}
444479

@@ -462,7 +497,7 @@ void Server::operator()(SetTraceNotification notification) {
462497
trace_ = notification.params().value();
463498

464499
if (trace_ != TraceValue::kOff) {
465-
logTrace("Trace level set to {}", to_string(trace_));
500+
logTrace(std::format("Trace level set to {}", to_string(trace_)));
466501
return;
467502
}
468503
}

src/parser/cxx/preprocessor.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,17 @@ void Preprocessor::setOnWillIncludeHeader(
27962796

27972797
void Preprocessor::squeeze() { d->pool_.reset(); }
27982798

2799+
auto Preprocessor::sourceFileName(uint32_t sourceFileId) const
2800+
-> const std::string & {
2801+
assert(sourceFileId > 0);
2802+
return d->sourceFiles_[sourceFileId - 1]->fileName;
2803+
}
2804+
2805+
auto Preprocessor::source(uint32_t sourceFileId) const -> const std::string & {
2806+
assert(sourceFileId > 0);
2807+
return d->sourceFiles_[sourceFileId - 1]->source;
2808+
}
2809+
27992810
void Preprocessor::preprocess(std::string source, std::string fileName,
28002811
std::vector<Token> &tokens) {
28012812
struct {

src/parser/cxx/preprocessor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ class Preprocessor {
119119
[[nodiscard]] auto continuePreprocessing(std::vector<Token> &outputTokens)
120120
-> Status;
121121

122+
[[nodiscard]] auto sourceFileName(uint32_t sourceFileId) const
123+
-> const std::string &;
124+
125+
[[nodiscard]] auto source(uint32_t sourceFileId) const -> const std::string &;
126+
122127
void preprocess(std::string source, std::string fileName,
123128
std::vector<Token> &tokens);
124129

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# RUN: %cxx -lsp-test < %s | %filecheck %s
2+
3+
{ "method": "initialize", "id": 0 }
4+
5+
{ "method": "textDocument/didOpen", "id": 1, "params": { "textDocument": { "uri": "test:///source.cc", "version": 0, "text": "#include <cstdio>\nauto main() -> int;" } } }
6+
7+
{ "method": "$/setTrace", "id": 2, "params": { "value": "verbose" } }
8+
9+
{ "method": "textDocument/documentSymbol", "id": 3, "params": { "textDocument": { "uri": "test:///source.cc" } } }
10+
11+
{ "method": "shutdown", "id": 4 }
12+
13+
{ "method": "exit" }
14+
15+
16+

0 commit comments

Comments
 (0)