Skip to content

Commit 2ff40ca

Browse files
committed
[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
1 parent aeb21b9 commit 2ff40ca

File tree

3 files changed

+39
-20
lines changed

3 files changed

+39
-20
lines changed

clang-tools-extra/clangd/Selection.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ class RangeSet {
6363
std::vector<std::pair<unsigned, unsigned>> Ranges; // Always sorted.
6464
};
6565

66-
// Dump a node for debugging.
67-
// DynTypedNode::print() doesn't include the kind of node, which is useful.
68-
void printNode(llvm::raw_ostream &OS, const DynTypedNode &N,
69-
const PrintingPolicy &PP) {
66+
// Show the type of a node for debugging.
67+
void printNodeKind(llvm::raw_ostream &OS, const DynTypedNode &N) {
7068
if (const TypeLoc *TL = N.get<TypeLoc>()) {
7169
// TypeLoc is a hierarchy, but has only a single ASTNodeKind.
7270
// Synthesize the name from the Type subclass (except for QualifiedTypeLoc).
@@ -77,14 +75,13 @@ void printNode(llvm::raw_ostream &OS, const DynTypedNode &N,
7775
} else {
7876
OS << N.getNodeKind().asStringRef();
7977
}
80-
OS << " ";
81-
N.print(OS, PP);
8278
}
8379

8480
std::string printNodeToString(const DynTypedNode &N, const PrintingPolicy &PP) {
8581
std::string S;
8682
llvm::raw_string_ostream OS(S);
87-
printNode(OS, N, PP);
83+
printNodeKind(OS, N);
84+
OS << " ";
8885
return std::move(OS.str());
8986
}
9087

@@ -155,6 +152,15 @@ class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> {
155152
pop();
156153
return true;
157154
}
155+
// QualifiedTypeLoc is handled strangely in RecursiveASTVisitor: the derived
156+
// TraverseTypeLoc is not called for the inner UnqualTypeLoc.
157+
// This means we'd never see 'int' in 'const int'! Work around that here.
158+
// (The reason for the behavior is to avoid traversing the nested Type twice,
159+
// but we ignore TraverseType anyway).
160+
bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX) {
161+
return traverseNode<TypeLoc>(
162+
&QX, [&] { return TraverseTypeLoc(QX.getUnqualifiedLoc()); });
163+
}
158164
// Uninteresting parts of the AST that don't have locations within them.
159165
bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; }
160166
bool TraverseType(QualType) { return true; }
@@ -361,12 +367,21 @@ void SelectionTree::print(llvm::raw_ostream &OS, const SelectionTree::Node &N,
361367
: '.');
362368
else
363369
OS.indent(Indent);
364-
printNode(OS, N.ASTNode, PrintPolicy);
370+
printNodeKind(OS, N.ASTNode);
371+
OS << ' ';
372+
N.ASTNode.print(OS, PrintPolicy);
365373
OS << "\n";
366374
for (const Node *Child : N.Children)
367375
print(OS, *Child, Indent + 2);
368376
}
369377

