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_c23;
141 return LangStandard::lang_cxx26;
145struct TransferableCommand {
149 std::optional<types::ID> Type;
155 TransferableCommand(CompileCommand
C)
156 : Cmd(std::move(
C)), Type(guessType(Cmd.Filename)) {
157 std::vector<std::string> OldArgs = std::move(Cmd.CommandLine);
158 Cmd.CommandLine.clear();
161 llvm::opt::InputArgList ArgList;
163 SmallVector<const char *, 16> TmpArgv;
164 for (
const std::string &S : OldArgs)
165 TmpArgv.push_back(S.c_str());
166 ClangCLMode = !TmpArgv.empty() &&
168 TmpArgv.front(), llvm::ArrayRef(TmpArgv).slice(1)));
169 ArgList = {TmpArgv.begin(), TmpArgv.end()};
177 if (!OldArgs.empty())
178 Cmd.CommandLine.emplace_back(OldArgs.front());
179 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
180 using namespace options;
182 const unsigned OldPos = Pos;
183 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
185 llvm::opt::Visibility(ClangCLMode ? CLOption : ClangOption)));
190 const llvm::opt::Option &Opt = Arg->getOption();
193 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
194 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||
195 Opt.matches(OPT__SLASH_Fe) ||
196 Opt.matches(OPT__SLASH_Fi) ||
197 Opt.matches(OPT__SLASH_Fo))))
201 if (Opt.matches(OPT__DASH_DASH))
205 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
211 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
217 Cmd.CommandLine.insert(Cmd.CommandLine.end(),
218 OldArgs.data() + OldPos, OldArgs.data() + Pos);
224 Type = foldType(*Type);
226 if (Type == types::TY_INVALID)
232 CompileCommand transferTo(StringRef Filename) && {
233 CompileCommand
Result = std::move(Cmd);
235 Result.Filename = std::string(Filename);
237 auto TargetType = guessType(Filename, &TypeCertain);
239 if ((!TargetType || !TypeCertain) && Type) {
243 (!TargetType || types::onlyPrecompileType(TargetType))
244 ? types::lookupHeaderTypeForSourceType(*Type)
247 const StringRef Flag = toCLFlag(TargetType);
249 Result.CommandLine.push_back(std::string(Flag));
251 Result.CommandLine.push_back(
"-x");
252 Result.CommandLine.push_back(types::getTypeName(TargetType));
259 const char *Spelling =
266 if (Std == LangStandard::lang_cxx23)
267 Spelling =
"c++23preview";
268 else if (Std == latestLangStandardC())
269 Spelling =
"clatest";
270 else if (Std == latestLangStandardCXX())
271 Spelling =
"c++latest";
274 Result.CommandLine.emplace_back(
275 (llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") + Spelling).str());
278 Result.CommandLine.push_back(
"--");
279 Result.CommandLine.push_back(std::string(Filename));
285 static types::ID toType(
Language Lang) {
290 return types::TY_CXX;
292 return types::TY_ObjC;
294 return types::TY_ObjCXX;
296 return types::TY_INVALID;
301 static StringRef toCLFlag(types::ID Type) {
304 case types::TY_CHeader:
307 case types::TY_CXXHeader:
315 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
316 const llvm::opt::Option &Opt = Arg.getOption();
317 using namespace options;
319 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
321 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
322 return types::TY_CXX;
324 if (Opt.matches(options::OPT_x))
325 return types::lookupTypeForTypeSpecifier(Arg.getValue());
331 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
332 using namespace options;
333 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
337 if (StringRef(Arg.getValue()) ==
"c++23preview")
338 return LangStandard::lang_cxx23;
339 if (StringRef(Arg.getValue()) ==
"clatest")
340 return latestLangStandardC();
341 if (StringRef(Arg.getValue()) ==
"c++latest")
342 return latestLangStandardCXX();
363 FileIndex(std::vector<std::string> Files)
364 : OriginalPaths(std::move(Files)), Strings(Arena) {
366 llvm::sort(OriginalPaths);
367 Paths.reserve(OriginalPaths.size());
368 Types.reserve(OriginalPaths.size());
369 Stems.reserve(OriginalPaths.size());
370 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
371 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
373 Paths.emplace_back(Path, I);
374 Types.push_back(foldType(guessType(OriginalPaths[I])));
375 Stems.emplace_back(sys::path::stem(Path), I);
376 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
377 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
378 if (Dir->size() > ShortDirectorySegment)
379 Components.emplace_back(*Dir, I);
383 llvm::sort(Components);
386 bool empty()
const {
return Paths.empty(); }
391 StringRef chooseProxy(StringRef OriginalFilename,
392 types::ID PreferLanguage)
const {
393 assert(!empty() &&
"need at least one candidate!");
394 std::string Filename = OriginalFilename.lower();
395 auto Candidates = scoreCandidates(Filename);
396 std::pair<size_t, int> Best =
397 pickWinner(Candidates, Filename, PreferLanguage);
401 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
402 <<
" as proxy for " << OriginalFilename <<
" preferring "
403 << (PreferLanguage == types::TY_INVALID
405 : types::getTypeName(PreferLanguage))
406 <<
" score=" << Best.second <<
"\n");
407 return OriginalPaths[Best.first];
411 using SubstringAndIndex = std::pair<StringRef, size_t>;
415 constexpr static int DirectorySegmentsIndexed = 4;
416 constexpr static int DirectorySegmentsQueried = 2;
417 constexpr static int ShortDirectorySegment = 1;
421 DenseMap<size_t, int> scoreCandidates(StringRef Filename)
const {
425 StringRef Stem = sys::path::stem(Filename);
426 llvm::SmallVector<StringRef, DirectorySegmentsQueried> Dirs;
427 llvm::StringRef Prefix;
428 auto Dir = ++sys::path::rbegin(Filename),
429 DirEnd = sys::path::rend(Filename);
430 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
431 if (Dir->size() > ShortDirectorySegment)
432 Dirs.push_back(*Dir);
433 Prefix = Filename.substr(0, Dir - DirEnd);
437 DenseMap<size_t, int> Candidates;
438 auto Award = [&](
int Points, ArrayRef<SubstringAndIndex>
Range) {
439 for (
const auto &Entry : Range)
440 Candidates[Entry.second] += Points;
444 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
445 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
448 for (StringRef Dir : Dirs)
449 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
451 if (sys::path::root_directory(Prefix) != Prefix)
452 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
458 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
460 types::ID PreferredLanguage)
const {
461 struct ScoredCandidate {
468 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
469 for (
const auto &Candidate : Candidates) {
471 S.Index = Candidate.first;
472 S.Preferred = PreferredLanguage == types::TY_INVALID ||
473 PreferredLanguage == Types[S.Index];
474 S.Points = Candidate.second;
475 if (!S.Preferred && Best.Preferred)
477 if (S.Preferred == Best.Preferred) {
478 if (S.Points < Best.Points)
480 if (S.Points == Best.Points) {
481 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
482 if (S.PrefixLength < Best.PrefixLength)
485 if (S.PrefixLength == Best.PrefixLength)
486 if (S.Index > Best.Index)
492 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
497 if (Best.Index ==
size_t(-1))
498 return {longestMatch(Filename, Paths).second, 0};
499 return {Best.Index, Best.Points};
504 template <
bool Prefix>
505 ArrayRef<SubstringAndIndex>
506 indexLookup(StringRef Key, ArrayRef<SubstringAndIndex> Idx)
const {
508 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
514 SubstringAndIndex longestMatch(StringRef Key,
515 ArrayRef<SubstringAndIndex> Idx)
const {
516 assert(!Idx.empty());
518 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
519 if (It == Idx.begin())
524 size_t Prefix = matchingPrefix(Key, It->first);
525 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
526 return Prefix > PrevPrefix ? *It : *--It;
530 std::vector<std::string> OriginalPaths;
531 BumpPtrAllocator Arena;
535 std::vector<SubstringAndIndex> Paths;
538 std::vector<types::ID> Types;
539 std::vector<SubstringAndIndex> Stems;
540 std::vector<SubstringAndIndex> Components;
548 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
549 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
551 std::vector<CompileCommand>
552 getCompileCommands(StringRef Filename)
const override {
553 auto Known = Inner->getCompileCommands(Filename);
554 if (Index.empty() || !Known.empty())
557 auto Lang = guessType(Filename, &TypeCertain);
559 Lang = types::TY_INVALID;
561 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
562 if (ProxyCommands.empty())
567 std::vector<std::string> getAllFiles()
const override {
568 return Inner->getAllFiles();
571 std::vector<CompileCommand> getAllCompileCommands()
const override {
572 return Inner->getAllCompileCommands();
576 std::unique_ptr<CompilationDatabase> Inner;
582std::unique_ptr<CompilationDatabase>
584 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
588 StringRef Filename) {
589 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.
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.
const llvm::opt::OptTable & getDriverOptTable()
static const LangStandard & getLangStandardForKind(Kind K)
const char * getName() const
getName - Get the name of this standard.
static Kind getLangKind(StringRef Name)