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 @@ -313,12 +313,22 @@ bool mayHit(SourceRange R) const { if (SpelledTokens.empty()) return false; - auto B = SM.getDecomposedLoc(R.getBegin()); - auto E = SM.getDecomposedLoc(R.getEnd()); - if (B.first == SelFile && E.first == SelFile) - if (E.second < SpelledTokens.front().Offset || - B.second > SpelledTokens.back().Offset) - return false; + // If the node starts after the selection ends, it is not selected. + // All tokens a macro location might claim are >= its expansion site. + // So it's safe to use the expansions location for the comparison. + // (This is particularly helpful for GTest's TEST macro). + auto B = SM.getDecomposedExpansionLoc(R.getBegin()); + if (B.first == SelFile && B.second > SpelledTokens.back().Offset) + return false; + // If the node ends before the selection begins, it is not selected. + SourceLocation EndLoc = R.getEnd(); + while (EndLoc.isMacroID()) + EndLoc = SM.getImmediateExpansionRange(EndLoc).getEnd(); + auto E = SM.getDecomposedLoc(EndLoc); + // In the rare case that the expansion range is a char range, EndLoc is + // ~one token too far to the right. We may fail to prune, that's OK. + if (E.first == SelFile && E.second < SpelledTokens.front().Offset) + return false; return true; }