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_cxx26;
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 == latestLangStandardC())
283 Spelling =
"clatest";
284 else if (Std == latestLangStandardCXX())
285 Spelling =
"c++latest";
288 Result.CommandLine.emplace_back(
289 (llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") + Spelling).str());
292 Result.CommandLine.push_back(
"--");
293 Result.CommandLine.push_back(std::string(Filename));
299 static types::ID toType(
Language Lang) {
304 return types::TY_CXX;
306 return types::TY_ObjC;
308 return types::TY_ObjCXX;
310 return types::TY_INVALID;
315 static StringRef toCLFlag(types::ID Type) {
318 case types::TY_CHeader:
321 case types::TY_CXXHeader:
329 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
330 const llvm::opt::Option &Opt = Arg.getOption();
331 using namespace options;
333 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
335 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
336 return types::TY_CXX;
338 if (Opt.matches(options::OPT_x))
339 return types::lookupTypeForTypeSpecifier(Arg.getValue());
345 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
346 using namespace options;
347 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
351 if (StringRef(Arg.getValue()) ==
"c++23preview")
352 return LangStandard::lang_cxx23;
353 if (StringRef(Arg.getValue()) ==
"clatest")
354 return latestLangStandardC();
355 if (StringRef(Arg.getValue()) ==
"c++latest")
356 return latestLangStandardCXX();
377 FileIndex(std::vector<std::string> Files)
378 : OriginalPaths(std::move(Files)), Strings(Arena) {
380 llvm::sort(OriginalPaths);
381 Paths.reserve(OriginalPaths.size());
382 Types.reserve(OriginalPaths.size());
383 Stems.reserve(OriginalPaths.size());
384 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
385 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
387 Paths.emplace_back(Path, I);
388 Types.push_back(foldType(guessType(OriginalPaths[I])));
389 Stems.emplace_back(sys::path::stem(Path), I);
390 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
391 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
392 if (Dir->size() > ShortDirectorySegment)
393 Components.emplace_back(*Dir, I);
397 llvm::sort(Components);
400 bool empty()
const {
return Paths.empty(); }
405 StringRef chooseProxy(StringRef OriginalFilename,
406 types::ID PreferLanguage)
const {
407 assert(!empty() &&
"need at least one candidate!");
408 std::string Filename = OriginalFilename.lower();
409 auto Candidates = scoreCandidates(Filename);
410 std::pair<size_t, int> Best =
411 pickWinner(Candidates, Filename, PreferLanguage);
415 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
416 <<
" as proxy for " << OriginalFilename <<
" preferring "
417 << (PreferLanguage == types::TY_INVALID
419 : types::getTypeName(PreferLanguage))
420 <<
" score=" << Best.second <<
"\n");
421 return OriginalPaths[Best.first];
425 using SubstringAndIndex = std::pair<StringRef, size_t>;
429 constexpr static int DirectorySegmentsIndexed = 4;
430 constexpr static int DirectorySegmentsQueried = 2;
431 constexpr static int ShortDirectorySegment = 1;
435 DenseMap<size_t, int> scoreCandidates(StringRef Filename)
const {
439 StringRef Stem = sys::path::stem(Filename);
440 llvm::SmallVector<StringRef, DirectorySegmentsQueried> Dirs;
441 llvm::StringRef Prefix;
442 auto Dir = ++sys::path::rbegin(Filename),
443 DirEnd = sys::path::rend(Filename);
444 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
445 if (Dir->size() > ShortDirectorySegment)
446 Dirs.push_back(*Dir);
447 Prefix = Filename.substr(0, Dir - DirEnd);
451 DenseMap<size_t, int> Candidates;
452 auto Award = [&](
int Points, ArrayRef<SubstringAndIndex>
Range) {
453 for (
const auto &Entry : Range)
454 Candidates[Entry.second] += Points;
458 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
459 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
462 for (StringRef Dir : Dirs)
463 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
465 if (sys::path::root_directory(Prefix) != Prefix)
466 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
472 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
474 types::ID PreferredLanguage)
const {
475 struct ScoredCandidate {
482 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
483 for (
const auto &Candidate : Candidates) {
485 S.Index = Candidate.first;
486 S.Preferred = PreferredLanguage == types::TY_INVALID ||
487 PreferredLanguage == Types[S.Index];
488 S.Points = Candidate.second;
489 if (!S.Preferred && Best.Preferred)
491 if (S.Preferred == Best.Preferred) {
492 if (S.Points < Best.Points)
494 if (S.Points == Best.Points) {
495 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
496 if (S.PrefixLength < Best.PrefixLength)
499 if (S.PrefixLength == Best.PrefixLength)
500 if (S.Index > Best.Index)
506 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
511 if (Best.Index ==
size_t(-1))
512 return {longestMatch(Filename, Paths).second, 0};
513 return {Best.Index, Best.Points};
518 template <
bool Prefix>
519 ArrayRef<SubstringAndIndex>
520 indexLookup(StringRef Key, ArrayRef<SubstringAndIndex> Idx)
const {
522 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
528 SubstringAndIndex longestMatch(StringRef Key,
529 ArrayRef<SubstringAndIndex> Idx)
const {
530 assert(!Idx.empty());
532 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
533 if (It == Idx.begin())
538 size_t Prefix = matchingPrefix(Key, It->first);
539 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
540 return Prefix > PrevPrefix ? *It : *--It;
544 std::vector<std::string> OriginalPaths;
545 BumpPtrAllocator Arena;
549 std::vector<SubstringAndIndex> Paths;
552 std::vector<types::ID> Types;
553 std::vector<SubstringAndIndex> Stems;
554 std::vector<SubstringAndIndex> Components;
562 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
563 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
565 std::vector<CompileCommand>
566 getCompileCommands(StringRef Filename)
const override {
567 auto Known = Inner->getCompileCommands(Filename);
568 if (Index.empty() || !Known.empty())
571 auto Lang = guessType(Filename, &TypeCertain);
573 Lang = types::TY_INVALID;
575 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
576 if (ProxyCommands.empty())
581 std::vector<std::string> getAllFiles()
const override {
582 return Inner->getAllFiles();
585 std::vector<CompileCommand> getAllCompileCommands()
const override {
586 return Inner->getAllCompileCommands();
590 std::unique_ptr<CompilationDatabase> Inner;
596std::unique_ptr<CompilationDatabase>
598 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
602 StringRef Filename) {
603 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)