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 case types::TY_CXXStdModule:
117 case types::TY_PP_CXXStdModule:
118 return types::TY_CXX;
119 case types::TY_ObjCXX:
120 case types::TY_ObjCXXHeader:
121 return types::TY_ObjCXX;
123 case types::TY_CUDA_DEVICE:
124 return types::TY_CUDA;
126 return types::TY_INVALID;
132static bool typesSameStandardFamily(types::ID T1, types::ID T2) {
133 if (!types::isDerivedFromC(T1) || !types::isDerivedFromC(T2))
135 return types::isCXX(T1) == types::isCXX(T2);
144 return LangStandard::lang_c23;
153 return LangStandard::lang_cxx29;
157struct TransferableCommand {
161 std::optional<types::ID> Type;
167 TransferableCommand(CompileCommand
C)
168 : Cmd(std::move(
C)), Type(guessType(Cmd.Filename)) {
169 std::vector<std::string> OldArgs = std::move(Cmd.CommandLine);
170 Cmd.CommandLine.clear();
173 llvm::opt::InputArgList ArgList;
175 SmallVector<const char *, 16> TmpArgv;
176 for (
const std::string &S : OldArgs)
177 TmpArgv.push_back(S.c_str());
178 ClangCLMode = !TmpArgv.empty() &&
180 TmpArgv.front(), llvm::ArrayRef(TmpArgv).slice(1)));
181 ArgList = {TmpArgv.begin(), TmpArgv.end()};
189 if (!OldArgs.empty())
190 Cmd.CommandLine.emplace_back(OldArgs.front());
191 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
192 using namespace options;
194 const unsigned OldPos = Pos;
195 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
197 llvm::opt::Visibility(ClangCLMode ? CLOption : ClangOption)));
202 const llvm::opt::Option &Opt = Arg->getOption();
205 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
206 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||
207 Opt.matches(OPT__SLASH_Fe) ||
208 Opt.matches(OPT__SLASH_Fi) ||
209 Opt.matches(OPT__SLASH_Fo))))
213 if (Opt.matches(OPT__DASH_DASH))
217 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
223 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
229 Cmd.CommandLine.insert(Cmd.CommandLine.end(),
230 OldArgs.data() + OldPos, OldArgs.data() + Pos);
236 Type = foldType(*Type);
238 if (Type == types::TY_INVALID)
244 CompileCommand transferTo(StringRef Filename) && {
245 CompileCommand
Result = std::move(Cmd);
247 Result.Filename = std::string(Filename);
249 auto TargetType = guessType(Filename, &TypeCertain);
251 if ((!TargetType || !TypeCertain) && Type) {
255 (!TargetType || types::onlyPrecompileType(TargetType))
256 ? types::lookupHeaderTypeForSourceType(*Type)
259 const StringRef Flag = toCLFlag(TargetType);
261 Result.CommandLine.push_back(std::string(Flag));
263 Result.CommandLine.push_back(
"-x");
264 Result.CommandLine.push_back(types::getTypeName(TargetType));
272 typesSameStandardFamily(foldType(TargetType), *Type)) {
273 const char *Spelling =
280 if (Std == LangStandard::lang_cxx23)
281 Spelling =
"c++23preview";
282 else if (Std == LangStandard::lang_cxx26)
283 Spelling =
"c++26preview";
284 else if (Std == latestLangStandardC())
285 Spelling =
"clatest";
286 else if (Std == latestLangStandardCXX())
287 Spelling =
"c++latest";
290 Result.CommandLine.emplace_back(
291 (llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") + Spelling).str());
294 Result.CommandLine.push_back(
"--");
295 Result.CommandLine.push_back(std::string(Filename));
301 static types::ID toType(
Language Lang) {
306 return types::TY_CXX;
308 return types::TY_ObjC;
310 return types::TY_ObjCXX;
312 return types::TY_INVALID;
317 static StringRef toCLFlag(types::ID Type) {
320 case types::TY_CHeader:
323 case types::TY_CXXHeader:
331 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
332 const llvm::opt::Option &Opt = Arg.getOption();
333 using namespace options;
335 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
337 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
338 return types::TY_CXX;
340 if (Opt.matches(options::OPT_x))
341 return types::lookupTypeForTypeSpecifier(Arg.getValue());
347 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
348 using namespace options;
349 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
353 if (StringRef(Arg.getValue()) ==
"c++23preview")
354 return LangStandard::lang_cxx23;
355 if (StringRef(Arg.getValue()) ==
"c++26preview")
356 return LangStandard::lang_cxx26;
357 if (StringRef(Arg.getValue()) ==
"clatest")
358 return latestLangStandardC();
359 if (StringRef(Arg.getValue()) ==
"c++latest")
360 return latestLangStandardCXX();
381 FileIndex(std::vector<std::string> Files)
382 : OriginalPaths(std::move(Files)), Strings(Arena) {
384 llvm::sort(OriginalPaths);
385 Paths.reserve(OriginalPaths.size());
386 Types.reserve(OriginalPaths.size());
387 Stems.reserve(OriginalPaths.size());
388 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
389 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
391 Paths.emplace_back(Path, I);
392 Types.push_back(foldType(guessType(OriginalPaths[I])));
393 Stems.emplace_back(sys::path::stem(Path), I);
394 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
395 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
396 if (Dir->size() > ShortDirectorySegment)
397 Components.emplace_back(*Dir, I);
401 llvm::sort(Components);
404 bool empty()
const {
return Paths.empty(); }
409 StringRef chooseProxy(StringRef OriginalFilename,
410 types::ID PreferLanguage)
const {
411 assert(!empty() &&
"need at least one candidate!");
412 std::string Filename = OriginalFilename.lower();
413 auto Candidates = scoreCandidates(Filename);
414 std::pair<size_t, int> Best =
415 pickWinner(Candidates, Filename, PreferLanguage);
419 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
420 <<
" as proxy for " << OriginalFilename <<
" preferring "
421 << (PreferLanguage == types::TY_INVALID
423 : types::getTypeName(PreferLanguage))
424 <<
" score=" << Best.second <<
"\n");
425 return OriginalPaths[Best.first];
429 using SubstringAndIndex = std::pair<StringRef, size_t>;
433 constexpr static int DirectorySegmentsIndexed = 4;
434 constexpr static int DirectorySegmentsQueried = 2;
435 constexpr static int ShortDirectorySegment = 1;
439 DenseMap<size_t, int> scoreCandidates(StringRef Filename)
const {
443 StringRef Stem = sys::path::stem(Filename);
444 llvm::SmallVector<StringRef, DirectorySegmentsQueried> Dirs;
445 llvm::StringRef Prefix;
446 auto Dir = ++sys::path::rbegin(Filename),
447 DirEnd = sys::path::rend(Filename);
448 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
449 if (Dir->size() > ShortDirectorySegment)
450 Dirs.push_back(*Dir);
451 Prefix = Filename.substr(0, Dir - DirEnd);
455 DenseMap<size_t, int> Candidates;
456 auto Award = [&](
int Points, ArrayRef<SubstringAndIndex>
Range) {
457 for (
const auto &Entry : Range)
458 Candidates[Entry.second] += Points;
462 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
463 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
466 for (StringRef Dir : Dirs)
467 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
469 if (sys::path::root_directory(Prefix) != Prefix)
470 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
476 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
478 types::ID PreferredLanguage)
const {
479 struct ScoredCandidate {
486 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
487 for (
const auto &Candidate : Candidates) {
489 S.Index = Candidate.first;
490 S.Preferred = PreferredLanguage == types::TY_INVALID ||
491 PreferredLanguage == Types[S.Index];
492 S.Points = Candidate.second;
493 if (!S.Preferred && Best.Preferred)
495 if (S.Preferred == Best.Preferred) {
496 if (S.Points < Best.Points)
498 if (S.Points == Best.Points) {
499 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
500 if (S.PrefixLength < Best.PrefixLength)
503 if (S.PrefixLength == Best.PrefixLength)
504 if (S.Index > Best.Index)
510 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
515 if (Best.Index ==
size_t(-1))
516 return {longestMatch(Filename, Paths).second, 0};
517 return {Best.Index, Best.Points};
522 template <
bool Prefix>
523 ArrayRef<SubstringAndIndex>
524 indexLookup(StringRef Key, ArrayRef<SubstringAndIndex> Idx)
const {
526 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
532 SubstringAndIndex longestMatch(StringRef Key,
533 ArrayRef<SubstringAndIndex> Idx)
const {
534 assert(!Idx.empty());
536 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
537 if (It == Idx.begin())
542 size_t Prefix = matchingPrefix(Key, It->first);
543 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
544 return Prefix > PrevPrefix ? *It : *--It;
548 std::vector<std::string> OriginalPaths;
549 BumpPtrAllocator Arena;
553 std::vector<SubstringAndIndex> Paths;
556 std::vector<types::ID> Types;
557 std::vector<SubstringAndIndex> Stems;
558 std::vector<SubstringAndIndex> Components;
566 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
567 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
569 std::vector<CompileCommand>
570 getCompileCommands(StringRef Filename)
const override {
571 auto Known = Inner->getCompileCommands(Filename);
572 if (Index.empty() || !Known.empty())
575 auto Lang = guessType(Filename, &TypeCertain);
577 Lang = types::TY_INVALID;
579 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
580 if (ProxyCommands.empty())
585 std::vector<std::string> getAllFiles()
const override {
586 return Inner->getAllFiles();
589 std::vector<CompileCommand> getAllCompileCommands()
const override {
590 return Inner->getAllCompileCommands();
594 std::unique_ptr<CompilationDatabase> Inner;
600std::unique_ptr<CompilationDatabase>
602 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
606 StringRef Filename) {
607 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)