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

1

2

3

4

5

6

7

8

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

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

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

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

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

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

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

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

26

27using namespace clang;

29

30#define DEBUG_TYPE "API Notes"

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

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

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

34STATISTIC(NumFrameworksSearched, "frameworks searched");

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

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

37

38namespace {

39

40

41class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {

42 StringRef First, Second;

43

44public:

45 PrettyStackTraceDoubleString(StringRef First, StringRef Second)

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

48};

49}

50

52 : SM(SM), ImplicitAPINotes(LangOpts.APINotes),

53 VersionIndependentSwift(LangOpts.SwiftVersionIndependentAPINotes) {}

54

56

57 for (const auto &Entry : Readers) {

58 if (auto Reader = dyn_cast_if_present<APINotesReader *>(Entry.second))

59 delete Reader;

60 }

61

62 delete CurrentModuleReaders[ReaderKind::Public];

63 delete CurrentModuleReaders[ReaderKind::Private];

64}

65

66std::unique_ptr

67APINotesManager::loadAPINotes(FileEntryRef APINotesFile) {

68 PrettyStackTraceDoubleString Trace("Loading API notes from ",

70

71

72 auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User);

73 auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation());

74 if (!SourceBuffer)

75 return nullptr;

76

77

78

79

80

81

83 std::unique_ptrllvm::MemoryBuffer CompiledBuffer;

84 {

86 SM, SM.getDiagnostics(), diag::err_apinotes_message,

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

88 llvm::raw_svector_ostream OS(APINotesBuffer);

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

91 SMAdapter.getDiagHandler(), SMAdapter.getDiagContext()))

92 return nullptr;

93

94

95 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(

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

97 }

98

99

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

102 return Reader;

103}

104

105std::unique_ptr

106APINotesManager::loadAPINotes(StringRef Buffer) {

108 std::unique_ptrllvm::MemoryBuffer CompiledBuffer;

110 SM, SM.getDiagnostics(), diag::err_apinotes_message,

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

112 llvm::raw_svector_ostream OS(APINotesBuffer);

113

115 SMAdapter.getDiagHandler(),

116 SMAdapter.getDiagContext()))

117 return nullptr;

118

119 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(

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

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

123 return Reader;

124}

125

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

127 FileEntryRef APINotesFile) {

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

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

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

131 return false;

132 }

133

134 Readers[HeaderDir] = nullptr;

135 return true;

136}

137

139APINotesManager::findAPINotesFile(DirectoryEntryRef Directory,

140 StringRef Basename, bool WantPublic) {

141 FileManager &FM = SM.getFileManager();

142

143 llvm::SmallString<128> Path(Directory.getName());

144

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

146

147

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

151}

152

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

155 FileManager &FM = SM.getFileManager();

156

157 llvm::SmallString<128> Path(FrameworkPath);

158 unsigned FrameworkNameLength = Path.size();

159

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

161

162

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

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

166

167

169 if (!APINotesFile)

170 return std::nullopt;

171

172

173 Path.resize(FrameworkNameLength);

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

175

176

178 if (!HeaderDir)

179 return std::nullopt;

180

181

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

183 return std::nullopt;

184

185

186 if (Public)

187 ++NumPublicFrameworkAPINotes;

188 else

189 ++NumPrivateFrameworkAPINotes;

190 return *HeaderDir;

191}

192

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

196 return;

197

198 StringRef RealFileName =

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

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

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

202 return;

203

204 unsigned DiagID = diag::warn_apinotes_private_case;

206 DiagID = diag::warn_apinotes_private_case_system;

207

209}

210

211

212

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

215 return Submodule->ModuleMapIsPrivate;

216 });

217}

218

219llvm::SmallVector<FileEntryRef, 2>

226

227

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

229

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

232 if (!WantPublic)

234

235 APINotes.push_back(*File);

236 }

237

238 if (!ExportedModuleName.empty())

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

240 APINotes.push_back(*File);

