@@ -274,22 +274,37 @@ class SelectionTester {
274
274
for (unsigned I = 0 ; I < Sel.size (); ++I) {
275
275
if (shouldIgnore (Sel[I]) || PPIgnored[I])
276
276
continue ;
277
- SpelledTokens .emplace_back ();
278
- Tok &S = SpelledTokens .back ();
277
+ SelectedSpelled .emplace_back ();
278
+ Tok &S = SelectedSpelled .back ();
279
279
S.Offset = SM.getFileOffset (Sel[I].location ());
280
280
if (S.Offset >= SelBegin && S.Offset + Sel[I].length () <= SelEnd)
281
281
S.Selected = SelectionTree::Complete;
282
282
else
283
283
S.Selected = SelectionTree::Partial;
284
284
}
285
+ MaybeSelectedExpanded = computeMaybeSelectedExpandedTokens (Buf);
285
286
}
286
287
287
288
// Test whether a consecutive range of tokens is selected.
288
289
// The tokens are taken from the expanded token stream.
289
290
SelectionTree::Selection
290
291
test (llvm::ArrayRef<syntax::Token> ExpandedTokens) const {
291
- if (SpelledTokens .empty ())
292
+ if (ExpandedTokens .empty ())
292
293
return NoTokens;
294
+ if (SelectedSpelled.empty ())
295
+ return SelectionTree::Unselected;
296
+ // Cheap (pointer) check whether any of the tokens could touch selection.
297
+ // In most cases, the node's overall source range touches ExpandedTokens,
298
+ // or we would have failed mayHit(). However now we're only considering
299
+ // the *unclaimed* spans of expanded tokens.
300
+ // This is a significant performance improvement when a lot of nodes
301
+ // surround the selection, including when generated by macros.
302
+ if (MaybeSelectedExpanded.empty () ||
303
+ &ExpandedTokens.front () > &MaybeSelectedExpanded.back () ||
304
+ &ExpandedTokens.back () < &MaybeSelectedExpanded.front ()) {
305
+ return SelectionTree::Unselected;
306
+ }
307
+
293
308
SelectionTree::Selection Result = NoTokens;
294
309
while (!ExpandedTokens.empty ()) {
295
310
// Take consecutive tokens from the same context together for efficiency.
@@ -312,14 +327,14 @@ class SelectionTester {
312
327
// If it returns false, test() will return NoTokens or Unselected.
313
328
// If it returns true, test() may return any value.
314
329
bool mayHit (SourceRange R) const {
315
- if (SpelledTokens .empty ())
330
+ if (SelectedSpelled. empty () || MaybeSelectedExpanded .empty ())
316
331
return false ;
317
332
// If the node starts after the selection ends, it is not selected.
318
333
// Tokens a macro location might claim are >= its expansion start.
319
334
// So if the expansion start > last selected token, we can prune it.
320
335
// (This is particularly helpful for GTest's TEST macro).
321
336
if (auto B = offsetInSelFile (getExpansionStart (R.getBegin ())))
322
- if (*B > SpelledTokens .back ().Offset )
337
+ if (*B > SelectedSpelled .back ().Offset )
323
338
return false ;
324
339
// If the node ends before the selection begins, it is not selected.
325
340
SourceLocation EndLoc = R.getEnd ();
@@ -328,12 +343,73 @@ class SelectionTester {
328
343
// In the rare case that the expansion range is a char range, EndLoc is
329
344
// ~one token too far to the right. We may fail to prune, that's OK.
330
345
if (auto E = offsetInSelFile (EndLoc))
331
- if (*E < SpelledTokens .front ().Offset )
346
+ if (*E < SelectedSpelled .front ().Offset )
332
347
return false ;
333
348
return true ;
334
349
}
335
350
336
351
private:
352
+ // Plausible expanded tokens that might be affected by the selection.
353
+ // This is an overestimate, it may contain tokens that are not selected.
354
+ // The point is to allow cheap pruning in test()
355
+ llvm::ArrayRef<syntax::Token>
356
+ computeMaybeSelectedExpandedTokens (const syntax::TokenBuffer &Toks) {
357
+ if (SelectedSpelled.empty ())
358
+ return {};
359
+
360
+ auto LastAffectedToken = [&](SourceLocation Loc) {
361
+ auto Offset = offsetInSelFile (Loc);
362
+ while (Loc.isValid () && !Offset) {
363
+ Loc = Loc.isMacroID () ? SM.getImmediateExpansionRange (Loc).getEnd ()
364
+ : SM.getIncludeLoc (SM.getFileID (Loc));
365
+ Offset = offsetInSelFile (Loc);
366
+ }
367
+ return Offset;
368
+ };
369
+ auto FirstAffectedToken = [&](SourceLocation Loc) {
370
+ auto Offset = offsetInSelFile (Loc);
371
+ while (Loc.isValid () && !Offset) {
372
+ Loc = Loc.isMacroID () ? SM.getImmediateExpansionRange (Loc).getBegin ()
373
+ : SM.getIncludeLoc (SM.getFileID (Loc));
374
+ Offset = offsetInSelFile (Loc);
375
+ }
376
+ return Offset;
377
+ };
378
+
379
+ const syntax::Token *Start = llvm::partition_point (
380
+ Toks.expandedTokens (),
381
+ [&, First = SelectedSpelled.front ().Offset ](const syntax::Token &Tok) {
382
+ if (Tok.kind () == tok::eof)
383
+ return false ;
384
+ // Implausible if upperbound(Tok) < First.
385
+ if (auto Offset = LastAffectedToken (Tok.location ()))
386
+ return *Offset < First;
387
+ // A prefix of the expanded tokens may be from an an implicit
388
+ // inclusion (e.g. preamble patch, or command-line -include).
389
+ return true ;
390
+ });
391
+
392
+ bool EndInvalid = false ;
393
+ const syntax::Token *End = std::partition_point (
394
+ Start, Toks.expandedTokens ().end (),
395
+ [&, Last = SelectedSpelled.back ().Offset ](const syntax::Token &Tok) {
396
+ if (Tok.kind () == tok::eof)
397
+ return false ;
398
+ // Plausible if lowerbound(Tok) <= Last.
399
+ if (auto Offset = FirstAffectedToken (Tok.location ()))
400
+ return *Offset <= Last;
401
+ // Shouldn't happen: once we've seen tokens traceable to the main
402
+ // file, there shouldn't be any more implicit inclusions.
403
+ assert (false && " Expanded token could not be resolved to main file!" );
404
+ EndInvalid = true ;
405
+ return true ; // conservatively assume this token can overlap
406
+ });
407
+ if (EndInvalid)
408
+ End = Toks.expandedTokens ().end ();
409
+
410
+ return llvm::makeArrayRef (Start, End);
411
+ }
412
+
337
413
// Hit-test a consecutive range of tokens from a single file ID.
338
414
SelectionTree::Selection
339
415
testChunk (FileID FID, llvm::ArrayRef<syntax::Token> Batch) const {
@@ -389,19 +465,20 @@ class SelectionTester {
389
465
SelectionTree::Selection testTokenRange (unsigned Begin, unsigned End) const {
390
466
assert (Begin <= End);
391
467
// Outside the selection entirely?
392
- if (End < SpelledTokens .front ().Offset ||
393
- Begin > SpelledTokens .back ().Offset )
468
+ if (End < SelectedSpelled .front ().Offset ||
469
+ Begin > SelectedSpelled .back ().Offset )
394
470
return SelectionTree::Unselected;
395
471
396
472
// Compute range of tokens.
397
473
auto B = llvm::partition_point (
398
- SpelledTokens, [&](const Tok &T) { return T.Offset < Begin; });
399
- auto E = std::partition_point (
400
- B, SpelledTokens.end (), [&](const Tok &T) { return T.Offset <= End; });
474
+ SelectedSpelled, [&](const Tok &T) { return T.Offset < Begin; });
475
+ auto E = std::partition_point (B, SelectedSpelled.end (), [&](const Tok &T) {
476
+ return T.Offset <= End;
477
+ });
401
478
402
479
// Aggregate selectedness of tokens in range.
403
- bool ExtendsOutsideSelection = Begin < SpelledTokens .front ().Offset ||
404
- End > SpelledTokens .back ().Offset ;
480
+ bool ExtendsOutsideSelection = Begin < SelectedSpelled .front ().Offset ||
481
+ End > SelectedSpelled .back ().Offset ;
405
482
SelectionTree::Selection Result =
406
483
ExtendsOutsideSelection ? SelectionTree::Unselected : NoTokens;
407
484
for (auto It = B; It != E; ++It)
@@ -412,13 +489,13 @@ class SelectionTester {
412
489
// Is the token at `Offset` selected?
413
490
SelectionTree::Selection testToken (unsigned Offset) const {
414
491
// Outside the selection entirely?
415
- if (Offset < SpelledTokens .front ().Offset ||
416
- Offset > SpelledTokens .back ().Offset )
492
+ if (Offset < SelectedSpelled .front ().Offset ||
493
+ Offset > SelectedSpelled .back ().Offset )
417
494
return SelectionTree::Unselected;
418
495
// Find the token, if it exists.
419
496
auto It = llvm::partition_point (
420
- SpelledTokens , [&](const Tok &T) { return T.Offset < Offset; });
421
- if (It != SpelledTokens .end () && It->Offset == Offset)
497
+ SelectedSpelled , [&](const Tok &T) { return T.Offset < Offset; });
498
+ if (It != SelectedSpelled .end () && It->Offset == Offset)
422
499
return It->Selected ;
423
500
return NoTokens;
424
501
}
@@ -444,7 +521,8 @@ class SelectionTester {
444
521
unsigned Offset;
445
522
SelectionTree::Selection Selected;
446
523
};
447
- std::vector<Tok> SpelledTokens;
524
+ std::vector<Tok> SelectedSpelled;
525
+ llvm::ArrayRef<syntax::Token> MaybeSelectedExpanded;
448
526
FileID SelFile;
449
527
SourceRange SelFileBounds;
450
528
const SourceManager &SM;
0 commit comments