clang: lib/Tooling/InterpolatingCompilationDatabase.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

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"

59#include

60#include

61

63namespace tooling {

64namespace {

65using namespace llvm;

67namespace path = llvm::sys::path;

68

69

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)

73 if (L[I] != R[I])

74 return I;

75 return Limit;

76}

77

78

79

80template 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;

83 return Key < V;

84 }

85 bool operator()(std::pair<StringRef, size_t> Value, StringRef Key) const {

86 StringRef V = Prefix ? Value.first.substr(0, Key.size()) : Value.first;

87 return V < Key;

88 }

89};

90

91

92

94

97 if (Certain)

100}

101

102

103

105 switch (Lang) {

106 case types::TY_C:

107 case types::TY_CHeader:

108 return types::TY_C;

109 case types::TY_ObjC:

110 case types::TY_ObjCHeader:

111 return types::TY_ObjC;

112 case types::TY_CXX:

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;

118 case types::TY_CUDA:

119 case types::TY_CUDA_DEVICE:

120 return types::TY_CUDA;

121 default:

123 }

124}

125

126

127struct TransferableCommand {

128

130

131 std::optionaltypes::ID Type;

132

134

136

137 TransferableCommand(CompileCommand C)

139 std::vectorstd::string OldArgs = std::move(Cmd.CommandLine);

140 Cmd.CommandLine.clear();

141

142

143 llvm::opt::InputArgList ArgList;

144 {

146 for (const std::string &S : OldArgs)

147 TmpArgv.push_back(S.c_str());

151 ArgList = {TmpArgv.begin(), TmpArgv.end()};

152 }

153

154

155

156

157

159 if (!OldArgs.empty())

160 Cmd.CommandLine.emplace_back(OldArgs.front());

161 for (unsigned Pos = 1; Pos < OldArgs.size();) {

162 using namespace driver::options;

163

164 const unsigned OldPos = Pos;

165 std::unique_ptrllvm::opt::Arg Arg(OptTable.ParseOneArg(

166 ArgList, Pos,

167 llvm::opt::Visibility(ClangCLMode ? CLOption : ClangOption)));

168

169 if (!Arg)

170 continue;

171

172 const llvm::opt::Option &Opt = Arg->getOption();

173

174

175 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||

176 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||

177 Opt.matches(OPT__SLASH_Fe) ||

178 Opt.matches(OPT__SLASH_Fi) ||

179 Opt.matches(OPT__SLASH_Fo))))

180 continue;

181

182

183 if (Opt.matches(OPT__DASH_DASH))

184 break;

185

186

187 if (const auto GivenType = tryParseTypeArg(*Arg)) {

188 Type = *GivenType;

189 continue;

190 }

191

192

193 if (const auto GivenStd = tryParseStdArg(*Arg)) {

195 Std = *GivenStd;

196 continue;

197 }

198

199 Cmd.CommandLine.insert(Cmd.CommandLine.end(),

200 OldArgs.data() + OldPos, OldArgs.data() + Pos);

201 }

202

203

207

209 Type = std::nullopt;

210 }

211

212

213

214 CompileCommand transferTo(StringRef Filename) && {

215 CompileCommand Result = std::move(Cmd);

216 Result.Heuristic = "inferred from " + Result.Filename;

218 bool TypeCertain;

219 auto TargetType = guessType(Filename, &TypeCertain);

220

221 if ((!TargetType || !TypeCertain) && Type) {

222

223

224 TargetType =

228 if (ClangCLMode) {

229 const StringRef Flag = toCLFlag(TargetType);

230 if (!Flag.empty())

231 Result.CommandLine.push_back(std::string(Flag));

232 } else {

233 Result.CommandLine.push_back("-x");

235 }

236 }

237

238

240 Result.CommandLine.emplace_back((

241 llvm::Twine(ClangCLMode ? "/std:" : "-std=") +

243 }

244 Result.CommandLine.push_back("--");

247 }

248

249private:

250

252 switch (Lang) {

254 return types::TY_C;

256 return types::TY_CXX;

258 return types::TY_ObjC;

260 return types::TY_ObjCXX;

261 default:

263 }

264 }

