clang: lib/APINotes/APINotesManager.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

19#include "llvm/ADT/APInt.h"

20#include "llvm/ADT/Hashing.h"

21#include "llvm/ADT/SetVector.h"

22#include "llvm/ADT/SmallPtrSet.h"

23#include "llvm/ADT/SmallVector.h"

24#include "llvm/ADT/Statistic.h"

25#include "llvm/Support/MemoryBuffer.h"

26#include "llvm/Support/Path.h"

27#include "llvm/Support/PrettyStackTrace.h"

28

29using namespace clang;

30using namespace api_notes;

31

32#define DEBUG_TYPE "API Notes"

33STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded");

34STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded");

35STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded");

36STATISTIC(NumFrameworksSearched, "frameworks searched");

37STATISTIC(NumDirectoriesSearched, "header directories searched");

38STATISTIC(NumDirectoryCacheHits, "directory cache hits");

39

40namespace {

41

42

43class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {

44 StringRef First, Second;

45

46public:

47 PrettyStackTraceDoubleString(StringRef First, StringRef Second)

49 void print(raw_ostream &OS) const override { OS << First << Second; }

50};

51}

52

54 : SM(SM), ImplicitAPINotes(LangOpts.APINotes) {}

55

57

58 for (const auto &Entry : Readers) {

59 if (auto Reader = Entry.second.dyn_cast<APINotesReader *>())

60 delete Reader;

61 }

62

63 delete CurrentModuleReaders[ReaderKind::Public];

64 delete CurrentModuleReaders[ReaderKind::Private];

65}

66

67std::unique_ptr

68APINotesManager::loadAPINotes(FileEntryRef APINotesFile) {

69 PrettyStackTraceDoubleString Trace("Loading API notes from ",

71

72

75 if (!SourceBuffer)

76 return nullptr;

77

78

79

80

81

82

84 std::unique_ptrllvm::MemoryBuffer CompiledBuffer;

85 {

88 diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile);

89 llvm::raw_svector_ostream OS(APINotesBuffer);

91 SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS,

92 SMAdapter.getDiagHandler(), SMAdapter.getDiagContext()))

93 return nullptr;

94

95

96 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(

97 StringRef(APINotesBuffer.data(), APINotesBuffer.size()));

98 }

99

100

102 assert(Reader && "Could not load the API notes we just generated?");

103 return Reader;

104}

105

106std::unique_ptr

107APINotesManager::loadAPINotes(StringRef Buffer) {

109 std::unique_ptrllvm::MemoryBuffer CompiledBuffer;

112 diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt);

113 llvm::raw_svector_ostream OS(APINotesBuffer);

114

116 SMAdapter.getDiagHandler(),

117 SMAdapter.getDiagContext()))

118 return nullptr;

119

120 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(

121 StringRef(APINotesBuffer.data(), APINotesBuffer.size()));

123 assert(Reader && "Could not load the API notes we just generated?");

124 return Reader;

125}

126

127bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,

129 assert(!Readers.contains(HeaderDir));

130 if (auto Reader = loadAPINotes(APINotesFile)) {

131 Readers[HeaderDir] = Reader.release();

132 return false;

133 }

134

135 Readers[HeaderDir] = nullptr;

136 return true;

137}

138

141 StringRef Basename, bool WantPublic) {

143

145

146 StringRef Suffix = WantPublic ? "" : "_private";

147

148

149 llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." +

152}

153

155 llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) {

157

159 unsigned FrameworkNameLength = Path.size();

160

161 StringRef Suffix = Public ? "" : "_private";

162

163

164 llvm::sys::path::append(Path, "APINotes");

165 llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." +

167

168

170 if (!APINotesFile)

171 return std::nullopt;

172

173

174 Path.resize(FrameworkNameLength);

175 llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders");

176

177

179 if (!HeaderDir)

180 return std::nullopt;

181

182

183 if (loadAPINotes(*HeaderDir, *APINotesFile))

184 return std::nullopt;

185

186

187 if (Public)

188 ++NumPublicFrameworkAPINotes;

189 else

190 ++NumPrivateFrameworkAPINotes;

191 return *HeaderDir;

192}

193

196 if (File->tryGetRealPathName().empty())

197 return;

198

199 StringRef RealFileName =

200 llvm::sys::path::filename(File->tryGetRealPathName());

201 StringRef RealStem = llvm::sys::path::stem(RealFileName);

202 if (RealStem.ends_with("_private"))

203 return;

204

205 unsigned DiagID = diag::warn_apinotes_private_case;

207 DiagID = diag::warn_apinotes_private_case_system;

208

210}

211

212

213

