forked from OSchip/llvm-project
Re-land [clangd] Elide even more checks in SelectionTree.
This reverts commit 1093b9f2e9
.
Fix added for implicit-include case.
This commit is contained in:
parent
192f8d9700
commit
4dedd82cc9
|
@ -274,22 +274,37 @@ public:
|
|||
for (unsigned I = 0; I < Sel.size(); ++I) {
|
||||
if (shouldIgnore(Sel[I]) || PPIgnored[I])
|
||||
continue;
|
||||
SpelledTokens.emplace_back();
|
||||
Tok &S = SpelledTokens.back();
|
||||
SelectedSpelled.emplace_back();
|
||||
Tok &S = SelectedSpelled.back();
|
||||
S.Offset = SM.getFileOffset(Sel[I].location());
|
||||
if (S.Offset >= SelBegin && S.Offset + Sel[I].length() <= SelEnd)
|
||||
S.Selected = SelectionTree::Complete;
|
||||
else
|
||||
S.Selected = SelectionTree::Partial;
|
||||
}
|
||||
MaybeSelectedExpanded = computeMaybeSelectedExpandedTokens(Buf);
|
||||
}
|
||||
|
||||
// Test whether a consecutive range of tokens is selected.
|
||||
// The tokens are taken from the expanded token stream.
|
||||
SelectionTree::Selection
|
||||
test(llvm::ArrayRef<syntax::Token> ExpandedTokens) const {
|
||||
if (SpelledTokens.empty())
|
||||
if (ExpandedTokens.empty())
|
||||
return NoTokens;
|
||||
if (SelectedSpelled.empty())
|
||||
return SelectionTree::Unselected;
|
||||
// Cheap (pointer) check whether any of the tokens could touch selection.
|
||||
// In most cases, the node's overall source range touches ExpandedTokens,
|
||||
// or we would have failed mayHit(). However now we're only considering
|
||||
// the *unclaimed* spans of expanded tokens.
|
||||
// This is a significant performance improvement when a lot of nodes
|
||||
// surround the selection, including when generated by macros.
|
||||
if (MaybeSelectedExpanded.empty() ||
|
||||
&ExpandedTokens.front() > &MaybeSelectedExpanded.back() ||
|
||||
&ExpandedTokens.back() < &MaybeSelectedExpanded.front()) {
|
||||
return SelectionTree::Unselected;
|
||||
}
|
||||
|
||||
SelectionTree::Selection Result = NoTokens;
|
||||
while (!ExpandedTokens.empty()) {
|
||||
// Take consecutive tokens from the same context together for efficiency.
|
||||
|
@ -312,14 +327,14 @@ public:
|
|||
// If it returns false, test() will return NoTokens or Unselected.
|
||||
// If it returns true, test() may return any value.
|
||||
bool mayHit(SourceRange R) const {
|
||||
if (SpelledTokens.empty())
|
||||
if (SelectedSpelled.empty() || MaybeSelectedExpanded.empty())
|
||||
return false;
|
||||
// If the node starts after the selection ends, it is not selected.
|
||||
// Tokens a macro location might claim are >= its expansion start.
|
||||
// So if the expansion start > last selected token, we can prune it.
|
||||
// (This is particularly helpful for GTest's TEST macro).
|
||||
if (auto B = offsetInSelFile(getExpansionStart(R.getBegin())))
|
||||
if (*B > SpelledTokens.back().Offset)
|
||||
if (*B > SelectedSpelled.back().Offset)
|
||||
return false;
|
||||
// If the node ends before the selection begins, it is not selected.
|
||||
SourceLocation EndLoc = R.getEnd();
|
||||
|
@ -328,12 +343,73 @@ public:
|
|||
// 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 (auto E = offsetInSelFile(EndLoc))
|
||||
if (*E < SpelledTokens.front().Offset)
|
||||
if (*E < SelectedSpelled.front().Offset)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Plausible expanded tokens that might be affected by the selection.
|
||||
// This is an overestimate, it may contain tokens that are not selected.
|
||||
// The point is to allow cheap pruning in test()
|
||||
llvm::ArrayRef<syntax::Token>
|
||||
computeMaybeSelectedExpandedTokens(const syntax::TokenBuffer &Toks) {
|
||||
if (SelectedSpelled.empty())
|
||||
return {};
|
||||
|
||||
auto LastAffectedToken = [&](SourceLocation Loc) {
|
||||
auto Offset = offsetInSelFile(Loc);
|
||||
while (Loc.isValid() && !Offset) {
|
||||
Loc = Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).getEnd()
|
||||
: SM.getIncludeLoc(SM.getFileID(Loc));
|
||||
Offset = offsetInSelFile(Loc);
|
||||
}
|
||||
return Offset;
|
||||
};
|
||||
auto FirstAffectedToken = [&](SourceLocation Loc) {
|
||||
auto Offset = offsetInSelFile(Loc);
|
||||
while (Loc.isValid() && !Offset) {
|
||||
Loc = Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).getBegin()
|
||||
: SM.getIncludeLoc(SM.getFileID(Loc));
|
||||
Offset = offsetInSelFile(Loc);
|
||||
}
|
||||
return Offset;
|
||||
};
|
||||
|
||||
const syntax::Token *Start = llvm::partition_point(
|
||||
Toks.expandedTokens(),
|
||||
[&, First = SelectedSpelled.front().Offset](const syntax::Token &Tok) {
|
||||
if (Tok.kind() == tok::eof)
|
||||
return false;
|
||||
// Implausible if upperbound(Tok) < First.
|
||||
if (auto Offset = LastAffectedToken(Tok.location()))
|
||||
return *Offset < First;
|
||||
// A prefix of the expanded tokens may be from an an implicit
|
||||
// inclusion (e.g. preamble patch, or command-line -include).
|
||||
return true;
|
||||
});
|
||||
|
||||
bool EndInvalid = false;
|
||||
const syntax::Token *End = std::partition_point(
|
||||
Start, Toks.expandedTokens().end(),
|
||||
[&, Last = SelectedSpelled.back().Offset](const syntax::Token &Tok) {
|
||||
if (Tok.kind() == tok::eof)
|
||||
return false;
|
||||
// Plausible if lowerbound(Tok) <= Last.
|
||||
if (auto Offset = FirstAffectedToken(Tok.location()))
|
||||
return *Offset <= Last;
|
||||
// Shouldn't happen: once we've seen tokens traceable to the main
|
||||
// file, there shouldn't be any more implicit inclusions.
|
||||
assert(false && "Expanded token could not be resolved to main file!");
|
||||
EndInvalid = true;
|
||||
return true; // conservatively assume this token can overlap
|
||||
});
|
||||
if (EndInvalid)
|
||||
End = Toks.expandedTokens().end();
|
||||
|
||||
return llvm::makeArrayRef(Start, End);
|
||||
}
|
||||
|
||||
// Hit-test a consecutive range of tokens from a single file ID.
|
||||
SelectionTree::Selection
|
||||
testChunk(FileID FID, llvm::ArrayRef<syntax::Token> Batch) const {
|
||||
|
@ -389,19 +465,20 @@ private:
|
|||
SelectionTree::Selection testTokenRange(unsigned Begin, unsigned End) const {
|
||||
assert(Begin <= End);
|
||||
// Outside the selection entirely?
|
||||
if (End < SpelledTokens.front().Offset ||
|
||||
Begin > SpelledTokens.back().Offset)
|
||||
if (End < SelectedSpelled.front().Offset ||
|
||||
Begin > SelectedSpelled.back().Offset)
|
||||
return SelectionTree::Unselected;
|
||||
|
||||
// Compute range of tokens.
|
||||
auto B = llvm::partition_point(
|
||||
SpelledTokens, [&](const Tok &T) { return T.Offset < Begin; });
|
||||
auto E = std::partition_point(
|
||||
B, SpelledTokens.end(), [&](const Tok &T) { return T.Offset <= End; });
|
||||
SelectedSpelled, [&](const Tok &T) { return T.Offset < Begin; });
|
||||
auto E = std::partition_point(B, SelectedSpelled.end(), [&](const Tok &T) {
|
||||
return T.Offset <= End;
|
||||
});
|
||||
|
||||
// Aggregate selectedness of tokens in range.
|
||||
bool ExtendsOutsideSelection = Begin < SpelledTokens.front().Offset ||
|
||||
End > SpelledTokens.back().Offset;
|
||||
bool ExtendsOutsideSelection = Begin < SelectedSpelled.front().Offset ||
|
||||
End > SelectedSpelled.back().Offset;
|
||||
SelectionTree::Selection Result =
|
||||
ExtendsOutsideSelection ? SelectionTree::Unselected : NoTokens;
|
||||
for (auto It = B; It != E; ++It)
|
||||
|
@ -412,13 +489,13 @@ private:
|
|||
// Is the token at `Offset` selected?
|
||||
SelectionTree::Selection testToken(unsigned Offset) const {
|
||||
// Outside the selection entirely?
|
||||
if (Offset < SpelledTokens.front().Offset ||
|
||||
Offset > SpelledTokens.back().Offset)
|
||||
if (Offset < SelectedSpelled.front().Offset ||
|
||||
Offset > SelectedSpelled.back().Offset)
|
||||
return SelectionTree::Unselected;
|
||||
// Find the token, if it exists.
|
||||
auto It = llvm::partition_point(
|
||||
SpelledTokens, [&](const Tok &T) { return T.Offset < Offset; });
|
||||
if (It != SpelledTokens.end() && It->Offset == Offset)
|
||||
SelectedSpelled, [&](const Tok &T) { return T.Offset < Offset; });
|
||||
if (It != SelectedSpelled.end() && It->Offset == Offset)
|
||||
return It->Selected;
|
||||
return NoTokens;
|
||||
}
|
||||
|
@ -444,7 +521,8 @@ private:
|
|||
unsigned Offset;
|
||||
SelectionTree::Selection Selected;
|
||||
};
|
||||
std::vector<Tok> SpelledTokens;
|
||||
std::vector<Tok> SelectedSpelled;
|
||||
llvm::ArrayRef<syntax::Token> MaybeSelectedExpanded;
|
||||
FileID SelFile;
|
||||
SourceRange SelFileBounds;
|
||||
const SourceManager &SM;
|
||||
|
|
|
@ -201,6 +201,13 @@ TEST(SelectionTest, CommonAncestor) {
|
|||
)cpp",
|
||||
nullptr,
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
#define TARGET void foo()
|
||||
[[TAR^GET{ return; }]]
|
||||
)cpp",
|
||||
"FunctionDecl",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct S { S(const char*); };
|
||||
|
|
Loading…
Reference in New Issue