265

266

267 static StringRef toCLFlag(types::ID Type) {

268 switch (Type) {

269 case types::TY_C:

270 case types::TY_CHeader:

271 return "/TC";

272 case types::TY_CXX:

273 case types::TY_CXXHeader:

274 return "/TP";

275 default:

276 return StringRef();

277 }

278 }

279

280

281 std::optionaltypes::ID tryParseTypeArg(const llvm::opt::Arg &Arg) {

282 const llvm::opt::Option &Opt = Arg.getOption();

283 using namespace driver::options;

284 if (ClangCLMode) {

285 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))

286 return types::TY_C;

287 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))

288 return types::TY_CXX;

289 } else {

290 if (Opt.matches(driver::options::OPT_x))

292 }

293 return std::nullopt;

294 }

295

296

297 std::optionalLangStandard::Kind tryParseStdArg(const llvm::opt::Arg &Arg) {

298 using namespace driver::options;

299 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))

301 return std::nullopt;

302 }

303};

304

305

306

307

308

309

310

311

312

313

314

315

316class FileIndex {

317public:

318 FileIndex(std::vectorstd::string Files)

319 : OriginalPaths(std::move(Files)), Strings(Arena) {

320

321 llvm::sort(OriginalPaths);

322 Paths.reserve(OriginalPaths.size());

323 Types.reserve(OriginalPaths.size());

324 Stems.reserve(OriginalPaths.size());

325 for (size_t I = 0; I < OriginalPaths.size(); ++I) {

326 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());

327

328 Paths.emplace_back(Path, I);

329 Types.push_back(foldType(guessType(OriginalPaths[I])));

330 Stems.emplace_back(sys::path::stem(Path), I);

331 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);

332 for (int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)

333 if (Dir->size() > ShortDirectorySegment)

334 Components.emplace_back(*Dir, I);

335 }

336 llvm::sort(Paths);

337 llvm::sort(Stems);

338 llvm::sort(Components);

339 }

340

341 bool empty() const { return Paths.empty(); }

342

343

344

345

346 StringRef chooseProxy(StringRef OriginalFilename,

347 types::ID PreferLanguage) const {

348 assert(!empty() && "need at least one candidate!");

349 std::string Filename = OriginalFilename.lower();

350 auto Candidates = scoreCandidates(Filename);

351 std::pair<size_t, int> Best =

352 pickWinner(Candidates, Filename, PreferLanguage);

353

354 DEBUG_WITH_TYPE(

355 "interpolate",

356 llvm::dbgs() << "interpolate: chose " << OriginalPaths[Best.first]

357 << " as proxy for " << OriginalFilename << " preferring "

359 ? "none"

361 << " score=" << Best.second << "\n");

362 return OriginalPaths[Best.first];

363 }

364

365private:

366 using SubstringAndIndex = std::pair<StringRef, size_t>;

367

368

369

370 constexpr static int DirectorySegmentsIndexed = 4;

371 constexpr static int DirectorySegmentsQueried = 2;

372 constexpr static int ShortDirectorySegment = 1;

373

374

375

376 DenseMap<size_t, int> scoreCandidates(StringRef Filename) const {

377

378

379

380 StringRef Stem = sys::path::stem(Filename);

382 llvm::StringRef Prefix;

383 auto Dir = ++sys::path::rbegin(Filename),

384 DirEnd = sys::path::rend(Filename);

385 for (int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {

386 if (Dir->size() > ShortDirectorySegment)

387 Dirs.push_back(*Dir);

388 Prefix = Filename.substr(0, Dir - DirEnd);

389 }

390

391

392 DenseMap<size_t, int> Candidates;

394 for (const auto &Entry : Range)

395 Candidates[Entry.second] += Points;

396 };

397

398

399 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));

400 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));

401

402

403 for (StringRef Dir : Dirs)

404 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));

405

406 if (sys::path::root_directory(Prefix) != Prefix)

407 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));

408 return Candidates;

409 }

410

411

412