378+
std::string SelectionTree::Node::kind() const {
379+
std::string S;
380+
llvm::raw_string_ostream OS(S);
381+
printNodeKind(OS, ASTNode);
382+
return std::move(OS.str());
383+
}
384+
370385
// Decide which selection emulates a "point" query in between characters.
371386
static std::pair<unsigned, unsigned> pointBounds(unsigned Offset, FileID FID,
372387
ASTContext &AST) {

clang-tools-extra/clangd/Selection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class SelectionTree {
9696
// Walk up the AST to get the DeclContext of this Node,
9797
// which is not the node itself.
9898
const DeclContext& getDeclContext() const;
99+
// Printable node kind, like "CXXRecordDecl" or "AutoTypeLoc".
100+
std::string kind() const;
99101
};
100102
// The most specific common ancestor of all the selected nodes.
101103
// If there is no selection, this is nullptr.

clang-tools-extra/clangd/unittests/SelectionTests.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ Range nodeRange(const SelectionTree::Node *N, ParsedAST &AST) {
4949
}
5050

5151
std::string nodeKind(const SelectionTree::Node *N) {
52-
if (!N)
53-
return "<null>";
54-
return N->ASTNode.getNodeKind().asStringRef().str();
52+
return N ? N->kind() : "<null>";
5553
}
5654

5755
std::vector<const SelectionTree::Node *> allNodes(const SelectionTree &T) {
@@ -102,14 +100,14 @@ TEST(SelectionTest, CommonAncestor) {
102100
struct AAA { struct BBB { static int ccc(); };};
103101
int x = AAA::[[B^B^B]]::ccc();
104102
)cpp",
105-
"TypeLoc",
103+
"RecordTypeLoc",
106104
},
107105
{
108106
R"cpp(
109107
struct AAA { struct BBB { static int ccc(); };};
110108
int x = AAA::[[B^BB^]]::ccc();
111109
)cpp",
112-
"TypeLoc",
110+
"RecordTypeLoc",
113111
},
114112
{
115113
R"cpp(
@@ -182,19 +180,19 @@ TEST(SelectionTest, CommonAncestor) {
182180
R"cpp(
183181
[[^void]] (*S)(int) = nullptr;
184182
)cpp",
185-
"TypeLoc",
183+
"BuiltinTypeLoc",
186184
},
187185
{
188186
R"cpp(
189187
[[void (*S)^(int)]] = nullptr;
190188
)cpp",
191-
"TypeLoc",
189+
"FunctionProtoTypeLoc",
192190
},
193191
{
194192
R"cpp(
195193
[[void (^*S)(int)]] = nullptr;
196194
)cpp",
197-
"TypeLoc",
195+
"FunctionProtoTypeLoc",
198196
},
199197
{
200198
R"cpp(
@@ -206,7 +204,7 @@ TEST(SelectionTest, CommonAncestor) {
206204
R"cpp(
207205
[[void ^(*S)(int)]] = nullptr;
208206
)cpp",
209-
"TypeLoc",
207+
"FunctionProtoTypeLoc",
210208
},
211209

212210
// Point selections.
@@ -218,8 +216,8 @@ TEST(SelectionTest, CommonAncestor) {
218216
{"int bar; void foo() [[{ foo (); }]]^", "CompoundStmt"},
219217

220218
// Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it.
221-
{"[[^void]] foo();", "TypeLoc"},
222-
{"[[void foo^()]];", "TypeLoc"},
219+
{"[[^void]] foo();", "BuiltinTypeLoc"},
220+
{"[[void foo^()]];", "FunctionProtoTypeLoc"},
223221
{"[[^void foo^()]];", "FunctionDecl"},
224222
{"[[void ^foo()]];", "FunctionDecl"},
225223
// Tricky case: two VarDecls share a specifier.
@@ -229,6 +227,9 @@ TEST(SelectionTest, CommonAncestor) {
229227
{"[[st^ruct {int x;}]] y;", "CXXRecordDecl"},
230228
{"[[struct {int x;} ^y]];", "VarDecl"},
231229
{"struct {[[int ^x]];} y;", "FieldDecl"},
230+
// FIXME: the AST has no location info for qualifiers.
231+
{"const [[a^uto]] x = 42;", "AutoTypeLoc"},
232+
{"[[co^nst auto x = 42]];", "VarDecl"},
232233

233234
{"^", nullptr},
234235
{"void foo() { [[foo^^]] (); }", "DeclRefExpr"},
@@ -239,7 +240,8 @@ TEST(SelectionTest, CommonAncestor) {
239240
{"int x = 42^;", nullptr},
240241

241242
// Node types that have caused problems in the past.
242-
{"template <typename T> void foo() { [[^T]] t; }", "TypeLoc"},
243+
{"template <typename T> void foo() { [[^T]] t; }",
244+
"TemplateTypeParmTypeLoc"},
243245

244246
// No crash
245247
{

0 commit comments

Comments
 (0)