/// A recursive function to trace all possible backtrace locations
/// to match the \p CaretLocFileID.
-static SourceLocation retrieveMacroLocation(SourceLocation Loc,
- FileID MacroFileID,
- FileID CaretFileID,
- bool getBeginLoc,
- const SourceManager *SM) {
- if (MacroFileID == CaretFileID) return Loc;
- if (!Loc.isMacroID()) return SourceLocation();
+static SourceLocation
+retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID,
+ FileID CaretFileID,
+ const SmallVectorImpl<FileID> &CommonArgExpansions,
+ bool IsBegin, const SourceManager *SM) {
+ assert(SM->getFileID(Loc) == MacroFileID);
+ if (MacroFileID == CaretFileID)
+ return Loc;
+ if (!Loc.isMacroID())
+ return SourceLocation();
SourceLocation MacroLocation, MacroArgLocation;
if (SM->isMacroArgExpansion(Loc)) {
- MacroLocation = SM->getImmediateSpellingLoc(Loc);
- MacroArgLocation = getBeginLoc ? SM->getImmediateExpansionRange(Loc).first
- : SM->getImmediateExpansionRange(Loc).second;
+ // Only look at the immediate spelling location of this macro argument if
+ // the other location in the source range is also present in that expansion.
+ if (std::binary_search(CommonArgExpansions.begin(),
+ CommonArgExpansions.end(), MacroFileID))
+ MacroLocation = SM->getImmediateSpellingLoc(Loc);
+ MacroArgLocation = IsBegin ? SM->getImmediateExpansionRange(Loc).first
+ : SM->getImmediateExpansionRange(Loc).second;
} else {
- MacroLocation = getBeginLoc ? SM->getImmediateExpansionRange(Loc).first
- : SM->getImmediateExpansionRange(Loc).second;
+ MacroLocation = IsBegin ? SM->getImmediateExpansionRange(Loc).first
+ : SM->getImmediateExpansionRange(Loc).second;
MacroArgLocation = SM->getImmediateSpellingLoc(Loc);
}
- MacroFileID = SM->getFileID(MacroLocation);
- MacroLocation = retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID,
- getBeginLoc, SM);
- if (MacroLocation.isValid()) return MacroLocation;
+ if (MacroLocation.isValid()) {
+ MacroFileID = SM->getFileID(MacroLocation);
+ MacroLocation =
+ retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID,
+ CommonArgExpansions, IsBegin, SM);
+ if (MacroLocation.isValid())
+ return MacroLocation;
+ }
MacroFileID = SM->getFileID(MacroArgLocation);
return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID,
- getBeginLoc, SM);
+ CommonArgExpansions, IsBegin, SM);
+}
+
+/// Walk up the chain of macro expansions and collect the FileIDs identifying the
+/// expansions.
+static void getMacroArgExpansionFileIDs(SourceLocation Loc,
+ SmallVectorImpl<FileID> &IDs,
+ bool IsBegin, const SourceManager *SM) {
+ while (Loc.isMacroID()) {
+ if (SM->isMacroArgExpansion(Loc)) {
+ IDs.push_back(SM->getFileID(Loc));
+ Loc = SM->getImmediateSpellingLoc(Loc);
+ } else {
+ auto ExpRange = SM->getImmediateExpansionRange(Loc);
+ Loc = IsBegin ? ExpRange.first : ExpRange.second;
+ }
+ }
+}
+
+/// Collect the expansions of the begin and end locations and compute the set
+/// intersection. Produces a sorted vector of FileIDs in CommonArgExpansions.
+static void computeCommonMacroArgExpansionFileIDs(
+ SourceLocation Begin, SourceLocation End, const SourceManager *SM,
+ SmallVectorImpl<FileID> &CommonArgExpansions) {
+ SmallVector<FileID, 4> BeginArgExpansions;
+ SmallVector<FileID, 4> EndArgExpansions;
+ getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM);
+ getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM);
+ std::sort(BeginArgExpansions.begin(), BeginArgExpansions.end());
+ std::sort(EndArgExpansions.begin(), EndArgExpansions.end());
+ std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),
+ EndArgExpansions.begin(), EndArgExpansions.end(),
+ std::back_inserter(CommonArgExpansions));
}
// Helper function to fix up source ranges. It takes in an array of ranges,
}
// Do the backtracking.
+ SmallVector<FileID, 4> CommonArgExpansions;
+ computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions);
Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID,
- true /*getBeginLoc*/, SM);
+ CommonArgExpansions, /*IsBegin=*/true, SM);
End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID,
- false /*getBeginLoc*/, SM);
+ CommonArgExpansions, /*IsBegin=*/false, SM);
if (Begin.isInvalid() || End.isInvalid()) continue;
// Return the spelling location of the beginning and end of the range.
// CHECK-NEXT: {{.*}}:206:56: note: expanded from macro 'sprintf2'
// CHECK-NEXT: __builtin___sprintf_chk (str, 0, __darwin_obsz(str), __VA_ARGS__)
// CHECK-NEXT: {{^ \^~~~~~~~~~~}}
+
+#define SWAP_AND_APPLY(arg, macro) macro arg
+#define APPLY(macro, arg) macro arg
+#define DECLARE_HELPER() __builtin_printf("%d\n", mylong);
+void use_evil_macros(long mylong) {
+ SWAP_AND_APPLY((), DECLARE_HELPER)
+ APPLY(DECLARE_HELPER, ())
+}
+// CHECK: {{.*}}:228:22: warning: format specifies type 'int' but the argument has type 'long'
+// CHECK-NEXT: SWAP_AND_APPLY((), DECLARE_HELPER)
+// CHECK-NEXT: ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
+// CHECK-NEXT: {{.*}}:224:36: note: expanded from macro 'SWAP_AND_APPLY'
+// CHECK-NEXT: #define SWAP_AND_APPLY(arg, macro) macro arg
+// CHECK-NEXT: ^~~~~~~~~
+// CHECK-NEXT: {{.*}}:226:51: note: expanded from macro 'DECLARE_HELPER'
+// CHECK-NEXT: #define DECLARE_HELPER() __builtin_printf("%d\n", mylong);
+// CHECK-NEXT: ~~ ^~~~~~
+// CHECK-NEXT: {{.*}}:229:9: warning: format specifies type 'int' but the argument has type 'long'
+// CHECK-NEXT: APPLY(DECLARE_HELPER, ())
+// CHECK-NEXT: ~~~~~~^~~~~~~~~~~~~~~~~~~
+// CHECK-NEXT: {{.*}}:225:27: note: expanded from macro 'APPLY'
+// CHECK-NEXT: #define APPLY(macro, arg) macro arg
+// CHECK-NEXT: ^~~~~~~~~
+// CHECK-NEXT: {{.*}}:226:51: note: expanded from macro 'DECLARE_HELPER'
+// CHECK-NEXT: #define DECLARE_HELPER() __builtin_printf("%d\n", mylong);
+// CHECK-NEXT: ~~ ^~~~~~