49 #include "llvm/ADT/DenseMap.h" 50 #include "llvm/ADT/Optional.h" 51 #include "llvm/ADT/StringExtras.h" 52 #include "llvm/ADT/StringSwitch.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" 66 namespace path = llvm::sys::path;
69 size_t matchingPrefix(StringRef L, StringRef R) {
70 size_t Limit =
std::min(L.size(), R.size());
71 for (
size_t I = 0; I < Limit; ++I)
79 template <
bool Prefix>
struct Less {
80 bool operator()(StringRef Key, std::pair<StringRef, size_t>
Value)
const {
81 StringRef
V = Prefix ? Value.first.substr(0, Key.size()) : Value.first;
84 bool operator()(std::pair<StringRef, size_t> Value, StringRef Key)
const {
85 StringRef V = Prefix ? Value.first.substr(0, Key.size()) : Value.first;
106 case types::TY_CHeader:
109 case types::TY_ObjCHeader:
110 return types::TY_ObjC;
112 case types::TY_CXXHeader:
113 return types::TY_CXX;
114 case types::TY_ObjCXX:
115 case types::TY_ObjCXXHeader:
116 return types::TY_ObjCXX;
123 struct TransferableCommand {
133 TransferableCommand(CompileCommand C)
134 : Cmd(
std::move(C)), Type(guessType(Cmd.Filename)),
135 ClangCLMode(checkIsCLMode(Cmd.CommandLine)) {
136 std::vector<std::string> OldArgs = std::move(Cmd.CommandLine);
137 Cmd.CommandLine.clear();
140 llvm::opt::InputArgList ArgList;
143 for (
const std::string &S : OldArgs)
144 TmpArgv.push_back(S.c_str());
145 ArgList = {TmpArgv.begin(), TmpArgv.end()};
153 if (!OldArgs.empty())
154 Cmd.CommandLine.emplace_back(OldArgs.front());
155 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
156 using namespace driver::options;
158 const unsigned OldPos = Pos;
159 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
167 const llvm::opt::Option &Opt = Arg->getOption();
170 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
171 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||
172 Opt.matches(OPT__SLASH_Fe) ||
173 Opt.matches(OPT__SLASH_Fi) ||
174 Opt.matches(OPT__SLASH_Fo))))
178 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
184 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
190 Cmd.CommandLine.insert(Cmd.CommandLine.end(),
191 OldArgs.data() + OldPos, OldArgs.data() + Pos);
197 Type = foldType(*Type);
204 CompileCommand transferTo(StringRef Filename)
const {
205 CompileCommand Result =
Cmd;
208 auto TargetType = guessType(Filename, &TypeCertain);
210 if ((!TargetType || !TypeCertain) && Type) {
218 const StringRef Flag = toCLFlag(TargetType);
220 Result.CommandLine.push_back(Flag);
222 Result.CommandLine.push_back(
"-x");
229 Result.CommandLine.emplace_back((
230 llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") +
233 Result.CommandLine.push_back(Filename);
234 Result.Heuristic =
"inferred from " + Cmd.Filename;
242 for (StringRef S : llvm::reverse(CmdLine)) {
243 if (S.consume_front(
"--driver-mode="))
248 return !CmdLine.empty() &&
249 llvm::sys::path::stem(CmdLine.front()).endswith_lower(
"cl");
258 return types::TY_CXX;
260 return types::TY_ObjC;
262 return types::TY_ObjCXX;
269 static StringRef toCLFlag(
types::ID Type) {
272 case types::TY_CHeader:
275 case types::TY_CXXHeader:
284 const llvm::opt::Option &Opt = Arg.getOption();
285 using namespace driver::options;
287 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
289 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
290 return types::TY_CXX;
292 if (Opt.matches(driver::options::OPT_x))
300 using namespace driver::options;
301 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
320 FileIndex(std::vector<std::string> Files)
321 : OriginalPaths(std::move(Files)), Strings(Arena) {
323 llvm::sort(OriginalPaths);
324 Paths.reserve(OriginalPaths.size());
325 Types.reserve(OriginalPaths.size());
326 Stems.reserve(OriginalPaths.size());
327 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
328 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
330 Paths.emplace_back(Path, I);
331 Types.push_back(foldType(guessType(Path)));
332 Stems.emplace_back(sys::path::stem(Path), I);
333 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
334 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
335 if (Dir->size() > ShortDirectorySegment)
336 Components.emplace_back(*Dir, I);
340 llvm::sort(Components);
343 bool empty()
const {
return Paths.empty(); }
348 StringRef chooseProxy(StringRef OriginalFilename,
350 assert(!empty() &&
"need at least one candidate!");
351 std::string Filename = OriginalFilename.lower();
352 auto Candidates = scoreCandidates(Filename);
353 std::pair<size_t, int> Best =
354 pickWinner(Candidates, Filename, PreferLanguage);
358 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
359 <<
" as proxy for " << OriginalFilename <<
" preferring " 363 <<
" score=" << Best.second <<
"\n");
364 return OriginalPaths[Best.first];
368 using SubstringAndIndex = std::pair<StringRef, size_t>;
372 constexpr
static int DirectorySegmentsIndexed = 4;
373 constexpr
static int DirectorySegmentsQueried = 2;
374 constexpr
static int ShortDirectorySegment = 1;
378 DenseMap<size_t, int> scoreCandidates(StringRef Filename)
const {
382 StringRef Stem = sys::path::stem(Filename);
384 llvm::StringRef Prefix;
385 auto Dir = ++sys::path::rbegin(Filename),
386 DirEnd = sys::path::rend(Filename);
387 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
388 if (Dir->size() > ShortDirectorySegment)
389 Dirs.push_back(*Dir);
390 Prefix = Filename.substr(0, Dir - DirEnd);
394 DenseMap<size_t, int> Candidates;
396 for (
const auto &Entry : Range)
397 Candidates[Entry.second] += Points;
401 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
402 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
405 for (StringRef Dir : Dirs)
406 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
408 if (sys::path::root_directory(Prefix) != Prefix)
409 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
415 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
418 struct ScoredCandidate {
425 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
426 for (
const auto &Candidate : Candidates) {
428 S.Index = Candidate.first;
430 PreferredLanguage == Types[S.Index];
431 S.Points = Candidate.second;
432 if (!S.Preferred && Best.Preferred)
434 if (S.Preferred == Best.Preferred) {
435 if (S.Points < Best.Points)
437 if (S.Points == Best.Points) {
438 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
439 if (S.PrefixLength < Best.PrefixLength)
442 if (S.PrefixLength == Best.PrefixLength)
443 if (S.Index > Best.Index)
449 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
454 if (Best.Index ==
size_t(-1))
455 return {longestMatch(Filename, Paths).second, 0};
456 return {Best.Index, Best.Points};
461 template <
bool Prefix>
465 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
467 return {Range.first, Range.second};
471 SubstringAndIndex longestMatch(StringRef Key,
473 assert(!Idx.empty());
475 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
476 if (It == Idx.begin())
481 size_t Prefix = matchingPrefix(Key, It->first);
482 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
483 return Prefix > PrevPrefix ? *It : *--It;
487 std::vector<std::string> OriginalPaths;
488 BumpPtrAllocator Arena;
492 std::vector<SubstringAndIndex> Paths;
495 std::vector<types::ID> Types;
496 std::vector<SubstringAndIndex> Stems;
497 std::vector<SubstringAndIndex> Components;
503 class InterpolatingCompilationDatabase :
public CompilationDatabase {
505 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
506 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
508 std::vector<CompileCommand>
509 getCompileCommands(StringRef Filename)
const override {
510 auto Known = Inner->getCompileCommands(Filename);
511 if (Index.empty() || !Known.empty())
514 auto Lang = guessType(Filename, &TypeCertain);
518 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
519 if (ProxyCommands.empty())
521 return {TransferableCommand(ProxyCommands[0]).transferTo(Filename)};
524 std::vector<std::string> getAllFiles()
const override {
525 return Inner->getAllFiles();
528 std::vector<CompileCommand> getAllCompileCommands()
const override {
529 return Inner->getAllCompileCommands();
533 std::unique_ptr<CompilationDatabase> Inner;
539 std::unique_ptr<CompilationDatabase>
541 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
static const LangStandard & getLangStandardForKind(Kind K)
static Kind getLangKind(StringRef Name)
__SIZE_TYPE__ size_t
The unsigned integer type of the result of the sizeof operator.
Languages that the frontend can parse and compile.
Language
The language for the input, used to select and validate the language standard and possible actions...
const llvm::opt::OptTable & getDriverOptTable()
ID lookupHeaderTypeForSourceType(ID Id)
Lookup header file input type that corresponds to given source file type (used for clang-cl emulation...
const char * getTypeName(ID Id)
getTypeName - Return the name of the type for Id.
Optional< types::ID > Type
Dataflow Directional Tag Classes.
static std::string getName(const CallEvent &Call)
ID lookupTypeForExtension(llvm::StringRef Ext)
lookupTypeForExtension - Lookup the type to use for the file extension Ext.
bool onlyPrecompileType(ID Id)
onlyPrecompileType - Should this type only be precompiled.
__DEVICE__ int min(int __a, int __b)
ID lookupTypeForTypeSpecifier(const char *Name)
lookupTypeForTypSpecifier - Lookup the type to use for a user specified type name.