215 return llvm::any_of(M->submodules(), [](const Module *Submodule) {

216 return Submodule->ModuleMapIsPrivate;

217 });

218}

219

227

228

229 if (LookInModule && M->Directory) {

230

232 if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) {

233 if (!WantPublic)

235

236 APINotes.push_back(*File);

237 }

238

239 if (!ExportedModuleName.empty())

240 if (auto File = findAPINotesFile(Dir, ExportedModuleName, WantPublic))

241 APINotes.push_back(*File);

242 };

243

245

246

247

248

249

250

251

252

253

255

257 unsigned PathLen = Path.size();

258

259 llvm::sys::path::append(Path, "Headers");

261 tryAPINotes(*APINotesDir, true);

262

263 Path.resize(PathLen);

264 }

265

267 llvm::sys::path::append(Path, "PrivateHeaders");

269 tryAPINotes(*PrivateAPINotesDir,

271 }

272 } else {

273

274

275

276

277

278

279 tryAPINotes(*M->Directory, true);

281 tryAPINotes(*M->Directory, false);

282 }

283

284 if (!APINotes.empty())

285 return APINotes;

286 }

287

288

289

290 for (const auto &SearchPath : SearchPaths) {

292 if (auto File = findAPINotesFile(*SearchDir, ModuleName)) {

293 APINotes.push_back(*File);

294 return APINotes;

295 }

296 }

297 }

298

299

300 return APINotes;

301}

302

305 assert(!CurrentModuleReaders[ReaderKind::Public] &&

306 "Already loaded API notes for the current module?");

307

309 unsigned NumReaders = 0;

310 for (auto File : APINotes) {

311 CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release();

314 }

315

316 return NumReaders > 0;

317}

318

321 unsigned NumReader = 0;

322 for (auto Buf : Buffers) {

323 auto Reader = loadAPINotes(Buf);

324 assert(Reader && "Could not load the API notes we just generated?");

325

326 CurrentModuleReaders[NumReader++] = Reader.release();

327 }

328 return NumReader;

329}

330

334

335

339 return Results;

340 }

341

342

343 if (!ImplicitAPINotes)

344 return Results;

345

346

348 return Results;

349

350

351

354 if (ID.isInvalid())

355 return Results;

358 return Results;

359

360

361

367 DirsVisited;

368 do {

369

370 auto Known = Readers.find(*Dir);

371

372

373 if (Known != Readers.end()) {

374 ++NumDirectoryCacheHits;

375

376

377 if (Known->second && isa(Known->second)) {

378 DirsVisited.insert(*Dir);

379 Dir = cast(Known->second);

380 continue;

381 }

382

383

384 if (auto Reader = Known->second.dyn_cast<APINotesReader *>())

385 Results.push_back(Reader);

386 break;

387 }

388

389

391 if (llvm::sys::path::extension(Path) == ".framework") {

392

393

394 auto FrameworkName = llvm::sys::path::stem(Path);

395 ++NumFrameworksSearched;

396

397

399 loadFrameworkAPINotes(Path, FrameworkName, true);

401 loadFrameworkAPINotes(Path, FrameworkName, false);

402

403 if (PublicDir || PrivateDir) {

404

405 Readers[*Dir] = nullptr;

406

407

408

409

410 if (!DirsVisited.empty()) {

411 if (PublicDir && DirsVisited.back() == *PublicDir) {

412 DirsVisited.pop_back();

413 Dir = *PublicDir;

414 } else if (PrivateDir && DirsVisited.back() == *PrivateDir) {

415 DirsVisited.pop_back();

416 Dir = *PrivateDir;

417 }

418 }

419

420

421 if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())

422 Results.push_back(Reader);

423 break;

424 }

425 } else {

426

428 llvm::sys::path::append(

430

431

432 ++NumDirectoriesSearched;

434 if (!loadAPINotes(*Dir, *APINotesFile)) {

435 ++NumHeaderAPINotes;

436 if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())

437 Results.push_back(Reader);

438 break;

439 }

440 }

441 }

442

443

444 if (!DirsVisited.insert(*Dir)) {

445 Dir = std::nullopt;

446 break;

447 }

448

449 StringRef ParentPath = llvm::sys::path::parent_path(Path);

450 while (llvm::sys::path::stem(ParentPath) == "..")

451 ParentPath = llvm::sys::path::parent_path(ParentPath);

452

453 Dir = ParentPath.empty() ? std::nullopt

455 } while (Dir);

456

457

458

459

460 for (const auto Visited : DirsVisited)

461 Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry();

462

463 return Results;

464}

static void checkPrivateAPINotesName(DiagnosticsEngine &Diags, const FileEntry *File, const Module *M)

