@@ -23,6 +23,44 @@ namespace ranges = std::ranges;
2323
2424} // namespace
2525
26+ std::vector<const char *> CompileCommand::to_argv () const {
27+ std::vector<const char *> argv;
28+ argv.reserve (resolved.flags .size () + 4 );
29+
30+ if (resolved.is_cc1 && source_file) {
31+ // cc1 mode requires TWO file-related arguments (both are needed):
32+ // 1. -main-file-name <basename> — used by clang for diagnostics/debug info
33+ // 2. <source_file> at the end — the actual input file path
34+ // These are NOT duplicates: (1) is just the basename, (2) is the full path.
35+ for (std::size_t i = 0 ; i < resolved.flags .size (); ++i) {
36+ argv.push_back (resolved.flags [i]);
37+ if (resolved.flags [i] == llvm::StringRef (" -cc1" )) {
38+ argv.push_back (" -main-file-name" );
39+ // path::filename returns a suffix of source_file (a pointer into
40+ // the same buffer), so .data() is null-terminated because source_file is.
41+ argv.push_back (path::filename (source_file).data ());
42+ }
43+ }
44+ } else {
45+ argv.insert (argv.end (), resolved.flags .begin (), resolved.flags .end ());
46+ }
47+
48+ if (source_file) {
49+ argv.push_back (source_file);
50+ }
51+ return argv;
52+ }
53+
54+ std::vector<std::string> CompileCommand::to_string_argv () const {
55+ auto argv = to_argv ();
56+ std::vector<std::string> result;
57+ result.reserve (argv.size ());
58+ for (auto * arg: argv) {
59+ result.emplace_back (arg);
60+ }
61+ return result;
62+ }
63+
2664CompilationDatabase::CompilationDatabase () = default ;
2765
2866CompilationDatabase::~CompilationDatabase () = default ;
@@ -329,26 +367,27 @@ std::size_t CompilationDatabase::load(llvm::StringRef path) {
329367 return entries.size ();
330368}
331369
332- llvm::SmallVector<CompilationContext > CompilationDatabase::lookup (llvm::StringRef file,
333- const CommandOptions& options) {
370+ llvm::SmallVector<CompileCommand > CompilationDatabase::lookup (llvm::StringRef file,
371+ const CommandOptions& options) {
334372 auto path_id = paths.intern (file);
335373 auto matched = find_entries (path_id);
336374
337375 auto render_arg = [&](auto & out, llvm::opt::Arg& arg) {
338376 render_arg_to ([&](llvm::StringRef s) { out.push_back (strings.save (s).data ()); }, arg);
339377 };
340378
341- // / Build one CompilationContext from a single CompilationInfo.
342- auto build_context = [&](object_ptr<CompilationInfo> info) -> CompilationContext {
379+ // / Build one CompileCommand from a single CompilationInfo.
380+ auto build_command = [&](object_ptr<CompilationInfo> info) -> CompileCommand {
343381 llvm::StringRef directory = info->directory ;
344- std::vector<const char *> arguments;
382+ std::vector<const char *> flags;
383+ bool is_cc1 = false ;
345384
346385 auto append_arg = [&](llvm::StringRef s) {
347- arguments .emplace_back (strings.save (s).data ());
386+ flags .emplace_back (strings.save (s).data ());
348387 };
349388
350389 auto append_args = [&](llvm::ArrayRef<const char *> args) {
351- arguments .insert (arguments .end (), args.begin (), args.end ());
390+ flags .insert (flags .end (), args.begin (), args.end ());
352391 };
353392
354393 if (options.query_toolchain ) {
@@ -361,23 +400,20 @@ llvm::SmallVector<CompilationContext> CompilationDatabase::lookup(llvm::StringRe
361400 append_args (info->canonical ->arguments );
362401 append_args (info->patch );
363402 } else {
364- arguments.assign (cached.begin (), cached.end ());
365- // TODO: add an assertion that the last arg is the temp source
366- // file (e.g., contains "query-toolchain") to guard against
367- // future changes in clang cc1 argument ordering.
368- arguments.pop_back (); // remove temp source file
403+ flags.assign (cached.begin (), cached.end ());
404+ flags.pop_back (); // remove temp source file
369405
370406 // Replace resource dir if needed.
371407 if (!resource_dir ().empty ()) {
372408 llvm::StringRef old_resource_dir;
373- for (std::size_t i = 0 ; i + 1 < arguments .size (); ++i) {
374- if (arguments [i] == llvm::StringRef (" -resource-dir" )) {
375- old_resource_dir = arguments [i + 1 ];
409+ for (std::size_t i = 0 ; i + 1 < flags .size (); ++i) {
410+ if (flags [i] == llvm::StringRef (" -resource-dir" )) {
411+ old_resource_dir = flags [i + 1 ];
376412 break ;
377413 }
378414 }
379415 if (!old_resource_dir.empty () && old_resource_dir != resource_dir ()) {
380- for (auto & arg: arguments ) {
416+ for (auto & arg: flags ) {
381417 llvm::StringRef s (arg);
382418 if (s.starts_with (old_resource_dir)) {
383419 auto replaced =
@@ -390,39 +426,42 @@ llvm::SmallVector<CompilationContext> CompilationDatabase::lookup(llvm::StringRe
390426
391427 append_args (info->patch );
392428
393- // Fix -main-file-name to match the actual file.
394- bool next_main_file = false ;
395- for (auto & arg: arguments) {
396- if (arg == llvm::StringRef (" -main-file-name" )) {
397- next_main_file = true ;
429+ // Strip -main-file-name and its value from flags (to_argv() will
430+ // re-inject it with the correct basename when is_cc1 is set).
431+ std::vector<const char *> cleaned;
432+ cleaned.reserve (flags.size ());
433+ for (std::size_t i = 0 ; i < flags.size (); ++i) {
434+ if (flags[i] == llvm::StringRef (" -main-file-name" ) && i + 1 < flags.size ()) {
435+ ++i; // skip the value
398436 continue ;
399437 }
400- if (next_main_file) {
401- arg = strings.save (path::filename (file)).data ();
402- next_main_file = false ;
403- }
438+ cleaned.push_back (flags[i]);
404439 }
405- }
440+ flags = std::move (cleaned);
406441
407- // Inject our resource dir if not already present.
408- if (!resource_dir ().empty ()) {
409- bool has_resource_dir = false ;
410- for (auto & arg: arguments) {
411- if (arg == llvm::StringRef (" -resource-dir" )) {
412- has_resource_dir = true ;
413- break ;
414- }
415- }
416- if (!has_resource_dir) {
417- append_arg (" -resource-dir" );
418- append_arg (resource_dir ());
419- }
442+ // Detect cc1 mode (search rather than assuming index).
443+ is_cc1 = ranges::contains (flags, llvm::StringRef (" -cc1" ));
420444 }
421445 } else {
422446 append_args (info->canonical ->arguments );
423447 append_args (info->patch );
424448 }
425449
450+ // Inject our resource dir if not already present.
451+ if (options.inject_resource_dir && !resource_dir ().empty ()) {
452+ bool has_resource_dir = false ;
453+ for (auto & arg: flags) {
454+ if (arg == llvm::StringRef (" -resource-dir" )) {
455+ has_resource_dir = true ;
456+ break ;
457+ }
458+ }
459+ if (!has_resource_dir) {
460+ append_arg (" -resource-dir" );
461+ append_arg (resource_dir ());
462+ }
463+ }
464+
426465 // Apply remove filter.
427466 if (!options.remove .empty ()) {
428467 using Arg = std::unique_ptr<llvm::opt::Arg>;
@@ -440,12 +479,12 @@ llvm::SmallVector<CompilationContext> CompilationDatabase::lookup(llvm::StringRe
440479 };
441480 std::ranges::sort (remove_args, {}, get_id);
442481
443- auto saved_args = std::move (arguments );
444- arguments .clear ();
445- arguments .push_back (saved_args .front ());
482+ auto saved_flags = std::move (flags );
483+ flags .clear ();
484+ flags .push_back (saved_flags .front ());
446485
447486 parser->parse (
448- llvm::ArrayRef (saved_args ).drop_front (),
487+ llvm::ArrayRef (saved_flags ).drop_front (),
449488 [&](Arg arg) {
450489 auto id = arg->getOption ().getID ();
451490 auto range = std::ranges::equal_range (remove_args, id, {}, get_id);
@@ -461,7 +500,7 @@ llvm::SmallVector<CompilationContext> CompilationDatabase::lookup(llvm::StringRe
461500 return ;
462501 }
463502 }
464- render_arg (arguments , *arg);
503+ render_arg (flags , *arg);
465504 },
466505 [](int , int ) {});
467506 }
@@ -470,26 +509,34 @@ llvm::SmallVector<CompilationContext> CompilationDatabase::lookup(llvm::StringRe
470509 append_arg (arg);
471510 }
472511
473- arguments.emplace_back (paths.resolve (path_id).data ());
474- return CompilationContext (directory, std::move (arguments));
512+ return CompileCommand{
513+ ResolvedFlags{directory, std::move (flags), is_cc1},
514+ paths.resolve (path_id).data ()
515+ };
475516 };
476517
477- llvm::SmallVector<CompilationContext > results;
518+ llvm::SmallVector<CompileCommand > results;
478519
479520 if (!matched.empty ()) {
480521 for (auto & entry: matched) {
481- results.push_back (build_context (entry.info ));
522+ results.push_back (build_command (entry.info ));
482523 }
483524 } else {
484525 // No matching entry — synthesize a default command.
485- std::vector<const char *> arguments ;
526+ std::vector<const char *> flags ;
486527 if (file.ends_with (" .cpp" ) || file.ends_with (" .hpp" ) || file.ends_with (" .cc" )) {
487- arguments = {" clang++" , " -std=c++20" };
528+ flags = {" clang++" , " -std=c++20" };
488529 } else {
489- arguments = {" clang" };
530+ flags = {" clang" };
531+ }
532+ if (options.inject_resource_dir && !resource_dir ().empty ()) {
533+ flags.push_back (strings.save (" -resource-dir" ).data ());
534+ flags.push_back (strings.save (resource_dir ()).data ());
490535 }
491- arguments.emplace_back (paths.resolve (path_id).data ());
492- results.push_back (CompilationContext ({}, std::move (arguments)));
536+ results.push_back (CompileCommand{
537+ ResolvedFlags{{}, std::move (flags), false },
538+ paths.resolve (path_id).data ()
539+ });
493540 }
494541
495542 return results;
@@ -513,8 +560,8 @@ SearchConfig CompilationDatabase::lookup_search_config(llvm::StringRef file,
513560 }
514561
515562 auto results = lookup (file, options);
516- auto & ctx = results.front ();
517- auto config = extract_search_config (ctx. arguments , ctx .directory );
563+ auto & cmd = results.front ();
564+ auto config = extract_search_config (cmd. to_argv (), cmd. resolved .directory );
518565
519566 if (cacheable) {
520567 auto key = ConfigCacheKey{matched.front ().info .ptr , options_bits (options)};
0 commit comments