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 case types::TY_CXXModule:
115 case types::TY_PP_CXXModule:
116 return types::TY_CXX;
117 case types::TY_ObjCXX:
118 case types::TY_ObjCXXHeader:
119 return types::TY_ObjCXX;
121 case types::TY_CUDA_DEVICE:
122 return types::TY_CUDA;
124 return types::TY_INVALID;
130static bool typesSameStandardFamily(types::ID T1, types::ID T2) {
131 if (!types::isDerivedFromC(T1) || !types::isDerivedFromC(T2))
133 return types::isCXX(T1) == types::isCXX(T2);
142 return LangStandard::lang_c23;
151 return LangStandard::lang_cxx26;
155struct TransferableCommand {
159 std::optional<types::ID> Type;
165 TransferableCommand(CompileCommand
C)
166 : Cmd(std::move(
C)), Type(guessType(Cmd.Filename)) {
167 std::vector<std::string> OldArgs = std::move(Cmd.CommandLine);
168 Cmd.CommandLine.clear();
171 llvm::opt::InputArgList ArgList;
173 SmallVector<const char *, 16> TmpArgv;
174 for (
const std::string &S : OldArgs)
175 TmpArgv.push_back(S.c_str());
176 ClangCLMode = !TmpArgv.empty() &&
178 TmpArgv.front(), llvm::ArrayRef(TmpArgv).slice(1)));
179 ArgList = {TmpArgv.begin(), TmpArgv.end()};
187 if (!OldArgs.empty())
188 Cmd.CommandLine.emplace_back(OldArgs.front());
189 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
190 using namespace options;
192 const unsigned OldPos = Pos;
193 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
195 llvm::opt::Visibility(ClangCLMode ? CLOption : ClangOption)));
200 const llvm::opt::Option &Opt = Arg->getOption();
203 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
204 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||
205 Opt.matches(OPT__SLASH_Fe) ||
206 Opt.matches(OPT__SLASH_Fi) ||
207 Opt.matches(OPT__SLASH_Fo))))
211 if (Opt.matches(OPT__DASH_DASH))
215 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
221 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
227 Cmd.CommandLine.insert(Cmd.CommandLine.end(),
228 OldArgs.data() + OldPos, OldArgs.data() + Pos);
234 Type = foldType(*Type);
236 if (Type == types::TY_INVALID)
242 CompileCommand transferTo(StringRef Filename) && {
243 CompileCommand
Result = std::move(Cmd);
245 Result.Filename = std::string(Filename);
247 auto TargetType = guessType(Filename, &TypeCertain);
249 if ((!TargetType || !TypeCertain) && Type) {
253 (!TargetType || types::onlyPrecompileType(TargetType))
254 ? types::lookupHeaderTypeForSourceType(*Type)
257 const StringRef Flag = toCLFlag(TargetType);
259 Result.CommandLine.push_back(std::string(Flag));
261 Result.CommandLine.push_back(
"-x");
262 Result.CommandLine.push_back(types::getTypeName(TargetType));
270 typesSameStandardFamily(foldType(TargetType), *Type)) {
271 const char *Spelling =
278 if (Std == LangStandard::lang_cxx23)
279 Spelling =
"c++23preview";
280 else if (Std == latestLangStandardC())
281 Spelling =
"clatest";
282 else if (Std == latestLangStandardCXX())
283 Spelling =
"c++latest";
286 Result.CommandLine.emplace_back(
287 (llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") + Spelling).str());
290 Result.CommandLine.push_back(
"--");
291 Result.CommandLine.push_back(std::string(Filename));
297 static types::ID toType(
Language Lang) {
302 return types::TY_CXX;
304 return types::TY_ObjC;
306 return types::TY_ObjCXX;
308 return types::TY_INVALID;
313 static StringRef toCLFlag(types::ID Type) {
316 case types::TY_CHeader:
319 case types::TY_CXXHeader:
327 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
328 const llvm::opt::Option &Opt = Arg.getOption();
329 using namespace options;
331 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
333 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
334 return types::TY_CXX;
336 if (Opt.matches(options::OPT_x))
337 return types::lookupTypeForTypeSpecifier(Arg.getValue());
343 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
344 using namespace options;
345 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
349 if (StringRef(Arg.getValue()) ==
"c++23preview")
350 return LangStandard::lang_cxx23;
351 if (StringRef(Arg.getValue()) ==
"clatest")
352 return latestLangStandardC();
353 if (StringRef(Arg.getValue()) ==
"c++latest")
354 return latestLangStandardCXX();
375 FileIndex(std::vector<std::string> Files)
376 : OriginalPaths(std::move(Files)), Strings(Arena) {
378 llvm::sort(OriginalPaths);
379 Paths.reserve(OriginalPaths.size());
380 Types.reserve(OriginalPaths.size());
381 Stems.reserve(OriginalPaths.size());
382 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
383 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
385 Paths.emplace_back(Path, I);
386 Types.push_back(foldType(guessType(OriginalPaths[I])));
387 Stems.emplace_back(sys::path::stem(Path), I);
388 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
389 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
390 if (Dir->size() > ShortDirectorySegment)
391 Components.emplace_back(*Dir, I);
395 llvm::sort(Components);
398 bool empty()
const {
return Paths.empty(); }
403 StringRef chooseProxy(StringRef OriginalFilename,
404 types::ID PreferLanguage)
const {
405 assert(!empty() &&
"need at least one candidate!");
406 std::string Filename = OriginalFilename.lower();
407 auto Candidates = scoreCandidates(Filename);
408 std::pair<size_t, int> Best =
409 pickWinner(Candidates, Filename, PreferLanguage);
413 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
414 <<
" as proxy for " << OriginalFilename <<
" preferring "
415 << (PreferLanguage == types::TY_INVALID
417 : types::getTypeName(PreferLanguage))
418 <<
" score=" << Best.second <<
"\n");
419 return OriginalPaths[Best.first];
423 using SubstringAndIndex = std::pair<StringRef, size_t>;
427 constexpr static int DirectorySegmentsIndexed = 4;
428 constexpr static int DirectorySegmentsQueried = 2;
429 constexpr static int ShortDirectorySegment = 1;
433 DenseMap<size_t, int> scoreCandidates(StringRef Filename)
const {
437 StringRef Stem = sys::path::stem(Filename);
438 llvm::SmallVector<StringRef, DirectorySegmentsQueried> Dirs;
439 llvm::StringRef Prefix;
440 auto Dir = ++sys::path::rbegin(Filename),
441 DirEnd = sys::path::rend(Filename);
442 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
443 if (Dir->size() > ShortDirectorySegment)
444 Dirs.push_back(*Dir);
445 Prefix = Filename.substr(0, Dir - DirEnd);
449 DenseMap<size_t, int> Candidates;
450 auto Award = [&](
int Points, ArrayRef<SubstringAndIndex>
Range) {
451 for (
const auto &Entry : Range)
452 Candidates[Entry.second] += Points;
456 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
457 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
460 for (StringRef Dir : Dirs)
461 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
463 if (sys::path::root_directory(Prefix) != Prefix)
464 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
470 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
472 types::ID PreferredLanguage)
const {
473 struct ScoredCandidate {
480 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
481 for (
const auto &Candidate : Candidates) {
483 S.Index = Candidate.first;
484 S.Preferred = PreferredLanguage == types::TY_INVALID ||
485 PreferredLanguage == Types[S.Index];
486 S.Points = Candidate.second;
487 if (!S.Preferred && Best.Preferred)
489 if (S.Preferred == Best.Preferred) {
490 if (S.Points < Best.Points)
492 if (S.Points == Best.Points) {
493 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
494 if (S.PrefixLength < Best.PrefixLength)
497 if (S.PrefixLength == Best.PrefixLength)
498 if (S.Index > Best.Index)
504 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
509 if (Best.Index ==
size_t(-1))
510 return {longestMatch(Filename, Paths).second, 0};
511 return {Best.Index, Best.Points};
516 template <
bool Prefix>
517 ArrayRef<SubstringAndIndex>
518 indexLookup(StringRef Key, ArrayRef<SubstringAndIndex> Idx)
const {
520 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
526 SubstringAndIndex longestMatch(StringRef Key,
527 ArrayRef<SubstringAndIndex> Idx)
const {
528 assert(!Idx.empty());
530 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
531 if (It == Idx.begin())
536 size_t Prefix = matchingPrefix(Key, It->first);
537 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
538 return Prefix > PrevPrefix ? *It : *--It;
542 std::vector<std::string> OriginalPaths;
543 BumpPtrAllocator Arena;
547 std::vector<SubstringAndIndex> Paths;
550 std::vector<types::ID> Types;
551 std::vector<SubstringAndIndex> Stems;
552 std::vector<SubstringAndIndex> Components;
560 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
561 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
563 std::vector<CompileCommand>
564 getCompileCommands(StringRef Filename)
const override {
565 auto Known = Inner->getCompileCommands(Filename);
566 if (Index.empty() || !Known.empty())
569 auto Lang = guessType(Filename, &TypeCertain);
571 Lang = types::TY_INVALID;
573 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
574 if (ProxyCommands.empty())
579 std::vector<std::string> getAllFiles()
const override {
580 return Inner->getAllFiles();
583 std::vector<CompileCommand> getAllCompileCommands()
const override {
584 return Inner->getAllCompileCommands();
588 std::unique_ptr<CompilationDatabase> Inner;
594std::unique_ptr<CompilationDatabase>
596 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
600 StringRef Filename) {
601 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)