[clangd] Fix SelectionTree traversal of qualified types

Summary:
QualifiedTypeLoc isn't treated like a regular citizen by RecursiveASTVisitor.
This meant we weren't intercepting the traversal of its inner TypeLoc.

Most of the changes here are about exposing kind() so we can improve the
precision of our tests.

This should fix the issue raised in D65067.

Reviewers: hokein

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D65100

llvm-svn: 366882
diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp
index 77db371d..cbb83a1 100644
--- a/clang-tools-extra/clangd/Selection.cpp
+++ b/clang-tools-extra/clangd/Selection.cpp
@@ -63,10 +63,8 @@
   std::vector<std::pair<unsigned, unsigned>> Ranges; // Always sorted.
 };
 
-// Dump a node for debugging.
-// DynTypedNode::print() doesn't include the kind of node, which is useful.
-void printNode(llvm::raw_ostream &OS, const DynTypedNode &N,
-               const PrintingPolicy &PP) {
+// Show the type of a node for debugging.
+void printNodeKind(llvm::raw_ostream &OS, const DynTypedNode &N) {
   if (const TypeLoc *TL = N.get<TypeLoc>()) {
     // TypeLoc is a hierarchy, but has only a single ASTNodeKind.
     // Synthesize the name from the Type subclass (except for QualifiedTypeLoc).
@@ -77,14 +75,13 @@
   } else {
     OS << N.getNodeKind().asStringRef();
   }
-  OS << " ";
-  N.print(OS, PP);
 }
 
 std::string printNodeToString(const DynTypedNode &N, const PrintingPolicy &PP) {
   std::string S;
   llvm::raw_string_ostream OS(S);
-  printNode(OS, N, PP);
+  printNodeKind(OS, N);
+  OS << " ";
   return std::move(OS.str());
 }
 
@@ -155,6 +152,15 @@
     pop();
     return true;
   }
+  // QualifiedTypeLoc is handled strangely in RecursiveASTVisitor: the derived
+  // TraverseTypeLoc is not called for the inner UnqualTypeLoc.
+  // This means we'd never see 'int' in 'const int'! Work around that here.
+  // (The reason for the behavior is to avoid traversing the nested Type twice,
+  // but we ignore TraverseType anyway).
+  bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX) {
+    return traverseNode<TypeLoc>(
+        &QX, [&] { return TraverseTypeLoc(QX.getUnqualifiedLoc()); });
+  }
   // Uninteresting parts of the AST that don't have locations within them.
   bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; }
   bool TraverseType(QualType) { return true; }
@@ -361,12 +367,21 @@
                                                                     : '.');
   else
     OS.indent(Indent);
-  printNode(OS, N.ASTNode, PrintPolicy);
+  printNodeKind(OS, N.ASTNode);
+  OS << ' ';
+  N.ASTNode.print(OS, PrintPolicy);
   OS << "\n";
   for (const Node *Child : N.Children)
     print(OS, *Child, Indent + 2);
 }
 
+std::string SelectionTree::Node::kind() const {
+  std::string S;
+  llvm::raw_string_ostream OS(S);
+  printNodeKind(OS, ASTNode);
+  return std::move(OS.str());
+}
+
 // Decide which selection emulates a "point" query in between characters.
 static std::pair<unsigned, unsigned> pointBounds(unsigned Offset, FileID FID,
                                                  ASTContext &AST) {