@@ -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,72 @@ 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
+ bool StartInvalid = false ;
361
+ const syntax::Token *Start = llvm::partition_point (
362
+ Toks.expandedTokens (),
363
+ [&, First = SelectedSpelled.front ().Offset ](const syntax::Token &Tok) {
364
+ if (Tok.kind () == tok::eof)
365
+ return false ;
366
+ // Implausible if upperbound(Tok) < First.
367
+ SourceLocation Loc = Tok.location ();
368
+ auto Offset = offsetInSelFile (Loc);
369
+ while (Loc.isValid () && !Offset) {
370
+ Loc = Loc.isMacroID () ? SM.getImmediateExpansionRange (Loc).getEnd ()
371
+ : SM.getIncludeLoc (SM.getFileID (Loc));
372
+ Offset = offsetInSelFile (Loc);
373
+ }
374
+ if (Offset)
375
+ return *Offset < First;
376
+ StartInvalid = true ;
377
+ return false ; // conservatively assume this token can overlap
378
+ });
379
+ if (StartInvalid) {
380
+ assert (false && " Expanded tokens could not be resolved to main file!" );
381
+ Start = Toks.expandedTokens ().begin ();
382
+ }
383
+
384
+ bool EndInvalid = false ;
385
+ const syntax::Token *End = llvm::partition_point (
386
+ Toks.expandedTokens (),
387
+ [&, Last = SelectedSpelled.back ().Offset ](const syntax::Token &Tok) {
388
+ if (Tok.kind () == tok::eof)
389
+ return false ;
390
+ // Plausible if lowerbound(Tok) <= Last.
391
+ SourceLocation Loc = Tok.location ();
392
+ auto Offset = offsetInSelFile (Loc);
393
+ while (Loc.isValid () && !Offset) {
394
+ Loc = Loc.isMacroID ()
395
+ ? SM.getImmediateExpansionRange (Loc).getBegin ()
396
+ : SM.getIncludeLoc (SM.getFileID (Loc));
397
+ Offset = offsetInSelFile (Loc);
398
+ }
399
+ if (Offset)
400
+ return *Offset <= Last;
401
+ EndInvalid = true ;
402
+ return true ; // conservatively assume this token can overlap
403
+ });
404
+ if (EndInvalid) {
405
+ assert (false && " Expanded tokens could not be resolved to main file!" );
406
+ End = Toks.expandedTokens ().end ();
407
+ }
408
+
409
+ return llvm::makeArrayRef (Start, End);
410
+ }
411
+
337
412
// Hit-test a consecutive range of tokens from a single file ID.
338
413
SelectionTree::Selection
339
414
testChunk (FileID FID, llvm::ArrayRef<syntax::Token> Batch) const {
@@ -389,19 +464,20 @@ class SelectionTester {
389
464
SelectionTree::Selection testTokenRange (unsigned Begin, unsigned End) const {
390
465
assert (Begin <= End);
391
466
// Outside the selection entirely?
392
- if (End < SpelledTokens .front ().Offset ||
393
- Begin > SpelledTokens .back ().Offset )
467
+ if (End < SelectedSpelled .front ().Offset ||
468
+ Begin > SelectedSpelled .back ().Offset )
394
469
return SelectionTree::Unselected;
395
470
396
471
// Compute range of tokens.
397
472
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; });
473
+ SelectedSpelled, [&](const Tok &T) { return T.Offset < Begin; });
474
+ auto E = std::partition_point (B, SelectedSpelled.end (), [&](const Tok &T) {
475
+ return T.Offset <= End;
476
+ });
401
477
402
478
// Aggregate selectedness of tokens in range.
403
- bool ExtendsOutsideSelection = Begin < SpelledTokens .front ().Offset ||
404
- End > SpelledTokens .back ().Offset ;
479
+ bool ExtendsOutsideSelection = Begin < SelectedSpelled .front ().Offset ||
480
+ End > SelectedSpelled .back ().Offset ;
405
481
SelectionTree::Selection Result =
406
482
ExtendsOutsideSelection ? SelectionTree::Unselected : NoTokens;
407
483
for (auto It = B; It != E; ++It)
@@ -412,13 +488,13 @@ class SelectionTester {
412
488
// Is the token at `Offset` selected?
413
489
SelectionTree::Selection testToken (unsigned Offset) const {
414
490
// Outside the selection entirely?
415
- if (Offset < SpelledTokens .front ().Offset ||
416
- Offset > SpelledTokens .back ().Offset )
491
+ if (Offset < SelectedSpelled .front ().Offset ||
492
+ Offset > SelectedSpelled .back ().Offset )
417
493
return SelectionTree::Unselected;
418
494
// Find the token, if it exists.
419
495
auto It = llvm::partition_point (
420
- SpelledTokens , [&](const Tok &T) { return T.Offset < Offset; });
421
- if (It != SpelledTokens .end () && It->Offset == Offset)
496
+ SelectedSpelled , [&](const Tok &T) { return T.Offset < Offset; });
497
+ if (It != SelectedSpelled .end () && It->Offset == Offset)
422
498
return It->Selected ;
423
499
return NoTokens;
424
500
}
@@ -444,7 +520,8 @@ class SelectionTester {
444
520
unsigned Offset;
445
521
SelectionTree::Selection Selected;
446
522
};
447
- std::vector<Tok> SpelledTokens;
523
+ std::vector<Tok> SelectedSpelled;
524
+ llvm::ArrayRef<syntax::Token> MaybeSelectedExpanded;
448
525
FileID SelFile;
449
526
SourceRange SelFileBounds;
450
527
const SourceManager &SM;
0 commit comments