@@ -55,45 +55,140 @@ getTokensCovering(llvm::ArrayRef<syntax::Token> Toks, SourceRange R,
55
55
return {Begin, End};
56
56
}
57
57
58
- // Finds the smallest expansion range that contains expanded tokens First and
59
- // Last, e.g.:
58
+ // Finds the range within FID corresponding to expanded tokens [First, Last].
59
+ // Prev precedes First and Next follows Last, these must *not* be included.
60
+ // If no range satisfies the criteria, returns an invalid range.
61
+ //
60
62
// #define ID(x) x
61
63
// ID(ID(ID(a1) a2))
62
64
// ~~ -> a1
63
65
// ~~ -> a2
64
66
// ~~~~~~~~~ -> a1 a2
65
- SourceRange findCommonRangeForMacroArgs (const syntax::Token &First,
66
- const syntax::Token &Last,
67
- const SourceManager &SM) {
68
- SourceRange Res;
69
- auto FirstLoc = First.location (), LastLoc = Last.location ();
70
- // Keep traversing up the spelling chain as longs as tokens are part of the
71
- // same expansion.
72
- while (!FirstLoc.isFileID () && !LastLoc.isFileID ()) {
73
- auto ExpInfoFirst = SM.getSLocEntry (SM.getFileID (FirstLoc)).getExpansion ();
74
- auto ExpInfoLast = SM.getSLocEntry (SM.getFileID (LastLoc)).getExpansion ();
75
- // Stop if expansions have diverged.
76
- if (ExpInfoFirst.getExpansionLocStart () !=
77
- ExpInfoLast.getExpansionLocStart ())
67
+ SourceRange spelledForExpandedSlow (SourceLocation First, SourceLocation Last,
68
+ SourceLocation Prev, SourceLocation Next,
69
+ FileID TargetFile,
70
+ const SourceManager &SM) {
71
+ // There are two main parts to this algorithm:
72
+ // - identifying which spelled range covers the expanded tokens
73
+ // - validating that this range doesn't cover any extra tokens (First/Last)
74
+ //
75
+ // We do these in order. However as we transform the expanded range into the
76
+ // spelled one, we adjust First/Last so the validation remains simple.
77
+
78
+ assert (SM.getSLocEntry (TargetFile).isFile ());
79
+ // In most cases, to select First and Last we must return their expansion
80
+ // range, i.e. the whole of any macros they are included in.
81
+ //
82
+ // When First and Last are part of the *same macro arg* of a macro written
83
+ // in TargetFile, we that slice of the arg, i.e. their spelling range.
84
+ //
85
+ // Unwrap such macro calls. If the target file has A(B(C)), the
86
+ // SourceLocation stack of a token inside C shows us the expansion of A first,
87
+ // then B, then any macros inside C's body, then C itself.
88
+ // (This is the reverse of the order the PP applies the expansions in).
89
+ while (First.isMacroID () && Last.isMacroID ()) {
90
+ auto DecFirst = SM.getDecomposedLoc (First);
91
+ auto DecLast = SM.getDecomposedLoc (Last);
92
+ auto &ExpFirst = SM.getSLocEntry (DecFirst.first ).getExpansion ();
93
+ auto &ExpLast = SM.getSLocEntry (DecLast.first ).getExpansion ();
94
+
95
+ if (!ExpFirst.isMacroArgExpansion () || !ExpLast.isMacroArgExpansion ())
96
+ break ;
97
+ // Locations are in the same macro arg if they expand to the same place.
98
+ // (They may still have different FileIDs - an arg can have >1 chunks!)
99
+ if (ExpFirst.getExpansionLocStart () != ExpLast.getExpansionLocStart ())
78
100
break ;
79
- // Do not continue into macro bodies.
80
- if (!ExpInfoFirst.isMacroArgExpansion () ||
81
- !ExpInfoLast.isMacroArgExpansion ())
101
+ // Careful, given:
102
+ // #define HIDE ID(ID(a))
103
+ // ID(ID(HIDE))
104
+ // The token `a` is wrapped in 4 arg-expansions, we only want to unwrap 2.
105
+ // We distinguish them by whether the macro expands into the target file.
106
+ // Fortunately, the target file ones will always appear first.
107
+ auto &ExpMacro =
108
+ SM.getSLocEntry (SM.getFileID (ExpFirst.getExpansionLocStart ()))
109
+ .getExpansion ();
110
+ if (ExpMacro.getExpansionLocStart ().isMacroID ())
82
111
break ;
83
- FirstLoc = SM.getImmediateSpellingLoc (FirstLoc);
84
- LastLoc = SM.getImmediateSpellingLoc (LastLoc);
85
- // Update the result afterwards, as we want the tokens that triggered the
86
- // expansion.
87
- Res = {FirstLoc, LastLoc};
112
+ // Replace each endpoint with its spelling inside the macro arg.
113
+ // (This is getImmediateSpellingLoc without repeating lookups).
114
+ First = ExpFirst.getSpellingLoc ().getLocWithOffset (DecFirst.second );
115
+ Last = ExpLast.getSpellingLoc ().getLocWithOffset (DecLast.second );
116
+
117
+ // Now: how do we adjust the previous/next bounds? Three cases:
118
+ // A) If they are also part of the same macro arg, we translate them too.
119
+ // This will ensure that we don't select any macros nested within the
120
+ // macro arg that cover extra tokens. Critical case:
121
+ // #define ID(X) X
122
+ // ID(prev target) // selecting 'target' succeeds
123
+ // #define LARGE ID(prev target)
124
+ // LARGE // selecting 'target' fails.
125
+ // B) They are not in the macro at all, then their expansion range is a
126
+ // sibling to it, and we can safely substitute that.
127
+ // #define PREV prev
128
+ // #define ID(X) X
129
+ // PREV ID(target) // selecting 'target' succeeds.
130
+ // #define LARGE PREV ID(target)
131
+ // LARGE // selecting 'target' fails.
132
+ // C) They are in a different arg of this macro, or the macro body.
133
+ // Now selecting the whole macro arg is fine, but the whole macro is not.
134
+ // Model this by setting using the edge of the macro call as the bound.
135
+ // #define ID2(X, Y) X Y
136
+ // ID2(prev, target) // selecting 'target' succeeds
137
+ // #define LARGE ID2(prev, target)
138
+ // LARGE // selecting 'target' fails
139
+ auto AdjustBound = [&](SourceLocation &Bound) {
140
+ if (Bound.isInvalid () || !Bound.isMacroID ()) // Non-macro must be case B.
141
+ return ;
142
+ auto DecBound = SM.getDecomposedLoc (Bound);
143
+ auto &ExpBound = SM.getSLocEntry (DecBound.first ).getExpansion ();
144
+ if (ExpBound.isMacroArgExpansion () &&
145
+ ExpBound.getExpansionLocStart () == ExpFirst.getExpansionLocStart ()) {
146
+ // Case A: translate to (spelling) loc within the macro arg.
147
+ Bound = ExpBound.getSpellingLoc ().getLocWithOffset (DecBound.second );
148
+ return ;
149
+ }
150
+ while (Bound.isMacroID ()) {
151
+ SourceRange Exp = SM.getImmediateExpansionRange (Bound).getAsRange ();
152
+ if (Exp.getBegin () == ExpMacro.getExpansionLocStart ()) {
153
+ // Case B: bounds become the macro call itself.
154
+ Bound = (&Bound == &Prev) ? Exp.getBegin () : Exp.getEnd ();
155
+ return ;
156
+ }
157
+ // Either case C, or expansion location will later find case B.
158
+ // We choose the upper bound for Prev and the lower one for Next:
159
+ // ID(prev) target ID(next)
160
+ // ^ ^
161
+ // new-prev new-next
162
+ Bound = (&Bound == &Prev) ? Exp.getEnd () : Exp.getBegin ();
163
+ }
164
+ };
165
+ AdjustBound (Prev);
166
+ AdjustBound (Next);
88
167
}
89
- // Normally mapping back to expansion location here only changes FileID, as
90
- // we've already found some tokens expanded from the same macro argument, and
91
- // they should map to a consecutive subset of spelled tokens. Unfortunately
92
- // SourceManager::isBeforeInTranslationUnit discriminates sourcelocations
93
- // based on their FileID in addition to offsets. So even though we are
94
- // referring to same tokens, SourceManager might tell us that one is before
95
- // the other if they've got different FileIDs.
96
- return SM.getExpansionRange (CharSourceRange (Res, true )).getAsRange ();
168
+
169
+ // In all remaining cases we need the full containing macros.
170
+ // If this overlaps Prev or Next, then no range is possible.
171
+ SourceRange Candidate =
172
+ SM.getExpansionRange (SourceRange (First, Last)).getAsRange ();
173
+ auto DecFirst = SM.getDecomposedExpansionLoc (Candidate.getBegin ());
174
+ auto DecLast = SM.getDecomposedLoc (Candidate.getEnd ());
175
+ // Can end up in the wrong file due to bad input or token-pasting shenanigans.
176
+ if (Candidate.isInvalid () || DecFirst.first != TargetFile || DecLast.first != TargetFile)
177
+ return SourceRange ();
178
+ // Check bounds, which may still be inside macros.
179
+ if (Prev.isValid ()) {
180
+ auto Dec = SM.getDecomposedLoc (SM.getExpansionRange (Prev).getBegin ());
181
+ if (Dec.first != DecFirst.first || Dec.second >= DecFirst.second )
182
+ return SourceRange ();
183
+ }
184
+ if (Next.isValid ()) {
185
+ auto Dec = SM.getDecomposedLoc (SM.getExpansionRange (Next).getEnd ());
186
+ if (Dec.first != DecLast.first || Dec.second <= DecLast.second )
187
+ return SourceRange ();
188
+ }
189
+ // Now we know that Candidate is a file range that covers [First, Last]
190
+ // without encroaching on {Prev, Next}. Ship it!
191
+ return Candidate;
97
192
}
98
193
99
194
} // namespace
@@ -363,51 +458,48 @@ TokenBuffer::spelledForExpanded(llvm::ArrayRef<syntax::Token> Expanded) const {
363
458
// of the range, bail out in that case.
364
459
if (Expanded.empty ())
365
460
return llvm::None;
461
+ const syntax::Token *First = &Expanded.front ();
462
+ const syntax::Token *Last = &Expanded.back ();
463
+ auto [FirstSpelled, FirstMapping] = spelledForExpandedToken (First);
464
+ auto [LastSpelled, LastMapping] = spelledForExpandedToken (Last);
366
465
367
- const syntax::Token *BeginSpelled;
368
- const Mapping *BeginMapping;
369
- std::tie (BeginSpelled, BeginMapping) =
370
- spelledForExpandedToken (&Expanded.front ());
371
-
372
- const syntax::Token *LastSpelled;
373
- const Mapping *LastMapping;
374
- std::tie (LastSpelled, LastMapping) =
375
- spelledForExpandedToken (&Expanded.back ());
376
-
377
- FileID FID = SourceMgr->getFileID (BeginSpelled->location ());
466
+ FileID FID = SourceMgr->getFileID (FirstSpelled->location ());
378
467
// FIXME: Handle multi-file changes by trying to map onto a common root.
379
468
if (FID != SourceMgr->getFileID (LastSpelled->location ()))
380
469
return llvm::None;
381
470
382
471
const MarkedFile &File = Files.find (FID)->second ;
383
472
384
- // If both tokens are coming from a macro argument expansion, try and map to
385
- // smallest part of the macro argument. BeginMapping && LastMapping check is
386
- // only for performance, they are a prerequisite for Expanded.front() and
387
- // Expanded.back() being part of a macro arg expansion.
388
- if (BeginMapping && LastMapping &&
389
- SourceMgr->isMacroArgExpansion (Expanded.front ().location ()) &&
390
- SourceMgr->isMacroArgExpansion (Expanded.back ().location ())) {
391
- auto CommonRange = findCommonRangeForMacroArgs (Expanded.front (),
392
- Expanded.back (), *SourceMgr);
393
- // It might be the case that tokens are arguments of different macro calls,
394
- // in that case we should continue with the logic below instead of returning
395
- // an empty range.
396
- if (CommonRange.isValid ())
397
- return getTokensCovering (File.SpelledTokens , CommonRange, *SourceMgr);
473
+ // If the range is within one macro argument, the result may be only part of a
474
+ // Mapping. We must use the general (SourceManager-based) algorithm.
475
+ if (FirstMapping && FirstMapping == LastMapping &&
476
+ SourceMgr->isMacroArgExpansion (First->location ()) &&
477
+ SourceMgr->isMacroArgExpansion (Last->location ())) {
478
+ // We use excluded Prev/Next token for bounds checking.
479
+ SourceLocation Prev = (First == &ExpandedTokens.front ())
480
+ ? SourceLocation ()
481
+ : (First - 1 )->location ();
482
+ SourceLocation Next = (Last == &ExpandedTokens.back ())
483
+ ? SourceLocation ()
484
+ : (Last + 1 )->location ();
485
+ SourceRange Range = spelledForExpandedSlow (
486
+ First->location (), Last->location (), Prev, Next, FID, *SourceMgr);
487
+ if (Range.isInvalid ())
488
+ return llvm::None;
489
+ return getTokensCovering (File.SpelledTokens , Range, *SourceMgr);
398
490
}
399
491
492
+ // Otherwise, use the fast version based on Mappings.
400
493
// Do not allow changes that doesn't cover full expansion.
401
- unsigned BeginExpanded = Expanded.begin () - ExpandedTokens.data ();
402
- unsigned EndExpanded = Expanded.end () - ExpandedTokens.data ();
403
- if (BeginMapping && BeginExpanded != BeginMapping ->BeginExpanded )
494
+ unsigned FirstExpanded = Expanded.begin () - ExpandedTokens.data ();
495
+ unsigned LastExpanded = Expanded.end () - ExpandedTokens.data ();
496
+ if (FirstMapping && FirstExpanded != FirstMapping ->BeginExpanded )
404
497
return llvm::None;
405
- if (LastMapping && LastMapping->EndExpanded != EndExpanded )
498
+ if (LastMapping && LastMapping->EndExpanded != LastExpanded )
406
499
return llvm::None;
407
- // All is good, return the result.
408
500
return llvm::makeArrayRef (
409
- BeginMapping ? File.SpelledTokens .data () + BeginMapping ->BeginSpelled
410
- : BeginSpelled ,
501
+ FirstMapping ? File.SpelledTokens .data () + FirstMapping ->BeginSpelled
502
+ : FirstSpelled ,
411
503
LastMapping ? File.SpelledTokens .data () + LastMapping->EndSpelled
412
504
: LastSpelled + 1 );
413
505
}
0 commit comments