diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -344,7 +344,8 @@ Callback>); /// Describe the AST subtree for a piece of code. - void getAST(PathRef File, Range R, Callback> CB); + void getAST(PathRef File, const llvm::Optional &R, + Callback> 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/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -884,22 +884,25 @@ Transient); } -void ClangdServer::getAST(PathRef File, Range R, +void ClangdServer::getAST(PathRef File, const llvm::Optional &R, Callback> CB) { auto Action = [R, CB(std::move(CB))](llvm::Expected Inputs) mutable { if (!Inputs) return CB(Inputs.takeError()); + if (!R) { + 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/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1725,7 +1725,9 @@ /// 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 top-level translation unit node will be + /// returned. + llvm::Optional 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 --- /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 --- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp @@ -157,6 +157,18 @@ EXPECT_EQ(Node.children.front().range, Case.range("type")); } +TEST(DumpASTTests, NoRange) { + ParsedAST AST = TestTU::withCode("int x;").build(); + auto Node = dumpAST( + DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()), + AST.getTokens(), AST.getASTContext()); + EXPECT_THAT(Node.arcana, testing::StartsWith("TranslationUnitDecl ")); + ASSERT_THAT(Node.children, SizeIs(testing::Ge(1u))) + << "Expected at least one child node"; + 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(),