nridge created this revision.
nridge added a reviewer: hokein.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay, ilya-biryukov.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D72041

Files:
  clang-tools-extra/clangd/Selection.cpp
  clang-tools-extra/clangd/Selection.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -349,7 +349,18 @@
        #define ADDRESSOF(X) &X;
        int *j = ADDRESSOF(^i);
       )cpp",
-
+      R"cpp(// Macro argument appearing multiple times in expansion
+        #define VALIDATE_TYPE(x) (void)x;
+        #define ASSERT(expr)       \
+          do {                     \
+            VALIDATE_TYPE(expr);   \
+            if (!expr);            \
+          } while (false)
+        bool [[waldo]]() { return true; }
+        void foo() {
+          ASSERT(wa^ldo());
+        }
+      )cpp",
       R"cpp(// Symbol concatenated inside macro (not supported)
        int *pi;
        #define POINTER(X) p # X;
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -140,6 +140,28 @@
     auto Decls = targetDecl(N->ASTNode, Relations);
     Result.assign(Decls.begin(), Decls.end());
   }
+  // If the common ancestor didn't have a target, the individual selected
+  // nodes still might. This can happen if the selection is in a macro
+  // invocation and is over a token that appears in multiple places in
+  // the macro expansion. As long as all occurrences map to the same target,
+  // return that target.
+  if (Result.empty()) {
+    auto SelectedNodes = Selection.allSelectedNodes();
+    if (SelectedNodes.size() <= 1)
+      return {};
+    for (const SelectionTree::Node *N : SelectedNodes) {
+      auto Decls = targetDecl(N->ASTNode, Relations);
+      if (Decls.size() != 1) {
+        return Result;
+      }
+      auto Candidate = Decls[0];
+      if (Result.empty()) {
+        Result.push_back(Decls[0]);
+      } else if (Result[0] != Candidate) {
+        return {};
+      }
+    }
+  }
   return Result;
 }
 
Index: clang-tools-extra/clangd/Selection.h
===================================================================
--- clang-tools-extra/clangd/Selection.h
+++ clang-tools-extra/clangd/Selection.h
@@ -103,15 +103,15 @@
     Selection Selected;
     // Walk up the AST to get the DeclContext of this Node,
     // which is not the node itself.
-    const DeclContext& getDeclContext() const;
+    const DeclContext &getDeclContext() const;
     // Printable node kind, like "CXXRecordDecl" or "AutoTypeLoc".
     std::string kind() const;
     // If this node is a wrapper with no syntax (e.g. implicit cast), return
     // its contents. (If multiple wrappers are present, unwraps all of them).
-    const Node& ignoreImplicit() const;
+    const Node &ignoreImplicit() const;
     // If this node is inside a wrapper with no syntax (e.g. implicit cast),
     // return that wrapper. (If multiple are present, unwraps all of them).
-    const Node& outerImplicit() const;
+    const Node &outerImplicit() const;
   };
   // The most specific common ancestor of all the selected nodes.
   // Returns nullptr if the common ancestor is the root.
@@ -119,6 +119,9 @@
   const Node *commonAncestor() const;
   // The selection node corresponding to TranslationUnitDecl.
   const Node &root() const { return *Root; }
+  // Get all individual selected nodes, in case the caller wants something
+  // other than their common ancestor.
+  std::vector<const Node *> allSelectedNodes() const;
 
 private:
   std::deque<Node> Nodes; // Stable-pointer storage.
Index: clang-tools-extra/clangd/Selection.cpp
===================================================================
--- clang-tools-extra/clangd/Selection.cpp
+++ clang-tools-extra/clangd/Selection.cpp
@@ -52,8 +52,7 @@
 // On traversing an AST node, its token range is erased from the unclaimed set.
 // The tokens actually removed are associated with that node, and hit-tested
 // against the selection to determine whether the node is selected.
-template <typename T>
-class IntervalSet {
+template <typename T> class IntervalSet {
 public:
   IntervalSet(llvm::ArrayRef<T> Range) { UnclaimedRanges.insert(Range); }
 
@@ -78,7 +77,7 @@
       --Overlap.first;
       // ...unless B isn't selected at all.
       if (Overlap.first->end() <= Claim.begin())
-          ++Overlap.first;
+        ++Overlap.first;
     }
     if (Overlap.first == Overlap.second)
       return Out;
@@ -118,8 +117,7 @@
   };
 
   // Disjoint sorted unclaimed ranges of expanded tokens.
-  std::set<llvm::ArrayRef<T>, RangeLess>
-      UnclaimedRanges;
+  std::set<llvm::ArrayRef<T>, RangeLess> UnclaimedRanges;
 };
 
 // Sentinel value for the selectedness of a node where we've seen no tokens yet.
@@ -142,7 +140,6 @@
     Result = SelectionTree::Partial;
 }
 
-
 // SelectionTester can determine whether a range of tokens from the PP-expanded
 // stream (corresponding to an AST node) is considered selected.
 //
@@ -343,7 +340,7 @@
 }
 #endif
 
-bool isImplicit(const Stmt* S) {
+bool isImplicit(const Stmt *S) {
   // Some Stmts are implicit and shouldn't be traversed, but there's no
   // "implicit" attribute on Stmt/Expr.
   // Unwrap implicit casts first if present (other nodes too?).
@@ -587,7 +584,7 @@
       // int (*[[s]])();
       else if (auto *VD = llvm::dyn_cast<VarDecl>(D))
         return VD->getLocation();
-    } else if (const auto* CCI = N.get<CXXCtorInitializer>()) {
+    } else if (const auto *CCI = N.get<CXXCtorInitializer>()) {
       // : [[b_]](42)
       return CCI->getMemberLocation();
     }
@@ -704,10 +701,29 @@
   return Ancestor != Root ? Ancestor : nullptr;
 }
 
-const DeclContext& SelectionTree::Node::getDeclContext() const {
-  for (const Node* CurrentNode = this; CurrentNode != nullptr;
+namespace {
+void gatherSelectedNodes(const Node *Root, std::vector<const Node *> &Result) {
+  if (Root->Selected) {
+    Result.push_back(Root);
+    return;
+  }
+  for (const Node *Child : Root->Children) {
+    gatherSelectedNodes(Child, Result);
+  }
+}
+
+} // namespace
+
+std::vector<const Node *> SelectionTree::allSelectedNodes() const {
+  std::vector<const Node *> Result;
+  gatherSelectedNodes(Root, Result);
+  return Result;
+}
+
+const DeclContext &SelectionTree::Node::getDeclContext() const {
+  for (const Node *CurrentNode = this; CurrentNode != nullptr;
        CurrentNode = CurrentNode->Parent) {
-    if (const Decl* Current = CurrentNode->ASTNode.get<Decl>()) {
+    if (const Decl *Current = CurrentNode->ASTNode.get<Decl>()) {
       if (CurrentNode != this)
         if (auto *DC = dyn_cast<DeclContext>(Current))
           return *DC;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to