diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -12,6 +12,7 @@ #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/TypeLoc.h" @@ -245,6 +246,10 @@ if (canSafelySkipNode(N)) return false; push(std::move(N)); + if (shouldSkipChildren(X)) { + pop(); + return false; + } return true; } bool dataTraverseStmtPost(Stmt *X) { @@ -355,6 +360,15 @@ return true; } + // There are certain nodes we want to treat as leaves in the SelectionTree, + // although they do have children. + bool shouldSkipChildren(const Stmt *X) const { + // UserDefinedLiteral (e.g. 12_i) has two children (12 and _i). + // Unfortunately TokenBuffer sees 12_i as one token and can't split it. + // So we treat UserDefinedLiteral as a leaf node, owning the token. + return llvm::isa(X); + } + // Pushes a node onto the ancestor stack. Pairs with pop(). // Performs early hit detection for some nodes (on the earlySourceRange). void push(DynTypedNode Node) { diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -304,6 +304,16 @@ } )cpp", "CallExpr"}, + + // User-defined literals are tricky: is 12_i one token or two? + // For now we treat it as one, and the UserDefinedLiteral as a leaf. + { + R"cpp( + struct Foo{}; + Foo operator""_ud(unsigned long long); + Foo x = [[^12_ud]]; + )cpp", + "UserDefinedLiteral"}, }; for (const Case &C : Cases) { Annotations Test(C.Code);