#include "command/search_config.h" #include "command/argument_parser.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "clang/Driver/Options.h" namespace clice { using ID = clang::driver::options::ID; SearchConfig extract_search_config(llvm::ArrayRef arguments, llvm::StringRef directory) { // Replicate clang's InitHeaderSearch::Realize layout: // Quoted (-iquote) → Angled (-I) → System (-isystem, -internal-isystem, etc.) // Then deduplicate across [Angled..end) matching clang's RemoveDuplicates. std::vector quoted; std::vector angled; std::vector system; std::vector after; auto make_absolute = [&](llvm::StringRef path) -> std::string { llvm::SmallString<256> abs_path(path); if(!llvm::sys::path::is_absolute(abs_path)) { llvm::sys::fs::make_absolute(directory, abs_path); } llvm::sys::path::remove_dots(abs_path, true); return abs_path.str().str(); }; // Track -iprefix state for -iwithprefix/-iwithprefixbefore. std::string prefix; llvm::BumpPtrAllocator allocator; ArgumentParser parser{&allocator}; parser.set_visibility(default_visibility(arguments[0])); parser.parse( llvm::ArrayRef(arguments).drop_front(), [&](std::unique_ptr arg) { auto id = arg->getOption().getID(); switch(id) { // Quoted group (clang: frontend::Quoted) case ID::OPT_iquote: quoted.push_back({make_absolute(arg->getValue())}); break; // Angled group (clang: frontend::Angled) case ID::OPT_I: angled.push_back({make_absolute(arg->getValue())}); break; // System group (clang: frontend::System / ExternCSystem) case ID::OPT_isystem: case ID::OPT_internal_isystem: case ID::OPT_internal_externc_isystem: system.push_back({make_absolute(arg->getValue())}); break; // Prefix options: must be processed in argument order. case ID::OPT_iprefix: prefix = arg->getValue(); break; case ID::OPT_iwithprefix: // clang maps to After group. after.push_back({make_absolute(prefix + arg->getValue())}); break; case ID::OPT_iwithprefixbefore: // clang maps to Angled group. angled.push_back({make_absolute(prefix + arg->getValue())}); break; case ID::OPT_idirafter: after.push_back({make_absolute(arg->getValue())}); break; // TODO: -cxx-isystem (clang: frontend::CXXSystem, C++-only system dirs) // TODO: -iwithsysroot (prepends sysroot to path, then adds to System) // TODO: HeaderMap support (-I foo.hmap remaps include names) default: break; } }, [](int, int) {}); // Concatenate: Quoted → Angled → System → After SearchConfig config; config.dirs.reserve(quoted.size() + angled.size() + system.size() + after.size()); config.dirs.insert(config.dirs.end(), std::make_move_iterator(quoted.begin()), std::make_move_iterator(quoted.end())); config.angled_start_idx = static_cast(config.dirs.size()); config.dirs.insert(config.dirs.end(), std::make_move_iterator(angled.begin()), std::make_move_iterator(angled.end())); config.system_start_idx = static_cast(config.dirs.size()); config.dirs.insert(config.dirs.end(), std::make_move_iterator(system.begin()), std::make_move_iterator(system.end())); config.after_start_idx = static_cast(config.dirs.size()); config.dirs.insert(config.dirs.end(), std::make_move_iterator(after.begin()), std::make_move_iterator(after.end())); // Deduplicate across [angled_start_idx..end), matching clang's // RemoveDuplicates(SearchList, NumQuoted). If a path appears in both // Angled and System, keep the first (Angled) occurrence. This is // critical for #include_next correctness. { llvm::StringSet<> seen; // Do NOT seed with Quoted paths. clang's RemoveDuplicates(SearchList, // NumQuoted) starts from NumQuoted, so a path in both Quoted and Angled // is kept in both — this matters for #include <...> and #include_next. unsigned write = config.angled_start_idx; unsigned removed_before_system = 0; unsigned removed_before_after = 0; for(unsigned read = config.angled_start_idx; read < config.dirs.size(); ++read) { if(seen.insert(config.dirs[read].path).second) { if(write != read) { config.dirs[write] = std::move(config.dirs[read]); } ++write; } else { if(read < config.system_start_idx) { ++removed_before_system; } if(read < config.after_start_idx) { ++removed_before_after; } } } config.dirs.resize(write); config.system_start_idx -= removed_before_system; config.after_start_idx -= removed_before_after; } return config; } } // namespace clice