50#include "llvm/ADT/ArrayRef.h"
51#include "llvm/ADT/DenseMap.h"
52#include "llvm/ADT/StringExtras.h"
53#include "llvm/Option/ArgList.h"
54#include "llvm/Option/OptTable.h"
55#include "llvm/Support/Debug.h"
56#include "llvm/Support/Path.h"
57#include "llvm/Support/StringSaver.h"
58#include "llvm/Support/raw_ostream.h"
66namespace types = clang::driver::types;
67namespace path = llvm::sys::path;
70size_t matchingPrefix(StringRef L, StringRef R) {
71 size_t Limit = std::min(L.size(), R.size());
72 for (
size_t I = 0; I < Limit; ++I)
80template <
bool Prefix>
struct Less {
81 bool operator()(StringRef Key, std::pair<StringRef, size_t>
Value)
const {
82 StringRef
V = Prefix ?
Value.first.substr(0, Key.size()) :
Value.first;
85 bool operator()(std::pair<StringRef, size_t>
Value, StringRef Key)
const {
86 StringRef
V = Prefix ?
Value.first.substr(0, Key.size()) :
Value.first;
93types::ID guessType(StringRef Filename,
bool *Certain =
nullptr) {
96 types::lookupTypeForExtension(path::extension(Filename).substr(1));
98 *Certain =
Lang != types::TY_CHeader &&
Lang != types::TY_INVALID;
104static types::ID foldType(types::ID Lang) {
107 case types::TY_CHeader:
110 case types::TY_ObjCHeader:
111 return types::TY_ObjC;
113 case types::TY_CXXHeader:
114 return types::TY_CXX;
115 case types::TY_ObjCXX:
116 case types::TY_ObjCXXHeader:
117 return types::TY_ObjCXX;
119 case types::TY_CUDA_DEVICE:
120 return types::TY_CUDA;
122 return types::TY_INVALID;
132 return LangStandard::lang_cxx26;
136struct TransferableCommand {
140 std::optional<types::ID> Type;
146 TransferableCommand(CompileCommand
C)
147 : Cmd(std::move(
C)), Type(guessType(Cmd.Filename)) {
148 std::vector<std::string> OldArgs = std::move(Cmd.CommandLine);
149 Cmd.CommandLine.clear();
152 llvm::opt::InputArgList ArgList;
154 SmallVector<const char *, 16> TmpArgv;
155 for (
const std::string &S : OldArgs)
156 TmpArgv.push_back(S.c_str());
157 ClangCLMode = !TmpArgv.empty() &&
159 TmpArgv.front(), llvm::ArrayRef(TmpArgv).slice(1)));
160 ArgList = {TmpArgv.begin(), TmpArgv.end()};
168 if (!OldArgs.empty())
169 Cmd.CommandLine.emplace_back(OldArgs.front());
170 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
171 using namespace driver::options;
173 const unsigned OldPos = Pos;
174 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
176 llvm::opt::Visibility(ClangCLMode ? CLOption : ClangOption)));
181 const llvm::opt::Option &Opt = Arg->getOption();
184 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
185 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||
186 Opt.matches(OPT__SLASH_Fe) ||
187 Opt.matches(OPT__SLASH_Fi) ||
188 Opt.matches(OPT__SLASH_Fo))))
192 if (Opt.matches(OPT__DASH_DASH))
196 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
202 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
208 Cmd.CommandLine.insert(Cmd.CommandLine.end(),
209 OldArgs.data() + OldPos, OldArgs.data() + Pos);
215 Type = foldType(*Type);
217 if (Type == types::TY_INVALID)
223 CompileCommand transferTo(StringRef Filename) && {
224 CompileCommand
Result = std::move(Cmd);
226 Result.Filename = std::string(Filename);
228 auto TargetType = guessType(Filename, &TypeCertain);
230 if ((!TargetType || !TypeCertain) && Type) {
234 (!TargetType || types::onlyPrecompileType(TargetType))
235 ? types::lookupHeaderTypeForSourceType(*Type)
238 const StringRef Flag = toCLFlag(TargetType);
240 Result.CommandLine.push_back(std::string(Flag));
242 Result.CommandLine.push_back(
"-x");
243 Result.CommandLine.push_back(types::getTypeName(TargetType));
249 const char *Spelling =
254 if (ClangCLMode && Std == latestLangStandard()) {
255 Spelling =
"c++latest";
257 Result.CommandLine.emplace_back(
258 (llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") + Spelling).str());
260 Result.CommandLine.push_back(
"--");
261 Result.CommandLine.push_back(std::string(Filename));
267 static types::ID toType(
Language Lang) {
272 return types::TY_CXX;
274 return types::TY_ObjC;
276 return types::TY_ObjCXX;
278 return types::TY_INVALID;
283 static StringRef toCLFlag(types::ID Type) {
286 case types::TY_CHeader:
289 case types::TY_CXXHeader:
297 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
298 const llvm::opt::Option &Opt = Arg.getOption();
299 using namespace driver::options;
301 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
303 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
304 return types::TY_CXX;
306 if (Opt.matches(driver::options::OPT_x))
307 return types::lookupTypeForTypeSpecifier(Arg.getValue());
313 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
314 using namespace driver::options;
315 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
318 if (ClangCLMode && StringRef(Arg.getValue()) ==
"c++latest") {
319 return latestLangStandard();
340 FileIndex(std::vector<std::string> Files)
341 : OriginalPaths(std::move(Files)), Strings(Arena) {
343 llvm::sort(OriginalPaths);
344 Paths.reserve(OriginalPaths.size());
345 Types.reserve(OriginalPaths.size());
346 Stems.reserve(OriginalPaths.size());
347 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
348 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
350 Paths.emplace_back(Path, I);
351 Types.push_back(foldType(guessType(OriginalPaths[I])));
352 Stems.emplace_back(sys::path::stem(Path), I);
353 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
354 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
355 if (Dir->size() > ShortDirectorySegment)
356 Components.emplace_back(*Dir, I);
360 llvm::sort(Components);
363 bool empty()
const {
return Paths.empty(); }
368 StringRef chooseProxy(StringRef OriginalFilename,
369 types::ID PreferLanguage)
const {
370 assert(!empty() &&
"need at least one candidate!");
371 std::string Filename = OriginalFilename.lower();
372 auto Candidates = scoreCandidates(Filename);
373 std::pair<size_t, int> Best =
374 pickWinner(Candidates, Filename, PreferLanguage);
378 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
379 <<
" as proxy for " << OriginalFilename <<
" preferring "
380 << (PreferLanguage == types::TY_INVALID
382 : types::getTypeName(PreferLanguage))
383 <<
" score=" << Best.second <<
"\n");
384 return OriginalPaths[Best.first];
388 using SubstringAndIndex = std::pair<StringRef, size_t>;
392 constexpr static int DirectorySegmentsIndexed = 4;
393 constexpr static int DirectorySegmentsQueried = 2;
394 constexpr static int ShortDirectorySegment = 1;
398 DenseMap<size_t, int> scoreCandidates(StringRef Filename)
const {
402 StringRef Stem = sys::path::stem(Filename);
403 llvm::SmallVector<StringRef, DirectorySegmentsQueried> Dirs;
404 llvm::StringRef Prefix;
405 auto Dir = ++sys::path::rbegin(Filename),
406 DirEnd = sys::path::rend(Filename);
407 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
408 if (Dir->size() > ShortDirectorySegment)
409 Dirs.push_back(*Dir);
410 Prefix = Filename.substr(0, Dir - DirEnd);
414 DenseMap<size_t, int> Candidates;
415 auto Award = [&](
int Points, ArrayRef<SubstringAndIndex>
Range) {
416 for (
const auto &Entry : Range)
417 Candidates[Entry.second] += Points;
421 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
422 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
425 for (StringRef Dir : Dirs)
426 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
428 if (sys::path::root_directory(Prefix) != Prefix)
429 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
435 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
437 types::ID PreferredLanguage)
const {
438 struct ScoredCandidate {
445 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
446 for (
const auto &Candidate : Candidates) {
448 S.Index = Candidate.first;
449 S.Preferred = PreferredLanguage == types::TY_INVALID ||
450 PreferredLanguage == Types[S.Index];
451 S.Points = Candidate.second;
452 if (!S.Preferred && Best.Preferred)
454 if (S.Preferred == Best.Preferred) {
455 if (S.Points < Best.Points)
457 if (S.Points == Best.Points) {
458 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
459 if (S.PrefixLength < Best.PrefixLength)
462 if (S.PrefixLength == Best.PrefixLength)
463 if (S.Index > Best.Index)
469 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
474 if (Best.Index ==
size_t(-1))
475 return {longestMatch(Filename, Paths).second, 0};
476 return {Best.Index, Best.Points};
481 template <
bool Prefix>
482 ArrayRef<SubstringAndIndex>
483 indexLookup(StringRef Key, ArrayRef<SubstringAndIndex> Idx)
const {
485 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
491 SubstringAndIndex longestMatch(StringRef Key,
492 ArrayRef<SubstringAndIndex> Idx)
const {
493 assert(!Idx.empty());
495 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
496 if (It == Idx.begin())
501 size_t Prefix = matchingPrefix(Key, It->first);
502 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
503 return Prefix > PrevPrefix ? *It : *--It;
507 std::vector<std::string> OriginalPaths;
508 BumpPtrAllocator Arena;
512 std::vector<SubstringAndIndex> Paths;
515 std::vector<types::ID> Types;
516 std::vector<SubstringAndIndex> Stems;
517 std::vector<SubstringAndIndex> Components;
525 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
526 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
528 std::vector<CompileCommand>
529 getCompileCommands(StringRef Filename)
const override {
530 auto Known = Inner->getCompileCommands(Filename);
531 if (Index.empty() || !Known.empty())
534 auto Lang = guessType(Filename, &TypeCertain);
536 Lang = types::TY_INVALID;
538 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
539 if (ProxyCommands.empty())
544 std::vector<std::string> getAllFiles()
const override {
545 return Inner->getAllFiles();
548 std::vector<CompileCommand> getAllCompileCommands()
const override {
549 return Inner->getAllCompileCommands();
553 std::unique_ptr<CompilationDatabase> Inner;
559std::unique_ptr<CompilationDatabase>
561 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
565 StringRef Filename) {
566 return TransferableCommand(std::move(Cmd)).transferTo(Filename);
__SIZE_TYPE__ size_t
The unsigned integer type of the result of the sizeof operator.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
const llvm::opt::OptTable & getDriverOptTable()
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
The JSON file list parser is used to communicate input to InstallAPI.
Language
The language for the input, used to select and validate the language standard and possible actions.
@ C
Languages that the frontend can parse and compile.
@ Result
The result type of a method or function.
static const LangStandard & getLangStandardForKind(Kind K)
const char * getName() const
getName - Get the name of this standard.
static Kind getLangKind(StringRef Name)