Skip to content

Commit 8fbb6ce

Browse files
committed
Fixed toHalfOpenFileRange assertion fail
Summary: - Added new function that gets Expansion range with both ends in the same file. - Fixes the crash at clangd/clangd#113 Subscribers: ilya-biryukov, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D65754 llvm-svn: 368058
1 parent c55c059 commit 8fbb6ce

File tree

2 files changed

+61
-12
lines changed

2 files changed

+61
-12
lines changed

clang-tools-extra/clangd/SourceCode.cpp

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -296,31 +296,72 @@ static SourceRange unionTokenRange(SourceRange R1, SourceRange R2,
296296
E1 < E2 ? R2.getEnd() : R1.getEnd());
297297
}
298298

299-
// Returns the tokenFileRange for a given Location as a Token Range
299+
// Check if two locations have the same file id.
300+
static bool inSameFile(SourceLocation Loc1, SourceLocation Loc2,
301+
const SourceManager &SM) {
302+
return SM.getFileID(Loc1) == SM.getFileID(Loc2);
303+
}
304+
305+
// Find an expansion range (not necessarily immediate) the ends of which are in
306+
// the same file id.
307+
static SourceRange
308+
getExpansionTokenRangeInSameFile(SourceLocation Loc, const SourceManager &SM,
309+
const LangOptions &LangOpts) {
310+
SourceRange ExpansionRange =
311+
toTokenRange(SM.getImmediateExpansionRange(Loc), SM, LangOpts);
312+
// Fast path for most common cases.
313+
if (inSameFile(ExpansionRange.getBegin(), ExpansionRange.getEnd(), SM))
314+
return ExpansionRange;
315+
// Record the stack of expansion locations for the beginning, keyed by FileID.
316+
llvm::DenseMap<FileID, SourceLocation> BeginExpansions;
317+
for (SourceLocation Begin = ExpansionRange.getBegin(); Begin.isValid();
318+
Begin = Begin.isFileID()
319+
? SourceLocation()
320+
: SM.getImmediateExpansionRange(Begin).getBegin()) {
321+
BeginExpansions[SM.getFileID(Begin)] = Begin;
322+
}
323+
// Move up the stack of expansion locations for the end until we find the
324+
// location in BeginExpansions with that has the same file id.
325+
for (SourceLocation End = ExpansionRange.getEnd(); End.isValid();
326+
End = End.isFileID() ? SourceLocation()
327+
: toTokenRange(SM.getImmediateExpansionRange(End),
328+
SM, LangOpts)
329+
.getEnd()) {
330+
auto It = BeginExpansions.find(SM.getFileID(End));
331+
if (It != BeginExpansions.end())
332+
return {It->second, End};
333+
}
334+
llvm_unreachable(
335+
"We should able to find a common ancestor in the expansion tree.");
336+
}
337+
// Returns the file range for a given Location as a Token Range
300338
// This is quite similar to getFileLoc in SourceManager as both use
301339
// getImmediateExpansionRange and getImmediateSpellingLoc (for macro IDs).
302340
// However:
303341
// - We want to maintain the full range information as we move from one file to
304342
// the next. getFileLoc only uses the BeginLoc of getImmediateExpansionRange.
305-
// - We want to split '>>' tokens as the lexer parses the '>>' in template
306-
// instantiations as a '>>' instead of a '>'.
343+
// - We want to split '>>' tokens as the lexer parses the '>>' in nested
344+
// template instantiations as a '>>' instead of two '>'s.
307345
// There is also getExpansionRange but it simply calls
308346
// getImmediateExpansionRange on the begin and ends separately which is wrong.
309347
static SourceRange getTokenFileRange(SourceLocation Loc,
310348
const SourceManager &SM,
311349
const LangOptions &LangOpts) {
312350
SourceRange FileRange = Loc;
313351
while (!FileRange.getBegin().isFileID()) {
314-
assert(!FileRange.getEnd().isFileID() &&
315-
"Both Begin and End should be MacroIDs.");
316352
if (SM.isMacroArgExpansion(FileRange.getBegin())) {
317-
FileRange.setBegin(SM.getImmediateSpellingLoc(FileRange.getBegin()));
318-
FileRange.setEnd(SM.getImmediateSpellingLoc(FileRange.getEnd()));
353+
FileRange = unionTokenRange(
354+
SM.getImmediateSpellingLoc(FileRange.getBegin()),
355+
SM.getImmediateSpellingLoc(FileRange.getEnd()), SM, LangOpts);
356+
assert(inSameFile(FileRange.getBegin(), FileRange.getEnd(), SM));
319357
} else {
320-
SourceRange ExpansionRangeForBegin = toTokenRange(
321-
SM.getImmediateExpansionRange(FileRange.getBegin()), SM, LangOpts);
322-
SourceRange ExpansionRangeForEnd = toTokenRange(
323-
SM.getImmediateExpansionRange(FileRange.getEnd()), SM, LangOpts);
358+
SourceRange ExpansionRangeForBegin =
359+
getExpansionTokenRangeInSameFile(FileRange.getBegin(), SM, LangOpts);
360+
SourceRange ExpansionRangeForEnd =
361+
getExpansionTokenRangeInSameFile(FileRange.getEnd(), SM, LangOpts);
362+
assert(inSameFile(ExpansionRangeForBegin.getBegin(),
363+
ExpansionRangeForEnd.getBegin(), SM) &&
364+
"Both Expansion ranges should be in same file.");
324365
FileRange = unionTokenRange(ExpansionRangeForBegin, ExpansionRangeForEnd,
325366
SM, LangOpts);
326367
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,15 +460,22 @@ TEST(SourceCodeTests, HalfOpenFileRange) {
460460
#define FOO(X, Y) int Y = ++X
461461
#define BAR(X) X + 1
462462
#define ECHO(X) X
463+
464+
#define BUZZ BAZZ(ADD)
465+
#define BAZZ(m) m(1)
466+
#define ADD(a) int f = a + 1;
463467
template<typename T>
464468
class P {};
465-
void f() {
469+
470+
int main() {
466471
$a[[P<P<P<P<P<int>>>>> a]];
467472
$b[[int b = 1]];
468473
$c[[FOO(b, c)]];
469474
$d[[FOO(BAR(BAR(b)), d)]];
470475
// FIXME: We might want to select everything inside the outer ECHO.
471476
ECHO(ECHO($e[[int) ECHO(e]]));
477+
// Shouldn't crash.
478+
$f[[BUZZ]];
472479
}
473480
)cpp");
474481

@@ -495,6 +502,7 @@ TEST(SourceCodeTests, HalfOpenFileRange) {
495502
CheckRange("c");
496503
CheckRange("d");
497504
CheckRange("e");
505+
CheckRange("f");
498506
}
499507

500508
} // namespace

0 commit comments

Comments
 (0)