#include "command/argument_parser.h" #include #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "clang/Driver/Driver.h" #include "clang/Driver/Options.h" #include "clang/Driver/Types.h" namespace clice { namespace { namespace opt = llvm::opt; namespace driver = clang::driver; /// Access private members of OptTable via the Thief pattern. bool enable_dash_dash_parsing(const opt::OptTable& table); bool enable_grouped_short_options(const opt::OptTable& table); template struct Thief { friend bool enable_dash_dash_parsing(const opt::OptTable& table) { return table.*MP1; } friend bool enable_grouped_short_options(const opt::OptTable& table) { return table.*MP2; } }; template struct Thief<&opt::OptTable::DashDashParsing, &opt::OptTable::GroupedShortOptions>; auto& option_table = driver::getDriverOptTable(); } // namespace std::unique_ptr ArgumentParser::parse_one(unsigned& index) { assert(!enable_dash_dash_parsing(option_table)); assert(!enable_grouped_short_options(option_table)); return option_table.ParseOneArg(*this, index, opt::Visibility(visibility_mask)); } using ID = clang::driver::options::ID; bool is_discarded_option(unsigned id) { switch(id) { /// Input file, unknown args, and output — we manage these ourselves. case ID::OPT_INPUT: case ID::OPT_UNKNOWN: case ID::OPT__DASH_DASH: case ID::OPT_c: case ID::OPT_o: case ID::OPT_dxc_Fc: case ID::OPT_dxc_Fo: case ID::OPT__SLASH_Fo: case ID::OPT__SLASH_Fd: /// PCH building. case ID::OPT_emit_pch: case ID::OPT_include_pch: case ID::OPT__SLASH_Yu: case ID::OPT__SLASH_Fp: /// Dependency scan. case ID::OPT_E: case ID::OPT_M: case ID::OPT_MM: case ID::OPT_MD: case ID::OPT_MMD: case ID::OPT_MF: case ID::OPT_MT: case ID::OPT_MQ: case ID::OPT_MG: case ID::OPT_MP: case ID::OPT_show_inst: case ID::OPT_show_encoding: case ID::OPT_show_includes: case ID::OPT__SLASH_showFilenames: case ID::OPT__SLASH_showFilenames_: case ID::OPT__SLASH_showIncludes: case ID::OPT__SLASH_showIncludes_user: /// C++ modules — we handle these ourselves. case ID::OPT_fmodule_file: case ID::OPT_fmodule_output: case ID::OPT_fprebuilt_module_path: return true; default: return false; } } bool is_user_content_option(unsigned id) { switch(id) { case ID::OPT_I: case ID::OPT_isystem: case ID::OPT_iquote: case ID::OPT_idirafter: case ID::OPT_D: case ID::OPT_U: case ID::OPT_include: return true; default: return false; } } bool is_include_path_option(unsigned id) { switch(id) { case ID::OPT_I: case ID::OPT_isystem: case ID::OPT_iquote: case ID::OPT_idirafter: return true; default: return false; } } bool is_xclang_option(unsigned id) { return id == ID::OPT_Xclang; } bool is_toolchain_option(unsigned id) { switch(id) { case ID::OPT_target: case ID::OPT_target_legacy_spelling: case ID::OPT_isysroot: case ID::OPT__sysroot_EQ: case ID::OPT__sysroot: case ID::OPT_stdlib_EQ: case ID::OPT_gcc_toolchain: case ID::OPT_gcc_install_dir_EQ: case ID::OPT_nostdinc: case ID::OPT_nostdincxx: case ID::OPT_std_EQ: case ID::OPT_x: return true; default: return false; } } std::optional get_option_id(llvm::StringRef argument) { llvm::SmallString<64> buffer = argument; if(argument.ends_with("=")) { buffer += "placeholder"; } unsigned index = 1; std::array arguments = {"clang++", buffer.c_str(), "placeholder"}; llvm::opt::InputArgList arg_list(arguments.data(), arguments.data() + arguments.size()); if(auto arg = option_table.ParseOneArg(arg_list, index)) { return arg->getOption().getID(); } else { return {}; } } llvm::StringRef resource_dir() { static std::string dir = [] { // Use address of this lambda to locate our binary via dladdr/proc. static int anchor; auto exe = llvm::sys::fs::getMainExecutable("", &anchor); if(exe.empty()) { return std::string{}; } return clang::driver::Driver::GetResourcesPath(exe); }(); return dir; } bool is_codegen_option(unsigned id, const llvm::opt::Option& opt) { /// Debug info options form a group (-g, -gdwarf-*, -gsplit-dwarf, etc.). if(opt.matches(ID::OPT_DebugInfo_Group)) { return true; } switch(id) { /// Position-independent code — pure codegen, no macro or semantic effect. case ID::OPT_fPIC: case ID::OPT_fno_PIC: case ID::OPT_fpic: case ID::OPT_fno_pic: case ID::OPT_fPIE: case ID::OPT_fno_PIE: case ID::OPT_fpie: case ID::OPT_fno_pie: /// Frame pointer and unwind tables — pure codegen. case ID::OPT_fomit_frame_pointer: case ID::OPT_fno_omit_frame_pointer: case ID::OPT_funwind_tables: case ID::OPT_fno_unwind_tables: case ID::OPT_fasynchronous_unwind_tables: case ID::OPT_fno_asynchronous_unwind_tables: /// Stack protection — pure codegen. case ID::OPT_fstack_protector: case ID::OPT_fstack_protector_strong: case ID::OPT_fstack_protector_all: case ID::OPT_fno_stack_protector: /// Section splitting, LTO, semantic interposition — pure codegen/linker. case ID::OPT_fdata_sections: case ID::OPT_fno_data_sections: case ID::OPT_ffunction_sections: case ID::OPT_fno_function_sections: case ID::OPT_flto: case ID::OPT_flto_EQ: case ID::OPT_fno_lto: case ID::OPT_fsemantic_interposition: case ID::OPT_fno_semantic_interposition: case ID::OPT_fvisibility_inlines_hidden: /// Diagnostics output formatting — doesn't affect analysis. case ID::OPT_fcolor_diagnostics: case ID::OPT_fno_color_diagnostics: /// Floating-point codegen — doesn't define macros (unlike -ffast-math). case ID::OPT_ftrapping_math: case ID::OPT_fno_trapping_math: return true; default: return false; } } std::string print_argv(llvm::ArrayRef args) { std::string buf; llvm::raw_string_ostream os(buf); bool sep = false; for(llvm::StringRef arg: args) { if(sep) os << ' '; sep = true; if(llvm::all_of(arg, llvm::isPrint) && arg.find_first_of(" \t\n\"\\") == llvm::StringRef::npos) { os << arg; continue; } os << '"'; os.write_escaped(arg, /*UseHexEscapes=*/true); os << '"'; } return std::move(os.str()); } unsigned default_visibility(llvm::StringRef driver) { namespace options = clang::driver::options; auto name = llvm::sys::path::filename(driver); name.consume_back(".exe"); auto is_cl = [](llvm::StringRef s) { return s.equals_insensitive("cl") || s.equals_insensitive("clang-cl"); }; /// cl.exe and clang-cl.exe both need MSVC-style /options. /// Also handle versioned names like clang-cl-17, clang-cl-17.0.1. if(is_cl(name) || is_cl(name.rtrim("0123456789.-"))) { return ~0u; } /// Exclude CLOption to prevent /U, /D, /I from matching Unix paths. return ~static_cast(options::CLOption); } bool is_c_family_file(llvm::StringRef filename) { namespace types = clang::driver::types; auto ext = llvm::sys::path::extension(filename); if(ext.empty()) { return false; } /// Drop the leading dot: ".cpp" → "cpp". auto type = types::lookupTypeForExtension(ext.drop_front()); return type != types::TY_INVALID && types::isAcceptedByClang(type); } } // namespace clice