@@ -179,6 +179,73 @@ 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 .trim (" \t " );
194
+ std::size_t I = 0 ;
195
+ for (; I != Name.size (); ++I) {
196
+ switch (Name[I]) {
197
+ case ' (' : // Start of macro parameter list
198
+ case ' ' : // End of macro name
199
+ case ' \t ' :
200
+ goto EndOfMacro;
201
+ case ' _' :
202
+ continue ;
203
+ default :
204
+ if (llvm::isAlnum (Name[I]))
205
+ continue ;
206
+ return std::nullopt;
207
+ }
208
+ }
209
+ EndOfMacro:
210
+ StringRef SimpleName = Name.slice (0 , I);
211
+ if (SimpleName.empty ())
212
+ return std::nullopt;
213
+ return SimpleName;
214
+ }
215
+
216
+ static void canonicalizeDefines (PreprocessorOptions &PPOpts) {
217
+ using MacroOpt = std::pair<StringRef, std::size_t >;
218
+ std::vector<MacroOpt> SimpleNames;
219
+ SimpleNames.reserve (PPOpts.Macros .size ());
220
+ std::size_t Index = 0 ;
221
+ for (const auto &M : PPOpts.Macros ) {
222
+ auto SName = getSimpleMacroName (M.first );
223
+ // Skip optimizing if we can't guarantee we can preserve relative order.
224
+ if (!SName)
225
+ return ;
226
+ SimpleNames.emplace_back (*SName, Index);
227
+ ++Index;
228
+ }
229
+
230
+ llvm::stable_sort (SimpleNames, [](const MacroOpt &A, const MacroOpt &B) {
231
+ return A.first < B.first ;
232
+ });
233
+ // Keep the last instance of each macro name by going in reverse
234
+ auto NewEnd = std::unique (SimpleNames.rbegin (), SimpleNames.rend (), [](const MacroOpt &A, const MacroOpt &B) {
235
+ return A.first == B.first ;
236
+ });
237
+ SimpleNames.erase (SimpleNames.begin (), NewEnd.base ());
238
+
239
+ // Apply permutation.
240
+ decltype (PPOpts.Macros ) NewMacros;
241
+ NewMacros.reserve (SimpleNames.size ());
242
+ for (std::size_t I = 0 , E = SimpleNames.size (); I != E; ++I) {
243
+ std::size_t OriginalIndex = SimpleNames[I].second ;
244
+ NewMacros.push_back (std::move (PPOpts.Macros [OriginalIndex]));
245
+ }
246
+ std::swap (PPOpts.Macros , NewMacros);
247
+ }
248
+
182
249
// / A clang tool that runs the preprocessor in a mode that's optimized for
183
250
// / dependency scanning for the given compiler invocation.
184
251
class DependencyScanningAction : public tooling ::ToolAction {
@@ -203,6 +270,8 @@ class DependencyScanningAction : public tooling::ToolAction {
203
270
CompilerInvocation OriginalInvocation (*Invocation);
204
271
// Restore the value of DisableFree, which may be modified by Tooling.
205
272
OriginalInvocation.getFrontendOpts ().DisableFree = DisableFree;
273
+ if (any (OptimizeArgs & ScanningOptimizations::Macros))
274
+ canonicalizeDefines (OriginalInvocation.getPreprocessorOpts ());
206
275
207
276
if (Scanned) {
208
277
// Scanning runs once for the first -cc1 invocation in a chain of driver
0 commit comments