static bool hasPrivateSubmodules(const Module *M)

STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded")

Defines the Diagnostic-related interfaces.

Defines the clang::FileManager interface and associated types.

llvm::DenseSet< const void * > Visited

static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx, QualType Ty)

Defines the clang::LangOptions interface.

Defines the clang::Module class, which describes a module in the source code.

Defines the SourceManager interface.

Defines version macros and version-related utility functions for Clang.

Concrete class used by the front-end to report problems and issues.

DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)

Issue the message to the client.

A reference to a DirectoryEntry that includes the name of the directory as it was accessed by the Fil...

StringRef getName() const

Cached information about one directory (either on disk or in the virtual file system).

A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...

StringRef getName() const

The name of this FileEntry.

Cached information about one file (either on disk or in the virtual file system).

An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...

Implements support for file system lookup, file system caching, and directory search management.

OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)

Get a FileEntryRef if it exists, without doing anything on error.

OptionalDirectoryEntryRef getOptionalDirectoryRef(StringRef DirName, bool CacheFailure=true)

Get a DirectoryEntryRef if it exists, without doing anything on error.

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

Describes a module or submodule.

StringRef getTopLevelModuleName() const

Retrieve the name of the top-level module.

unsigned IsSystem

Whether this is a "system" module (which assumes that all headers in it are system headers).

std::string Name

The name of this module.

llvm::iterator_range< submodule_iterator > submodules()

unsigned ModuleMapIsPrivate

Whether this module came from a "private" module map, found next to a regular (public) module map.

OptionalDirectoryEntryRef Directory

The build directory of this module.

std::string APINotesFile

For the debug info, the path to this module's .apinotes file, if any.

unsigned IsFramework

Whether this is a framework module.

std::string ExportAsModule

The module through which entities defined in this module will eventually be exposed,...

Module * getTopLevelModule()

Retrieve the top-level module for this (sub)module, which may be this module.

Encodes a location in the source.

This class handles loading and caching of source files into memory.

FileID getFileID(SourceLocation SpellingLoc) const

Return the FileID for a SourceLocation.

DiagnosticsEngine & getDiagnostics() const

OptionalFileEntryRef getFileEntryRefForID(FileID FID) const

Returns the FileEntryRef for the provided FileID.

FileManager & getFileManager() const

FileID getOrCreateFileID(FileEntryRef SourceFile, SrcMgr::CharacteristicKind FileCharacter)

Get the FileID for SourceFile if it exists.

const FileEntry * getFileEntryForID(FileID FID) const

Returns the FileEntry record for the provided FileID.

SourceLocation getExpansionLoc(SourceLocation Loc) const

Given a SourceLocation object Loc, return the expansion location referenced by the ID.

std::optional< llvm::MemoryBufferRef > getBufferOrNone(FileID FID, SourceLocation Loc=SourceLocation()) const

Return the buffer for the specified FileID.

An adapter that can be used to translate diagnostics from one or more llvm::SourceMgr instances to a ...

APINotesManager(SourceManager &SM, const LangOptions &LangOpts)

llvm::SmallVector< FileEntryRef, 2 > getCurrentModuleAPINotes(Module *M, bool LookInModule, ArrayRef< std::string > SearchPaths)

Get FileEntry for the APINotes of the module that is currently being compiled.

ArrayRef< APINotesReader * > getCurrentModuleReaders() const

Retrieve the set of API notes readers for the current module.

bool loadCurrentModuleAPINotesFromBuffer(ArrayRef< StringRef > Buffers)

Load Compiled API notes for current module.

llvm::SmallVector< APINotesReader *, 2 > findAPINotes(SourceLocation Loc)

Find the API notes readers that correspond to the given source location.

bool loadCurrentModuleAPINotes(Module *M, bool LookInModule, ArrayRef< std::string > SearchPaths)

Load the API notes for the current module.

A class that reads API notes data from a binary file that was written by the APINotesWriter.

static std::unique_ptr< APINotesReader > Create(std::unique_ptr< llvm::MemoryBuffer > InputBuffer, llvm::VersionTuple SwiftVersion)

Create a new API notes reader from the given member buffer, which contains the contents of a binary A...

bool compileAPINotes(llvm::StringRef YAMLInput, const FileEntry *SourceFile, llvm::raw_ostream &OS, llvm::SourceMgr::DiagHandlerTy DiagHandler=nullptr, void *DiagHandlerCtxt=nullptr)

Converts API notes from YAML format to binary format.

static const constexpr char SOURCE_APINOTES_EXTENSION[]

The file extension used for the source representation of API notes.

@ Public

Represents declarations accessible to all clients.

The JSON file list parser is used to communicate input to InstallAPI.