[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