@@ -197,6 +197,78 @@ static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
197
197
DiagOpts.IgnoreWarnings = true ;
198
198
}
199
199
200
+ // Clang implements -D and -U by splatting text into a predefines buffer. This
201
+ // allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
202
+ // define the same macro, or adding C++ style comments before the macro name.
203
+ //
204
+ // This function checks that the first non-space characters in the macro
205
+ // obviously form an identifier that can be uniqued on without lexing. Failing
206
+ // to do this could lead to changing the final definition of a macro.
207
+ //
208
+ // We could set up a preprocessor and actually lex the name, but that's very
209
+ // heavyweight for a situation that will almost never happen in practice.
210
+ static std::optional<StringRef> getSimpleMacroName (StringRef Macro) {
211
+ StringRef Name = Macro.split (" =" ).first .ltrim (" \t " );
212
+ std::size_t I = 0 ;
213
+
214
+ auto FinishName = [&]() -> std::optional<StringRef> {
215
+ StringRef SimpleName = Name.slice (0 , I);
216
+ if (SimpleName.empty ())
217
+ return std::nullopt;
218
+ return SimpleName;
219
+ };
220
+
221
+ for (; I != Name.size (); ++I) {
222
+ switch (Name[I]) {
223
+ case ' (' : // Start of macro parameter list
224
+ case ' ' : // End of macro name
225
+ case ' \t ' :
226
+ return FinishName ();
227
+ case ' _' :
228
+ continue ;
229
+ default :
230
+ if (llvm::isAlnum (Name[I]))
231
+ continue ;
232
+ return std::nullopt;
233
+ }
234
+ }
235
+ return FinishName ();
236
+ }
237
+
238
+ static void canonicalizeDefines (PreprocessorOptions &PPOpts) {
239
+ using MacroOpt = std::pair<StringRef, std::size_t >;
240
+ std::vector<MacroOpt> SimpleNames;
241
+ SimpleNames.reserve (PPOpts.Macros .size ());
242
+ std::size_t Index = 0 ;
243
+ for (const auto &M : PPOpts.Macros ) {
244
+ auto SName = getSimpleMacroName (M.first );
245
+ // Skip optimizing if we can't guarantee we can preserve relative order.
246
+ if (!SName)
247
+ return ;
248
+ SimpleNames.emplace_back (*SName, Index);
249
+ ++Index;
250
+ }
251
+
252
+ llvm::stable_sort (SimpleNames, [](const MacroOpt &A, const MacroOpt &B) {
253
+ return A.first < B.first ;
254
+ });
255
+ // Keep the last instance of each macro name by going in reverse
256
+ auto NewEnd = std::unique (
257
+ SimpleNames.rbegin (), SimpleNames.rend (),
258
+ [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first ; });
259
+ SimpleNames.erase (SimpleNames.begin (), NewEnd.base ());
260
+
261
+ // Apply permutation.
262
+ decltype (PPOpts.Macros ) NewMacros;
263
+ NewMacros.reserve (SimpleNames.size ());
264
+ for (std::size_t I = 0 , E = SimpleNames.size (); I != E; ++I) {
265
+ std::size_t OriginalIndex = SimpleNames[I].second ;
266
+ // We still emit undefines here as they may be undefining a predefined macro
267
+ NewMacros.push_back (std::move (PPOpts.Macros [OriginalIndex]));
268
+ }
269
+ std::swap (PPOpts.Macros , NewMacros);
270
+ }
271
+
200
272
// / Builds a dependency file after reversing prefix mappings. This allows
201
273
// / emitting a .d file that has real paths where they would otherwise be
202
274
// / canonicalized.
@@ -322,6 +394,8 @@ class DependencyScanningAction : public tooling::ToolAction {
322
394
CompilerInvocation OriginalInvocation (*Invocation);
323
395
// Restore the value of DisableFree, which may be modified by Tooling.
324
396
OriginalInvocation.getFrontendOpts ().DisableFree = DisableFree;
397
+ if (any (OptimizeArgs & ScanningOptimizations::Macros))
398
+ canonicalizeDefines (OriginalInvocation.getPreprocessorOpts ());
325
399
326
400
if (Scanned) {
327
401
// Scanning runs once for the first -cc1 invocation in a chain of driver
0 commit comments