413 std::pair<size_t, int> pickWinner(const DenseMap<size_t, int> &Candidates,

415 types::ID PreferredLanguage) const {

416 struct ScoredCandidate {

417 size_t Index;

418 bool Preferred;

419 int Points;

420 size_t PrefixLength;

421 };

422

423 ScoredCandidate Best = {size_t(-1), false, 0, 0};

424 for (const auto &Candidate : Candidates) {

425 ScoredCandidate S;

426 S.Index = Candidate.first;

428 PreferredLanguage == Types[S.Index];

429 S.Points = Candidate.second;

430 if (!S.Preferred && Best.Preferred)

431 continue;

432 if (S.Preferred == Best.Preferred) {

433 if (S.Points < Best.Points)

434 continue;

435 if (S.Points == Best.Points) {

436 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);

437 if (S.PrefixLength < Best.PrefixLength)

438 continue;

439

440 if (S.PrefixLength == Best.PrefixLength)

441 if (S.Index > Best.Index)

442 continue;

443 }

444 }

445

446

447 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);

448 Best = S;

449 }

450

451

452 if (Best.Index == size_t(-1))

453 return {longestMatch(Filename, Paths).second, 0};

454 return {Best.Index, Best.Points};

455 }

456

457

458

459 template

462

463 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,

464 Less());

466 }

467

468

469 SubstringAndIndex longestMatch(StringRef Key,

471 assert(!Idx.empty());

472

473 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});

474 if (It == Idx.begin())

475 return *It;

476 if (It == Idx.end())

477 return *--It;

478

479 size_t Prefix = matchingPrefix(Key, It->first);

480 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);

481 return Prefix > PrevPrefix ? *It : *--It;

482 }

483

484

485 std::vectorstd::string OriginalPaths;

486 BumpPtrAllocator Arena;

487 StringSaver Strings;

488

489

490 std::vector Paths;

491

492

493 std::vectortypes::ID Types;

494 std::vector Stems;

495 std::vector Components;

496};

497

498

499

500

501class InterpolatingCompilationDatabase : public CompilationDatabase {

502public:

503 InterpolatingCompilationDatabase(std::unique_ptr Inner)

504 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}

505

506 std::vector

507 getCompileCommands(StringRef Filename) const override {

508 auto Known = Inner->getCompileCommands(Filename);

509 if (Index.empty() || !Known.empty())

510 return Known;

511 bool TypeCertain;

512 auto Lang = guessType(Filename, &TypeCertain);

513 if (!TypeCertain)

515 auto ProxyCommands =

516 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));

517 if (ProxyCommands.empty())

518 return {};

520 }

521

522 std::vectorstd::string getAllFiles() const override {

523 return Inner->getAllFiles();

524 }

525

526 std::vector getAllCompileCommands() const override {

527 return Inner->getAllCompileCommands();

528 }

529

530private:

531 std::unique_ptr Inner;

532 FileIndex Index;

533};

534

535}

536

537std::unique_ptr

539 return std::make_unique(std::move(Inner));

540}

541

544 return TransferableCommand(std::move(Cmd)).transferTo(Filename);

545}

546

547}

548}

static std::string getName(const CallEvent &Call)

ID lookupTypeForTypeSpecifier(const char *Name)

lookupTypeForTypSpecifier - Lookup the type to use for a user specified type name.

bool onlyPrecompileType(ID Id)

onlyPrecompileType - Should this type only be precompiled.

const char * getTypeName(ID Id)

getTypeName - Return the name of the type for Id.

ID lookupHeaderTypeForSourceType(ID Id)

Lookup header file input type that corresponds to given source file type (used for clang-cl emulation...

ID lookupTypeForExtension(llvm::StringRef Ext)

lookupTypeForExtension - Lookup the type to use for the file extension Ext.

llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)

Returns the driver mode option's value, i.e.

const llvm::opt::OptTable & getDriverOptTable()

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.

Diagnostic wrappers for TextAPI types for error reporting.

static const LangStandard & getLangStandardForKind(Kind K)

static Kind getLangKind(StringRef Name)