Skip to content

Commit c9c714c

Browse files
committed
Reland [clangd] Rethink how SelectionTree deals with macros and #includes.
This reverts commit 905b002. Avoid tricky (and invalid) comparator for std::set.
1 parent 5595249 commit c9c714c

File tree

7 files changed

+397
-153
lines changed

7 files changed

+397
-153
lines changed

clang-tools-extra/clangd/Selection.cpp

+285-135
Large diffs are not rendered by default.

clang-tools-extra/clangd/Selection.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class SelectionTree {
7676
unsigned Start, unsigned End);
7777

7878
// Describes to what extent an AST node is covered by the selection.
79-
enum Selection {
79+
enum Selection : unsigned char {
8080
// The AST node owns no characters covered by the selection.
8181
// Note that characters owned by children don't count:
8282
// if (x == 0) scream();

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

+63-3
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ TEST(SelectionTest, CommonAncestor) {
134134
)cpp",
135135
"IfStmt",
136136
},
137+
{
138+
R"cpp(
139+
int x(int);
140+
#define M(foo) x(foo)
141+
int a = 42;
142+
int b = M([[^a]]);
143+
)cpp",
144+
"DeclRefExpr",
145+
},
137146
{
138147
R"cpp(
139148
void foo();
@@ -378,6 +387,7 @@ TEST(SelectionTest, Selected) {
378387
$C[[return]];
379388
}]] else [[{^
380389
}]]]]
390+
char z;
381391
}
382392
)cpp",
383393
R"cpp(
@@ -386,10 +396,10 @@ TEST(SelectionTest, Selected) {
386396
void foo(^$C[[unique_ptr<$C[[unique_ptr<$C[[int]]>]]>]]^ a) {}
387397
)cpp",
388398
R"cpp(int a = [[5 >^> 1]];)cpp",
389-
R"cpp([[
399+
R"cpp(
390400
#define ECHO(X) X
391-
ECHO(EC^HO([[$C[[int]]) EC^HO(a]]));
392-
]])cpp",
401+
ECHO(EC^HO($C[[int]]) EC^HO(a));
402+
)cpp",
393403
R"cpp( $C[[^$C[[int]] a^]]; )cpp",
394404
R"cpp( $C[[^$C[[int]] a = $C[[5]]^]]; )cpp",
395405
};
@@ -428,6 +438,56 @@ TEST(SelectionTest, PathologicalPreprocessor) {
428438
EXPECT_EQ("WhileStmt", T.commonAncestor()->Parent->kind());
429439
}
430440

