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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits