[clang-tools-extra] 81dae18 - [clangd] Allow AST request without range
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 23 12:35:56 PDT 2021
Author: Christian Kandeler
Date: 2021-04-23T21:35:42+02:00
New Revision: 81dae18dff7fe8c2783c7b73e5c08167d6c60b47
URL: https://github.com/llvm/llvm-project/commit/81dae18dff7fe8c2783c7b73e5c08167d6c60b47
DIFF: https://github.com/llvm/llvm-project/commit/81dae18dff7fe8c2783c7b73e5c08167d6c60b47.diff
LOG: [clangd] Allow AST request without range
If no range is given, return the translation unit AST.
This is useful for tooling operations that require e.g. the full path to
a node.
Reviewed By: sammccall
Differential Revision: https://reviews.llvm.org/D101057
Added:
clang-tools-extra/clangd/test/ast-no-range.test
Modified:
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/DumpAST.cpp
clang-tools-extra/clangd/DumpAST.h
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/unittests/DumpASTTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index f36c7d8cdefb7..11cd1a8fee537 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -884,22 +884,29 @@ void ClangdServer::semanticHighlights(
Transient);
}
-void ClangdServer::getAST(PathRef File, Range R,
+void ClangdServer::getAST(PathRef File, llvm::Optional<Range> R,
Callback<llvm::Optional<ASTNode>> CB) {
auto Action =
[R, CB(std::move(CB))](llvm::Expected<InputsAndAST> Inputs) mutable {
if (!Inputs)
return CB(Inputs.takeError());
+ if (!R) {
+ // It's safe to pass in the TU, as dumpAST() does not
+ // deserialize the preamble.
+ auto Node = DynTypedNode::create(
+ *Inputs->AST.getASTContext().getTranslationUnitDecl());
+ return CB(dumpAST(Node, Inputs->AST.getTokens(),
+ Inputs->AST.getASTContext()));
+ }
unsigned Start, End;
- if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R.start))
+ if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->start))
Start = *Offset;
else
return CB(Offset.takeError());
- if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R.end))
+ if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->end))
End = *Offset;
else
return CB(Offset.takeError());
-
bool Success = SelectionTree::createEach(
Inputs->AST.getASTContext(), Inputs->AST.getTokens(), Start, End,
[&](SelectionTree T) {
diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index dc1ce22463f42..7d98977a70567 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -344,7 +344,8 @@ class ClangdServer {
Callback<std::vector<HighlightingToken>>);
/// Describe the AST subtree for a piece of code.
- void getAST(PathRef File, Range R, Callback<llvm::Optional<ASTNode>> CB);
+ void getAST(PathRef File, llvm::Optional<Range> R,
+ Callback<llvm::Optional<ASTNode>> CB);
/// Runs an arbitrary action that has access to the AST of the specified file.
/// The action will execute on one of ClangdServer's internal threads.
diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index 588bcfcf2424a..40ac8afbb5000 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -335,9 +335,14 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
// Override traversal to record the nodes we care about.
// Generally, these are nodes with position information (TypeLoc, not Type).
+
bool TraverseDecl(Decl *D) {
- return !D || isInjectedClassName(D) ||
- traverseNode("declaration", D, [&] { Base::TraverseDecl(D); });
+ return !D || isInjectedClassName(D) || traverseNode("declaration", D, [&] {
+ if (isa<TranslationUnitDecl>(D))
+ Base::TraverseAST(const_cast<ASTContext &>(Ctx));
+ else
+ Base::TraverseDecl(D);
+ });
}
bool TraverseTypeLoc(TypeLoc TL) {
return !TL || traverseNode("type", TL, [&] { Base::TraverseTypeLoc(TL); });
diff --git a/clang-tools-extra/clangd/DumpAST.h b/clang-tools-extra/clangd/DumpAST.h
index 968ccbfabec69..424025aeca796 100644
--- a/clang-tools-extra/clangd/DumpAST.h
+++ b/clang-tools-extra/clangd/DumpAST.h
@@ -39,6 +39,8 @@ class TokenBuffer;
} // namespace syntax
namespace clangd {
+// Note: It's safe for the node to be a TranslationUnitDecl, as this function
+// does not deserialize the preamble.
ASTNode dumpAST(const DynTypedNode &, const syntax::TokenBuffer &Tokens,
const ASTContext &);
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 8ee0bb00396dd..2ae60910639e1 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1725,7 +1725,8 @@ struct ASTParams {
/// The position of the node to be dumped.
/// The highest-level node that entirely contains the range will be returned.
- Range range;
+ /// If no range is given, the root translation unit node will be returned.
+ llvm::Optional<Range> range;
};
bool fromJSON(const llvm::json::Value &, ASTParams &, llvm::json::Path);
diff --git a/clang-tools-extra/clangd/test/ast-no-range.test b/clang-tools-extra/clangd/test/ast-no-range.test
new file mode 100644
index 0000000000000..34adffefc91f7
--- /dev/null
+++ b/clang-tools-extra/clangd/test/ast-no-range.test
@@ -0,0 +1,53 @@
+# RUN: clangd -lit-test < %s | FileCheck %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///simple.cpp","languageId":"cpp","version":1,"text":"int x;"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/ast","params":{"textDocument":{"uri":"test:///simple.cpp"}}}
+# CHECK: "id": 1,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": {
+# CHECK-NEXT: "arcana": "{{TranslationUnitDecl.*}}"
+# CHECK-NEXT: "children": [
+# CHECK-NEXT: {
+# CHECK: "arcana": "VarDecl {{.*}} x 'int'",
+# CHECK-NEXT: "children": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "arcana": "QualType {{.*}} 'int' ",
+# CHECK-NEXT: "detail": "int",
+# CHECK-NEXT: "kind": "Builtin",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 3,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 0,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "role": "type"
+# CHECK-NEXT: }
+# CHECK-NEXT: ],
+# CHECK-NEXT: "detail": "x",
+# CHECK-NEXT: "kind": "Var",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 5,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 0,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "role": "declaration"
+# CHECK-NEXT: }
+# CHECK-NEXT: ],
+# CHECK-NEXT: "kind": "TranslationUnit",
+# CHECK-NEXT: "role": "declaration"
+# CHECK-NEXT: }
+---
+{"jsonrpc":"2.0","id":2,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
index 23c673284f660..2df310a952582 100644
--- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
@@ -16,8 +16,12 @@
namespace clang {
namespace clangd {
namespace {
+using testing::Contains;
+using testing::Not;
using testing::SizeIs;
+MATCHER_P(WithDetail, str, "") { return arg.detail == str; }
+
TEST(DumpASTTests, BasicInfo) {
std::pair</*Code=*/std::string, /*Expected=*/std::string> Cases[] = {
{R"cpp(
@@ -157,6 +161,20 @@ TEST(DumpASTTests, Range) {
EXPECT_EQ(Node.children.front().range, Case.range("type"));
}
+TEST(DumpASTTests, NoRange) {
+ auto TU = TestTU::withHeaderCode("void funcFromHeader();");
+ TU.Code = "int varFromSource;";
+ ParsedAST AST = TU.build();
+ auto Node = dumpAST(
+ DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()),
+ AST.getTokens(), AST.getASTContext());
+ ASSERT_THAT(Node.children, Contains(WithDetail("varFromSource")));
+ ASSERT_THAT(Node.children, Not(Contains(WithDetail("funcFromHeader"))));
+ EXPECT_THAT(Node.arcana, testing::StartsWith("TranslationUnitDecl "));
+ ASSERT_FALSE(Node.range.hasValue())
+ << "Expected no range for translation unit";
+}
+
TEST(DumpASTTests, Arcana) {
ParsedAST AST = TestTU::withCode("int x;").build();
auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "x")), AST.getTokens(),
More information about the cfe-commits
mailing list