241 };

242

244

245

246

247

248

249

250

251

252

254

256 unsigned PathLen = Path.size();

257

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

260 tryAPINotes(*APINotesDir, true);

261

262 Path.resize(PathLen);

263 }

264

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

268 tryAPINotes(*PrivateAPINotesDir,

270 }

271 } else {

272

273

274

275

276

277

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

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

281 }

282

283 if (!APINotes.empty())

284 return APINotes;

285 }

286

287

288

289 for (const auto &SearchPath : SearchPaths) {

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

292 APINotes.push_back(*File);

293 return APINotes;

294 }

295 }

296 }

297

298

299 return APINotes;

300}

301

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

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

306

308 unsigned NumReaders = 0;

309 for (auto File : APINotes) {

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

313 }

314

315 return NumReaders > 0;

316}

317

320 unsigned NumReader = 0;

321 for (auto Buf : Buffers) {

322 auto Reader = loadAPINotes(Buf);

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

324

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

326 }

327 return NumReader;

328}

329

333

334

338 return Results;

339 }

340

341

342 if (!ImplicitAPINotes)

343 return Results;

344

345

347 return Results;

348

349

350

351 SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);

352 FileID ID = SM.getFileID(ExpansionLoc);

353 if (ID.isInvalid())

354 return Results;

357 return Results;

358

359

360

366 DirsVisited;

367 do {

368

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

370

371

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

373 ++NumDirectoryCacheHits;

374

375

377 DirsVisited.insert(*Dir);

379 continue;

380 }

381

382

383 if (auto Reader = dyn_cast_if_present<APINotesReader *>(Known->second))

384 Results.push_back(Reader);

385 break;

386 }

387

388

389 StringRef Path = Dir->getName();

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

391

392

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

394 ++NumFrameworksSearched;

395

396

398 loadFrameworkAPINotes(Path, FrameworkName, true);

400 loadFrameworkAPINotes(Path, FrameworkName, false);

401

402 if (PublicDir || PrivateDir) {

403

404 Readers[*Dir] = nullptr;

405

406

407

408

409 if (!DirsVisited.empty()) {

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

411 DirsVisited.pop_back();

412 Dir = *PublicDir;

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

414 DirsVisited.pop_back();

415 Dir = *PrivateDir;

416 }

417 }

418

419

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

421 Results.push_back(Reader);

422 break;

423 }

424 } else {

425

427 llvm::sys::path::append(

429

430

431 ++NumDirectoriesSearched;

432 if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) {

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

434 ++NumHeaderAPINotes;

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

436 Results.push_back(Reader);

437 break;

438 }

439 }

440 }

441

442

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

444 Dir = std::nullopt;

445 break;

446 }

447

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

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

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

451

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

453 : FileMgr.getOptionalDirectoryRef(ParentPath);

454 } while (Dir);

455

456

457

458

459 for (const auto Visited : DirsVisited)

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

461

462 return Results;

463}

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

Definition APINotesManager.cpp:193

static bool hasPrivateSubmodules(const Module *M)

Definition APINotesManager.cpp:213

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

Defines the Diagnostic-related interfaces.

Defines the clang::FileManager interface and associated types.

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.

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.

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

APINotesManager(SourceManager &SM, const LangOptions &LangOpts)

Definition APINotesManager.cpp:51

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.

Definition APINotesManager.cpp:220

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.

Definition APINotesManager.cpp:318

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

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

Definition APINotesManager.cpp:331

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

Load the API notes for the current module.

Definition APINotesManager.cpp:302

~APINotesManager()

Definition APINotesManager.cpp:55

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.

@ OS

Indicates that the tracking object is a descendant of a referenced-counted OSObject,...

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

bool isa(CodeGen::Address addr)

CustomizableOptional< FileEntryRef > OptionalFileEntryRef

CustomizableOptional< DirectoryEntryRef > OptionalDirectoryEntryRef

U cast(CodeGen::Address addr)