@@ -179,6 +179,78 @@ static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
179
179
DiagOpts.IgnoreWarnings = true ;
180
180
}
181
181
182
+ // Clang implements -D and -U by splatting text into a predefines buffer. This
183
+ // allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
184
+ // define the same macro, or adding C++ style comments before the macro name.
185
+ //
186
+ // This function checks that the first non-space characters in the macro
187
+ // obviously form an identifier that can be uniqued on without lexing. Failing
188
+ // to do this could lead to changing the final definition of a macro.
189
+ //
190
+ // We could set up a preprocessor and actually lex the name, but that's very
191
+ // heavyweight for a situation that will almost never happen in practice.
192
+ static std::optional<StringRef> getSimpleMacroName (StringRef Macro) {
193
+ StringRef Name = Macro.split (" =" ).first .ltrim (" \t " );
194
+ std::size_t I = 0 ;
195
+
196
+ auto FinishName = [&]() -> std::optional<StringRef> {
197
+ StringRef SimpleName = Name.slice (0 , I);
198
+ if (SimpleName.empty ())
199
+ return std::nullopt;
200
+ return SimpleName;
201
+ };
202
+
203
+ for (; I != Name.size (); ++I) {
204
+ switch (Name[I]) {
205
+ case ' (' : // Start of macro parameter list
206
+ case ' ' : // End of macro name
207
+ case ' \t ' :
208
+ return FinishName ();
209
+ case ' _' :
210
+ continue ;
211
+ default :
212
+ if (llvm::isAlnum (Name[I]))
213
+ continue ;
214
+ return std::nullopt;
215
+ }
216
+ }
217
+ return FinishName ();
218
+ }
219
+
220
+ static void canonicalizeDefines (PreprocessorOptions &PPOpts) {
221
+ using MacroOpt = std::pair<StringRef, std::size_t >;
222
+ std::vector<MacroOpt> SimpleNames;
223
+ SimpleNames.reserve (PPOpts.Macros .size ());
224
+ std::size_t Index = 0 ;
225
+ for (const auto &M : PPOpts.Macros ) {
226
+ auto SName = getSimpleMacroName (M.first );
227
+ // Skip optimizing if we can't guarantee we can preserve relative order.
228
+ if (!SName)
229
+ return ;
230
+ SimpleNames.emplace_back (*SName, Index);
231
+ ++Index;
232
+ }
233
+
234
+ llvm::stable_sort (SimpleNames, [](const MacroOpt &A, const MacroOpt &B) {
235
+ return A.first < B.first ;
236
+ });
237
+ // Keep the last instance of each macro name by going in reverse
238
+ auto NewEnd = std::unique (
239
+ SimpleNames.rbegin (), SimpleNames.rend (),
240
+ [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first ; });
241
+ SimpleNames.erase (SimpleNames.begin (), NewEnd.base ());
242
+
243
+ // Apply permutation.
244
+ decltype (PPOpts.Macros ) NewMacros;
245
+ NewMacros.reserve (SimpleNames.size ());
246
+ for (std::size_t I = 0 , E = SimpleNames.size (); I != E; ++I) {
247
+ std::size_t OriginalIndex = SimpleNames[I].second ;
248
+ // We still emit undefines here as they may be undefining a predefined macro
249
+ NewMacros.push_back (std::move (PPOpts.Macros [OriginalIndex]));
250
+ }
251
+ std::swap (PPOpts.Macros , NewMacros);
252
+ }
253
+
182
254
// / A clang tool that runs the preprocessor in a mode that's optimized for
183
255
// / dependency scanning for the given compiler invocation.
184
256
class DependencyScanningAction : public tooling ::ToolAction {
@@ -203,6 +275,8 @@ class DependencyScanningAction : public tooling::ToolAction {
203
275
CompilerInvocation OriginalInvocation (*Invocation);
204
276
// Restore the value of DisableFree, which may be modified by Tooling.
205
277
OriginalInvocation.getFrontendOpts ().DisableFree = DisableFree;
278
+ if (any (OptimizeArgs & ScanningOptimizations::Macros))
279
+ canonicalizeDefines (OriginalInvocation.getPreprocessorOpts ());
206
280
207
281
if (Scanned) {
208
282
// Scanning runs once for the first -cc1 invocation in a chain of driver
0 commit comments