441+
TEST(SelectionTest, IncludedFile) {
442+
const char *Case = R"cpp(
443+
void test() {
444+
#include "Exp^and.inc"
445+
break;
446+
}
447+
)cpp";
448+
Annotations Test(Case);
449+
auto TU = TestTU::withCode(Test.code());
450+
TU.AdditionalFiles["Expand.inc"] = "while(1)\n";
451+
auto AST = TU.build();
452+
auto T = makeSelectionTree(Case, AST);
453+
454+
EXPECT_EQ("WhileStmt", T.commonAncestor()->kind());
455+
}
456+
457+
TEST(SelectionTest, MacroArgExpansion) {
458+
// If a macro arg is expanded several times, we consider them all selected.
459+
const char *Case = R"cpp(
460+
int mul(int, int);
461+
#define SQUARE(X) mul(X, X);
462+
int nine = SQUARE(^3);
463+
)cpp";
464+
Annotations Test(Case);
465+
auto AST = TestTU::withCode(Test.code()).build();
466+
auto T = makeSelectionTree(Case, AST);
467+
// Unfortunately, this makes the common ancestor the CallExpr...
468+
// FIXME: hack around this by picking one?
469+
EXPECT_EQ("CallExpr", T.commonAncestor()->kind());
470+
EXPECT_FALSE(T.commonAncestor()->Selected);
471+
EXPECT_EQ(2u, T.commonAncestor()->Children.size());
472+
for (const auto* N : T.commonAncestor()->Children) {
473+
EXPECT_EQ("IntegerLiteral", N->kind());
474+
EXPECT_TRUE(N->Selected);
475+
}
476+
477+
// Verify that the common assert() macro doesn't suffer from this.
478+
// (This is because we don't associate the stringified token with the arg).
479+
Case = R"cpp(
480+
void die(const char*);
481+
#define assert(x) (x ? (void)0 : die(#x)
482+
void foo() { assert(^42); }
483+
)cpp";
484+
Test = Annotations(Case);
485+
AST = TestTU::withCode(Test.code()).build();
486+
T = makeSelectionTree(Case, AST);
487+
488+
EXPECT_EQ("IntegerLiteral", T.commonAncestor()->kind());
489+
}
490+
431491
TEST(SelectionTest, Implicit) {
432492
const char* Test = R"cpp(
433493
struct S { S(const char*); };

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

+12-14
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ TEST_F(ExtractVariableTest, Test) {
269269
EXPECT_UNAVAILABLE(UnavailableCases);
270270

271271
// vector of pairs of input and output strings
272-
const std::vector<std::pair<llvm::StringLiteral, llvm::StringLiteral>>
272+
const std::vector<std::pair<std::string, std::string>>
273273
InputOutputs = {
274274
// extraction from variable declaration/assignment
275275
{R"cpp(void varDecl() {
@@ -321,17 +321,10 @@ TEST_F(ExtractVariableTest, Test) {
321321
if(1)
322322
LOOP(5 + [[3]])
323323
})cpp",
324-
/*FIXME: It should be extracted like this. SelectionTree needs to be
325-
* fixed for macros.
326324
R"cpp(#define LOOP(x) while (1) {a = x;}
327-
void f(int a) {
328-
auto dummy = 3; if(1)
329-
LOOP(5 + dummy)
330-
})cpp"},*/
331-
R"cpp(#define LOOP(x) while (1) {a = x;}
332325
void f(int a) {
333-
auto dummy = LOOP(5 + 3); if(1)
334-
dummy
326+
auto dummy = 3; if(1)
327+
LOOP(5 + dummy)
335328
})cpp"},
336329
{R"cpp(#define LOOP(x) do {x;} while(1);
337330
void f(int a) {
@@ -644,13 +637,18 @@ void f(const int c) {
644637
)cpp";
645638
EXPECT_EQ(apply(TemplateFailInput), "unavailable");
646639

647-
// FIXME: This should be extractable after selectionTree works correctly for
648-
// macros (currently it doesn't select anything for the following case)
649-
std::string MacroFailInput = R"cpp(
640+
std::string MacroInput = R"cpp(
650641
#define F(BODY) void f() { BODY }
651642
F ([[int x = 0;]])
652643
)cpp";
653-
EXPECT_EQ(apply(MacroFailInput), "unavailable");
644+
std::string MacroOutput = R"cpp(
645+
#define F(BODY) void f() { BODY }
646+
void extracted() {
647+
int x = 0;
648+
}
649+
F (extracted();)
650+
)cpp";
651+
EXPECT_EQ(apply(MacroInput), MacroOutput);
654652

655653
// Shouldn't crash.
656654
EXPECT_EQ(apply("void f([[int a]]);"), "unavailable");

clang/include/clang/Tooling/Syntax/Tokens.h

+5
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,18 @@ class TokenBuffer {
175175
/// All tokens produced by the preprocessor after all macro replacements,
176176
/// directives, etc. Source locations found in the clang AST will always
177177
/// point to one of these tokens.
178+
/// Tokens are in TU order (per SourceManager::isBeforeInTranslationUnit()).
178179
/// FIXME: figure out how to handle token splitting, e.g. '>>' can be split
179180
/// into two '>' tokens by the parser. However, TokenBuffer currently
180181
/// keeps it as a single '>>' token.
181182
llvm::ArrayRef<syntax::Token> expandedTokens() const {
182183
return ExpandedTokens;
183184
}
184185

186+
/// Returns the subrange of expandedTokens() corresponding to the closed
187+
/// token range R.
188+
llvm::ArrayRef<syntax::Token> expandedTokens(SourceRange R) const;
189+
185190
/// Find the subrange of spelled tokens that produced the corresponding \p
186191
/// Expanded tokens.
187192
///

clang/lib/Tooling/Syntax/Tokens.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ llvm::StringRef FileRange::text(const SourceManager &SM) const {
119119
return Text.substr(Begin, length());
120120
}
121121

122+
llvm::ArrayRef<syntax::Token> TokenBuffer::expandedTokens(SourceRange R) const {
123+
if (R.isInvalid())
124+
return {};
125+
const Token *Begin =
126+
llvm::partition_point(expandedTokens(), [&](const syntax::Token &T) {
127+
return SourceMgr->isBeforeInTranslationUnit(T.location(), R.getBegin());
128+
});
129+
const Token *End =
130+
llvm::partition_point(expandedTokens(), [&](const syntax::Token &T) {
131+
return !SourceMgr->isBeforeInTranslationUnit(R.getEnd(), T.location());
132+
});
133+
if (Begin > End)
134+
return {};
135+
return {Begin, End};
136+
}
137+
122138
std::pair<const syntax::Token *, const TokenBuffer::Mapping *>
123139
TokenBuffer::spelledForExpandedToken(const syntax::Token *Expanded) const {
124140
assert(Expanded);

clang/unittests/Tooling/Syntax/TokensTest.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "llvm/Support/raw_ostream.h"
4141
#include "llvm/Testing/Support/Annotations.h"
4242
#include "llvm/Testing/Support/SupportHelpers.h"
43+
#include "gmock/gmock.h"
4344
#include <cassert>
4445
#include <cstdlib>
4546
#include <gmock/gmock.h>
@@ -663,6 +664,20 @@ TEST_F(TokenBufferTest, SpelledByExpanded) {
663664
ValueIs(SameRange(findSpelled("not_mapped"))));
664665
}
665666

667+
TEST_F(TokenBufferTest, ExpandedTokensForRange) {
668+
recordTokens(R"cpp(
669+
#define SIGN(X) X##_washere
670+
A SIGN(B) C SIGN(D) E SIGN(F) G
671+
)cpp");
672+
673+
SourceRange R(findExpanded("C").front().location(),
674+
findExpanded("F_washere").front().location());
675+
// Sanity check: expanded and spelled tokens are stored separately.
676+
EXPECT_THAT(Buffer.expandedTokens(R),
677+
SameRange(findExpanded("C D_washere E F_washere")));
678+
EXPECT_THAT(Buffer.expandedTokens(SourceRange()), testing::IsEmpty());
679+
}
680+
666681
TEST_F(TokenBufferTest, ExpansionStartingAt) {
667682
// Object-like macro expansions.
668683
recordTokens(R"cpp(

0 commit comments

Comments
 (0)