Files
clice/src/command/argument_parser.h
2026-03-28 17:40:29 +08:00

139 lines
4.9 KiB
C++

#pragma once
#include <cassert>
#include <cstdint>
#include <memory>
#include <optional>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/Allocator.h"
namespace clice {
class ArgumentParser final : public llvm::opt::ArgList {
public:
ArgumentParser(llvm::BumpPtrAllocator* allocator) : allocator(allocator) {}
~ArgumentParser() {
/// We never use the private `Args` field, so make sure it's empty.
if(getArgs().size() != 0) {
std::abort();
}
}
const char* getArgString(unsigned index) const override {
return arguments[index];
}
unsigned getNumInputArgStrings() const override {
return arguments.size();
}
const char* MakeArgStringRef(llvm::StringRef s) const override {
auto p = allocator->Allocate<char>(s.size() + 1);
std::ranges::copy(s, p);
p[s.size()] = '\0';
return p;
}
/// Set visibility mask for option parsing. The default (~0u) accepts all
/// options. Pass a narrower mask to exclude option groups — e.g. exclude
/// MSVC cl.exe-style /U, /D, /I options that would otherwise misparse
/// Unix absolute paths like /Users/... on macOS.
void set_visibility(unsigned mask) {
visibility_mask = mask;
}
/// Parse a single argument at the given index. Defined out-of-line in
/// argument_parser.cpp to isolate the heavy clang driver option table include.
std::unique_ptr<llvm::opt::Arg> parse_one(unsigned& index);
void parse(llvm::ArrayRef<const char*> arguments, const auto& on_parse, const auto& on_error) {
this->arguments = arguments;
unsigned it = 0;
while(it != arguments.size()) {
llvm::StringRef s = arguments[it];
if(s.empty()) [[unlikely]] {
it += 1;
continue;
}
auto prev = it;
auto arg = parse_one(it);
assert(it > prev && "parser failed to consume argument");
if(!arg) [[unlikely]] {
assert(it >= arguments.size() && "unexpected parser error!");
assert(it - prev - 1 && "no missing arguments!");
on_error(prev, it - prev - 1);
break;
}
on_parse(std::move(arg));
}
}
private:
llvm::BumpPtrAllocator* allocator;
unsigned visibility_mask = ~0u;
llvm::ArrayRef<const char*> arguments;
};
/// Check if an option is a codegen-only flag that doesn't affect frontend
/// semantics (parsing, diagnostics, code completion). These are pure
/// backend/linker concerns irrelevant to an LSP server.
///
/// Note: options that DO affect semantics are intentionally kept:
/// -fno-exceptions, -fno-rtti, -std=*, -march=*, -fsanitize=*, -O*, -W*
///
/// Defined out-of-line in argument_parser.cpp (needs clang driver option IDs).
bool is_codegen_option(unsigned id, const llvm::opt::Option& opt);
/// Options that are completely irrelevant to an LSP and should be discarded
/// (input/output, PCH building, dependency scan, C++ modules).
bool is_discarded_option(unsigned id);
/// User-content options that go into the per-file patch rather than the
/// shared canonical command: -I, -D, -U, -include, -isystem, -iquote, -idirafter.
bool is_user_content_option(unsigned id);
/// Subset of user-content options that are include-path flags
/// (-I, -isystem, -iquote, -idirafter) — used for path absolutization.
bool is_include_path_option(unsigned id);
/// Check if this is the -Xclang pass-through option.
bool is_xclang_option(unsigned id);
/// Options that affect system path discovery and should be included in the
/// toolchain cache key. Only these flags are passed to the toolchain query.
bool is_toolchain_option(unsigned id);
/// Get the option ID for a specific argument string.
std::optional<std::uint32_t> get_option_id(llvm::StringRef argument);
/// Get the resource directory for clang builtin headers. Computed once
/// from the current executable path using Driver::GetResourcesPath.
llvm::StringRef resource_dir();
/// Format an argument list as a human-readable string: "[arg1 arg2 ...]".
std::string print_argv(llvm::ArrayRef<const char*> args);
/// Return the visibility mask to exclude MSVC cl.exe-style options (/U, /D,
/// /I, etc.) unless the driver is cl.exe. This prevents Unix absolute paths
/// like /Users/... from being misparsed as /U sers/... on macOS/Linux.
/// Defined out-of-line in argument_parser.cpp (needs ClangVisibility enum).
unsigned default_visibility(llvm::StringRef driver);
/// Check if a filename has a C/C++/ObjC/CUDA/etc. extension accepted by clang.
/// Returns false for .rc (Windows resource), .asm, .def, and other non-C-family files.
/// Defined out-of-line in argument_parser.cpp (needs clang::driver::types).
bool is_c_family_file(llvm::StringRef filename);
} // namespace clice