[APINotes] Upstream APINotesManager
This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes
This commit is contained in:
parent
ec42d547eb
commit
f049395fc8
175
clang/include/clang/APINotes/APINotesManager.h
Normal file
175
clang/include/clang/APINotes/APINotesManager.h
Normal file
@ -0,0 +1,175 @@
|
||||
//===--- APINotesManager.h - Manage API Notes Files -------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H
|
||||
#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H
|
||||
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/VersionTuple.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class DirectoryEntry;
|
||||
class FileEntry;
|
||||
class LangOptions;
|
||||
class SourceManager;
|
||||
|
||||
namespace api_notes {
|
||||
|
||||
class APINotesReader;
|
||||
|
||||
/// The API notes manager helps find API notes associated with declarations.
|
||||
///
|
||||
/// API notes are externally-provided annotations for declarations that can
|
||||
/// introduce new attributes (covering availability, nullability of
|
||||
/// parameters/results, and so on) for specific declarations without directly
|
||||
/// modifying the headers that contain those declarations.
|
||||
///
|
||||
/// The API notes manager is responsible for finding and loading the
|
||||
/// external API notes files that correspond to a given header. Its primary
|
||||
/// operation is \c findAPINotes(), which finds the API notes reader that
|
||||
/// provides information about the declarations at that location.
|
||||
class APINotesManager {
|
||||
using ReaderEntry = llvm::PointerUnion<DirectoryEntryRef, APINotesReader *>;
|
||||
|
||||
SourceManager &SM;
|
||||
|
||||
/// Whether to implicitly search for API notes files based on the
|
||||
/// source file from which an entity was declared.
|
||||
bool ImplicitAPINotes;
|
||||
|
||||
/// The Swift version to use when interpreting versioned API notes.
|
||||
llvm::VersionTuple SwiftVersion;
|
||||
|
||||
enum ReaderKind : unsigned { Public = 0, Private = 1 };
|
||||
|
||||
/// API notes readers for the current module.
|
||||
///
|
||||
/// There can be up to two of these, one for public headers and one
|
||||
/// for private headers.
|
||||
///
|
||||
/// Not using std::unique_ptr to store these, since the reader pointers are
|
||||
/// also stored in llvm::PointerUnion below.
|
||||
APINotesReader *CurrentModuleReaders[2] = {nullptr, nullptr};
|
||||
|
||||
/// A mapping from header file directories to the API notes reader for
|
||||
/// that directory, or a redirection to another directory entry that may
|
||||
/// have more information, or NULL to indicate that there is no API notes
|
||||
/// reader for this directory.
|
||||
llvm::DenseMap<const DirectoryEntry *, ReaderEntry> Readers;
|
||||
|
||||
/// Load the API notes associated with the given file, whether it is
|
||||
/// the binary or source form of API notes.
|
||||
///
|
||||
/// \returns the API notes reader for this file, or null if there is
|
||||
/// a failure.
|
||||
std::unique_ptr<APINotesReader> loadAPINotes(FileEntryRef APINotesFile);
|
||||
|
||||
/// Load the API notes associated with the given buffer, whether it is
|
||||
/// the binary or source form of API notes.
|
||||
///
|
||||
/// \returns the API notes reader for this file, or null if there is
|
||||
/// a failure.
|
||||
std::unique_ptr<APINotesReader> loadAPINotes(StringRef Buffer);
|
||||
|
||||
/// Load the given API notes file for the given header directory.
|
||||
///
|
||||
/// \param HeaderDir The directory at which we
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
bool loadAPINotes(const DirectoryEntry *HeaderDir, FileEntryRef APINotesFile);
|
||||
|
||||
/// Look for API notes in the given directory.
|
||||
///
|
||||
/// This might find either a binary or source API notes.
|
||||
OptionalFileEntryRef findAPINotesFile(DirectoryEntryRef Directory,
|
||||
StringRef FileName,
|
||||
bool WantPublic = true);
|
||||
|
||||
/// Attempt to load API notes for the given framework. A framework will have
|
||||
/// the API notes file under either {FrameworkPath}/APINotes,
|
||||
/// {FrameworkPath}/Headers or {FrameworkPath}/PrivateHeaders, while a
|
||||
/// library will have the API notes simply in its directory.
|
||||
///
|
||||
/// \param FrameworkPath The path to the framework.
|
||||
/// \param Public Whether to load the public API notes. Otherwise, attempt
|
||||
/// to load the private API notes.
|
||||
///
|
||||
/// \returns the header directory entry (e.g., for Headers or PrivateHeaders)
|
||||
/// for which the API notes were successfully loaded, or NULL if API notes
|
||||
/// could not be loaded for any reason.
|
||||
OptionalDirectoryEntryRef loadFrameworkAPINotes(llvm::StringRef FrameworkPath,
|
||||
llvm::StringRef FrameworkName,
|
||||
bool Public);
|
||||
|
||||
public:
|
||||
APINotesManager(SourceManager &SM, const LangOptions &LangOpts);
|
||||
~APINotesManager();
|
||||
|
||||
/// Set the Swift version to use when filtering API notes.
|
||||
void setSwiftVersion(llvm::VersionTuple Version) {
|
||||
this->SwiftVersion = Version;
|
||||
}
|
||||
|
||||
/// Load the API notes for the current module.
|
||||
///
|
||||
/// \param M The current module.
|
||||
/// \param LookInModule Whether to look inside the module itself.
|
||||
/// \param SearchPaths The paths in which we should search for API notes
|
||||
/// for the current module.
|
||||
///
|
||||
/// \returns true if API notes were successfully loaded, \c false otherwise.
|
||||
bool loadCurrentModuleAPINotes(Module *M, bool LookInModule,
|
||||
ArrayRef<std::string> SearchPaths);
|
||||
|
||||
/// Get FileEntry for the APINotes of the module that is currently being
|
||||
/// compiled.
|
||||
///
|
||||
/// \param M The current module.
|
||||
/// \param LookInModule Whether to look inside the directory of the current
|
||||
/// module.
|
||||
/// \param SearchPaths The paths in which we should search for API
|
||||
/// notes for the current module.
|
||||
///
|
||||
/// \returns a vector of FileEntry where APINotes files are.
|
||||
llvm::SmallVector<FileEntryRef, 2>
|
||||
getCurrentModuleAPINotes(Module *M, bool LookInModule,
|
||||
ArrayRef<std::string> SearchPaths);
|
||||
|
||||
/// Load Compiled API notes for current module.
|
||||
///
|
||||
/// \param Buffers Array of compiled API notes.
|
||||
///
|
||||
/// \returns true if API notes were successfully loaded, \c false otherwise.
|
||||
bool loadCurrentModuleAPINotesFromBuffer(ArrayRef<StringRef> Buffers);
|
||||
|
||||
/// Retrieve the set of API notes readers for the current module.
|
||||
ArrayRef<APINotesReader *> getCurrentModuleReaders() const {
|
||||
bool HasPublic = CurrentModuleReaders[ReaderKind::Public];
|
||||
bool HasPrivate = CurrentModuleReaders[ReaderKind::Private];
|
||||
assert(!HasPrivate || HasPublic && "private module requires public module");
|
||||
if (!HasPrivate && !HasPublic)
|
||||
return {};
|
||||
return ArrayRef(CurrentModuleReaders).slice(0, HasPrivate ? 2 : 1);
|
||||
}
|
||||
|
||||
/// Find the API notes readers that correspond to the given source location.
|
||||
llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
|
||||
};
|
||||
|
||||
} // end namespace api_notes
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
@ -737,6 +737,9 @@ inline bool operator!=(const TypedefInfo &LHS, const TypedefInfo &RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
|
||||
/// The file extension used for the source representation of API notes.
|
||||
static const constexpr char SOURCE_APINOTES_EXTENSION[] = "apinotes";
|
||||
|
||||
/// Opaque context ID used to refer to an Objective-C class or protocol or a C++
|
||||
/// namespace.
|
||||
class ContextID {
|
||||
|
@ -390,6 +390,19 @@ def note_mt_message : Note<"[rewriter] %0">;
|
||||
def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">;
|
||||
def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">;
|
||||
|
||||
// API notes
|
||||
def err_apinotes_message : Error<"%0">;
|
||||
def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>;
|
||||
def note_apinotes_message : Note<"%0">;
|
||||
|
||||
class NonportablePrivateAPINotesPath : Warning<
|
||||
"private API notes file for module '%0' should be named "
|
||||
"'%0_private.apinotes', not '%1'">;
|
||||
def warn_apinotes_private_case : NonportablePrivateAPINotesPath,
|
||||
InGroup<DiagGroup<"nonportable-private-apinotes-path">>;
|
||||
def warn_apinotes_private_case_system : NonportablePrivateAPINotesPath,
|
||||
DefaultIgnore, InGroup<DiagGroup<"nonportable-private-system-apinotes-path">>;
|
||||
|
||||
// C++ for OpenCL.
|
||||
def err_openclcxx_not_supported : Error<
|
||||
"'%0' is not supported in C++ for OpenCL">;
|
||||
|
@ -402,6 +402,8 @@ LANGOPT(XLPragmaPack, 1, 0, "IBM XL #pragma pack handling")
|
||||
|
||||
LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
|
||||
|
||||
LANGOPT(APINotes, 1, 0, "use external API notes")
|
||||
|
||||
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
|
||||
"field padding (0: none, 1:least "
|
||||
"aggressive, 2: more aggressive)")
|
||||
|
@ -178,6 +178,9 @@ public:
|
||||
/// eventually be exposed, for use in "private" modules.
|
||||
std::string ExportAsModule;
|
||||
|
||||
/// For the debug info, the path to this module's .apinotes file, if any.
|
||||
std::string APINotesFile;
|
||||
|
||||
/// Does this Module is a named module of a standard named module?
|
||||
bool isNamedModule() const {
|
||||
switch (Kind) {
|
||||
|
85
clang/include/clang/Basic/SourceMgrAdapter.h
Normal file
85
clang/include/clang/Basic/SourceMgrAdapter.h
Normal file
@ -0,0 +1,85 @@
|
||||
//=== SourceMgrAdapter.h - SourceMgr to SourceManager Adapter ---*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides an adapter that maps diagnostics from llvm::SourceMgr
|
||||
// to Clang's SourceManager.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_SOURCEMGRADAPTER_H
|
||||
#define LLVM_CLANG_SOURCEMGRADAPTER_H
|
||||
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class DiagnosticsEngine;
|
||||
class FileEntry;
|
||||
|
||||
/// An adapter that can be used to translate diagnostics from one or more
|
||||
/// llvm::SourceMgr instances to a ,
|
||||
class SourceMgrAdapter {
|
||||
/// Clang source manager.
|
||||
SourceManager &SrcMgr;
|
||||
|
||||
/// Clang diagnostics engine.
|
||||
DiagnosticsEngine &Diagnostics;
|
||||
|
||||
/// Diagnostic IDs for errors, warnings, and notes.
|
||||
unsigned ErrorDiagID, WarningDiagID, NoteDiagID;
|
||||
|
||||
/// The default file to use when mapping buffers.
|
||||
OptionalFileEntryRef DefaultFile;
|
||||
|
||||
/// A mapping from (LLVM source manager, buffer ID) pairs to the
|
||||
/// corresponding file ID within the Clang source manager.
|
||||
llvm::DenseMap<std::pair<const llvm::SourceMgr *, unsigned>, FileID>
|
||||
FileIDMapping;
|
||||
|
||||
/// Diagnostic handler.
|
||||
static void handleDiag(const llvm::SMDiagnostic &Diag, void *Context);
|
||||
|
||||
public:
|
||||
/// Create a new \c SourceMgr adaptor that maps to the given source
|
||||
/// manager and diagnostics engine.
|
||||
SourceMgrAdapter(SourceManager &SM, DiagnosticsEngine &Diagnostics,
|
||||
unsigned ErrorDiagID, unsigned WarningDiagID,
|
||||
unsigned NoteDiagID,
|
||||
OptionalFileEntryRef DefaultFile = std::nullopt);
|
||||
|
||||
~SourceMgrAdapter();
|
||||
|
||||
/// Map a source location in the given LLVM source manager to its
|
||||
/// corresponding location in the Clang source manager.
|
||||
SourceLocation mapLocation(const llvm::SourceMgr &LLVMSrcMgr,
|
||||
llvm::SMLoc Loc);
|
||||
|
||||
/// Map a source range in the given LLVM source manager to its corresponding
|
||||
/// range in the Clang source manager.
|
||||
SourceRange mapRange(const llvm::SourceMgr &LLVMSrcMgr, llvm::SMRange Range);
|
||||
|
||||
/// Handle the given diagnostic from an LLVM source manager.
|
||||
void handleDiag(const llvm::SMDiagnostic &Diag);
|
||||
|
||||
/// Retrieve the diagnostic handler to use with the underlying SourceMgr.
|
||||
llvm::SourceMgr::DiagHandlerTy getDiagHandler() {
|
||||
return &SourceMgrAdapter::handleDiag;
|
||||
}
|
||||
|
||||
/// Retrieve the context to use with the diagnostic handler produced by
|
||||
/// \c getDiagHandler().
|
||||
void *getDiagContext() { return this; }
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
458
clang/lib/APINotes/APINotesManager.cpp
Normal file
458
clang/lib/APINotes/APINotesManager.cpp
Normal file
@ -0,0 +1,458 @@
|
||||
//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/APINotes/APINotesManager.h"
|
||||
#include "clang/APINotes/APINotesReader.h"
|
||||
#include "clang/APINotes/APINotesYAMLCompiler.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/SourceMgrAdapter.h"
|
||||
#include "clang/Basic/Version.h"
|
||||
#include "llvm/ADT/APInt.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/PrettyStackTrace.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace api_notes;
|
||||
|
||||
#define DEBUG_TYPE "API Notes"
|
||||
STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded");
|
||||
STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded");
|
||||
STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded");
|
||||
STATISTIC(NumFrameworksSearched, "frameworks searched");
|
||||
STATISTIC(NumDirectoriesSearched, "header directories searched");
|
||||
STATISTIC(NumDirectoryCacheHits, "directory cache hits");
|
||||
|
||||
namespace {
|
||||
/// Prints two successive strings, which much be kept alive as long as the
|
||||
/// PrettyStackTrace entry.
|
||||
class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
|
||||
StringRef First, Second;
|
||||
|
||||
public:
|
||||
PrettyStackTraceDoubleString(StringRef First, StringRef Second)
|
||||
: First(First), Second(Second) {}
|
||||
void print(raw_ostream &OS) const override { OS << First << Second; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts)
|
||||
: SM(SM), ImplicitAPINotes(LangOpts.APINotes) {}
|
||||
|
||||
APINotesManager::~APINotesManager() {
|
||||
// Free the API notes readers.
|
||||
for (const auto &Entry : Readers) {
|
||||
if (auto Reader = Entry.second.dyn_cast<APINotesReader *>())
|
||||
delete Reader;
|
||||
}
|
||||
|
||||
delete CurrentModuleReaders[ReaderKind::Public];
|
||||
delete CurrentModuleReaders[ReaderKind::Private];
|
||||
}
|
||||
|
||||
std::unique_ptr<APINotesReader>
|
||||
APINotesManager::loadAPINotes(FileEntryRef APINotesFile) {
|
||||
PrettyStackTraceDoubleString Trace("Loading API notes from ",
|
||||
APINotesFile.getName());
|
||||
|
||||
// Open the source file.
|
||||
auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User);
|
||||
auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation());
|
||||
if (!SourceBuffer)
|
||||
return nullptr;
|
||||
|
||||
// Compile the API notes source into a buffer.
|
||||
// FIXME: Either propagate OSType through or, better yet, improve the binary
|
||||
// APINotes format to maintain complete availability information.
|
||||
// FIXME: We don't even really need to go through the binary format at all;
|
||||
// we're just going to immediately deserialize it again.
|
||||
llvm::SmallVector<char, 1024> APINotesBuffer;
|
||||
std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer;
|
||||
{
|
||||
SourceMgrAdapter SMAdapter(
|
||||
SM, SM.getDiagnostics(), diag::err_apinotes_message,
|
||||
diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile);
|
||||
llvm::raw_svector_ostream OS(APINotesBuffer);
|
||||
if (api_notes::compileAPINotes(
|
||||
SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS,
|
||||
SMAdapter.getDiagHandler(), SMAdapter.getDiagContext()))
|
||||
return nullptr;
|
||||
|
||||
// Make a copy of the compiled form into the buffer.
|
||||
CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
|
||||
StringRef(APINotesBuffer.data(), APINotesBuffer.size()));
|
||||
}
|
||||
|
||||
// Load the binary form we just compiled.
|
||||
auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion);
|
||||
assert(Reader && "Could not load the API notes we just generated?");
|
||||
return Reader;
|
||||
}
|
||||
|
||||
std::unique_ptr<APINotesReader>
|
||||
APINotesManager::loadAPINotes(StringRef Buffer) {
|
||||
llvm::SmallVector<char, 1024> APINotesBuffer;
|
||||
std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer;
|
||||
SourceMgrAdapter SMAdapter(
|
||||
SM, SM.getDiagnostics(), diag::err_apinotes_message,
|
||||
diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt);
|
||||
llvm::raw_svector_ostream OS(APINotesBuffer);
|
||||
|
||||
if (api_notes::compileAPINotes(Buffer, nullptr, OS,
|
||||
SMAdapter.getDiagHandler(),
|
||||
SMAdapter.getDiagContext()))
|
||||
return nullptr;
|
||||
|
||||
CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
|
||||
StringRef(APINotesBuffer.data(), APINotesBuffer.size()));
|
||||
auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion);
|
||||
assert(Reader && "Could not load the API notes we just generated?");
|
||||
return Reader;
|
||||
}
|
||||
|
||||
bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
|
||||
FileEntryRef APINotesFile) {
|
||||
assert(Readers.find(HeaderDir) == Readers.end());
|
||||
if (auto Reader = loadAPINotes(APINotesFile)) {
|
||||
Readers[HeaderDir] = Reader.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
Readers[HeaderDir] = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
OptionalFileEntryRef
|
||||
APINotesManager::findAPINotesFile(DirectoryEntryRef Directory,
|
||||
StringRef Basename, bool WantPublic) {
|
||||
FileManager &FM = SM.getFileManager();
|
||||
|
||||
llvm::SmallString<128> Path(Directory.getName());
|
||||
|
||||
StringRef Suffix = WantPublic ? "" : "_private";
|
||||
|
||||
// Look for the source API notes file.
|
||||
llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." +
|
||||
SOURCE_APINOTES_EXTENSION);
|
||||
return FM.getOptionalFileRef(Path, /*Open*/ true);
|
||||
}
|
||||
|
||||
OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes(
|
||||
llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) {
|
||||
FileManager &FM = SM.getFileManager();
|
||||
|
||||
llvm::SmallString<128> Path(FrameworkPath);
|
||||
unsigned FrameworkNameLength = Path.size();
|
||||
|
||||
StringRef Suffix = Public ? "" : "_private";
|
||||
|
||||
// Form the path to the APINotes file.
|
||||
llvm::sys::path::append(Path, "APINotes");
|
||||
llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." +
|
||||
SOURCE_APINOTES_EXTENSION));
|
||||
|
||||
// Try to open the APINotes file.
|
||||
auto APINotesFile = FM.getOptionalFileRef(Path);
|
||||
if (!APINotesFile)
|
||||
return std::nullopt;
|
||||
|
||||
// Form the path to the corresponding header directory.
|
||||
Path.resize(FrameworkNameLength);
|
||||
llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders");
|
||||
|
||||
// Try to access the header directory.
|
||||
auto HeaderDir = FM.getOptionalDirectoryRef(Path);
|
||||
if (!HeaderDir)
|
||||
return std::nullopt;
|
||||
|
||||
// Try to load the API notes.
|
||||
if (loadAPINotes(*HeaderDir, *APINotesFile))
|
||||
return std::nullopt;
|
||||
|
||||
// Success: return the header directory.
|
||||
if (Public)
|
||||
++NumPublicFrameworkAPINotes;
|
||||
else
|
||||
++NumPrivateFrameworkAPINotes;
|
||||
return *HeaderDir;
|
||||
}
|
||||
|
||||
static void checkPrivateAPINotesName(DiagnosticsEngine &Diags,
|
||||
const FileEntry *File, const Module *M) {
|
||||
if (File->tryGetRealPathName().empty())
|
||||
return;
|
||||
|
||||
StringRef RealFileName =
|
||||
llvm::sys::path::filename(File->tryGetRealPathName());
|
||||
StringRef RealStem = llvm::sys::path::stem(RealFileName);
|
||||
if (RealStem.endswith("_private"))
|
||||
return;
|
||||
|
||||
unsigned DiagID = diag::warn_apinotes_private_case;
|
||||
if (M->IsSystem)
|
||||
DiagID = diag::warn_apinotes_private_case_system;
|
||||
|
||||
Diags.Report(SourceLocation(), DiagID) << M->Name << RealFileName;
|
||||
}
|
||||
|
||||
/// \returns true if any of \p module's immediate submodules are defined in a
|
||||
/// private module map
|
||||
static bool hasPrivateSubmodules(const Module *M) {
|
||||
return llvm::any_of(M->submodules(), [](const Module *Submodule) {
|
||||
return Submodule->ModuleMapIsPrivate;
|
||||
});
|
||||
}
|
||||
|
||||
llvm::SmallVector<FileEntryRef, 2>
|
||||
APINotesManager::getCurrentModuleAPINotes(Module *M, bool LookInModule,
|
||||
ArrayRef<std::string> SearchPaths) {
|
||||
FileManager &FM = SM.getFileManager();
|
||||
auto ModuleName = M->getTopLevelModuleName();
|
||||
llvm::SmallVector<FileEntryRef, 2> APINotes;
|
||||
|
||||
// First, look relative to the module itself.
|
||||
if (LookInModule) {
|
||||
// Local function to try loading an API notes file in the given directory.
|
||||
auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) {
|
||||
if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) {
|
||||
if (!WantPublic)
|
||||
checkPrivateAPINotesName(SM.getDiagnostics(), *File, M);
|
||||
|
||||
APINotes.push_back(*File);
|
||||
}
|
||||
};
|
||||
|
||||
if (M->IsFramework) {
|
||||
// For frameworks, we search in the "Headers" or "PrivateHeaders"
|
||||
// subdirectory.
|
||||
//
|
||||
// Public modules:
|
||||
// - Headers/Foo.apinotes
|
||||
// - PrivateHeaders/Foo_private.apinotes (if there are private submodules)
|
||||
// Private modules:
|
||||
// - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has
|
||||
// the word "Private" in it in practice)
|
||||
llvm::SmallString<128> Path(M->Directory->getName());
|
||||
|
||||
if (!M->ModuleMapIsPrivate) {
|
||||
unsigned PathLen = Path.size();
|
||||
|
||||
llvm::sys::path::append(Path, "Headers");
|
||||
if (auto APINotesDir = FM.getOptionalDirectoryRef(Path))
|
||||
tryAPINotes(*APINotesDir, /*wantPublic=*/true);
|
||||
|
||||
Path.resize(PathLen);
|
||||
}
|
||||
|
||||
if (M->ModuleMapIsPrivate || hasPrivateSubmodules(M)) {
|
||||
llvm::sys::path::append(Path, "PrivateHeaders");
|
||||
if (auto PrivateAPINotesDir = FM.getOptionalDirectoryRef(Path))
|
||||
tryAPINotes(*PrivateAPINotesDir,
|
||||
/*wantPublic=*/M->ModuleMapIsPrivate);
|
||||
}
|
||||
} else {
|
||||
// Public modules:
|
||||
// - Foo.apinotes
|
||||
// - Foo_private.apinotes (if there are private submodules)
|
||||
// Private modules:
|
||||
// - Bar.apinotes (except that 'Bar' probably already has the word
|
||||
// "Private" in it in practice)
|
||||
tryAPINotes(*M->Directory, /*wantPublic=*/true);
|
||||
if (!M->ModuleMapIsPrivate && hasPrivateSubmodules(M))
|
||||
tryAPINotes(*M->Directory, /*wantPublic=*/false);
|
||||
}
|
||||
|
||||
if (!APINotes.empty())
|
||||
return APINotes;
|
||||
}
|
||||
|
||||
// Second, look for API notes for this module in the module API
|
||||
// notes search paths.
|
||||
for (const auto &SearchPath : SearchPaths) {
|
||||
if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) {
|
||||
if (auto File = findAPINotesFile(*SearchDir, ModuleName)) {
|
||||
APINotes.push_back(*File);
|
||||
return APINotes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find any API notes.
|
||||
return APINotes;
|
||||
}
|
||||
|
||||
bool APINotesManager::loadCurrentModuleAPINotes(
|
||||
Module *M, bool LookInModule, ArrayRef<std::string> SearchPaths) {
|
||||
assert(!CurrentModuleReaders[ReaderKind::Public] &&
|
||||
"Already loaded API notes for the current module?");
|
||||
|
||||
auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths);
|
||||
unsigned NumReaders = 0;
|
||||
for (auto File : APINotes) {
|
||||
CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release();
|
||||
if (!getCurrentModuleReaders().empty())
|
||||
M->APINotesFile = File.getName().str();
|
||||
}
|
||||
|
||||
return NumReaders > 0;
|
||||
}
|
||||
|
||||
bool APINotesManager::loadCurrentModuleAPINotesFromBuffer(
|
||||
ArrayRef<StringRef> Buffers) {
|
||||
unsigned NumReader = 0;
|
||||
for (auto Buf : Buffers) {
|
||||
auto Reader = loadAPINotes(Buf);
|
||||
assert(Reader && "Could not load the API notes we just generated?");
|
||||
|
||||
CurrentModuleReaders[NumReader++] = Reader.release();
|
||||
}
|
||||
return NumReader;
|
||||
}
|
||||
|
||||
llvm::SmallVector<APINotesReader *, 2>
|
||||
APINotesManager::findAPINotes(SourceLocation Loc) {
|
||||
llvm::SmallVector<APINotesReader *, 2> Results;
|
||||
|
||||
// If there are readers for the current module, return them.
|
||||
if (!getCurrentModuleReaders().empty()) {
|
||||
Results.append(getCurrentModuleReaders().begin(),
|
||||
getCurrentModuleReaders().end());
|
||||
return Results;
|
||||
}
|
||||
|
||||
// If we're not allowed to implicitly load API notes files, we're done.
|
||||
if (!ImplicitAPINotes)
|
||||
return Results;
|
||||
|
||||
// If we don't have source location information, we're done.
|
||||
if (Loc.isInvalid())
|
||||
return Results;
|
||||
|
||||
// API notes are associated with the expansion location. Retrieve the
|
||||
// file for this location.
|
||||
SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
|
||||
FileID ID = SM.getFileID(ExpansionLoc);
|
||||
if (ID.isInvalid())
|
||||
return Results;
|
||||
OptionalFileEntryRef File = SM.getFileEntryRefForID(ID);
|
||||
if (!File)
|
||||
return Results;
|
||||
|
||||
// Look for API notes in the directory corresponding to this file, or one of
|
||||
// its its parent directories.
|
||||
OptionalDirectoryEntryRef Dir = File->getDir();
|
||||
FileManager &FileMgr = SM.getFileManager();
|
||||
llvm::SetVector<const DirectoryEntry *,
|
||||
SmallVector<const DirectoryEntry *, 4>,
|
||||
llvm::SmallPtrSet<const DirectoryEntry *, 4>>
|
||||
DirsVisited;
|
||||
do {
|
||||
// Look for an API notes reader for this header search directory.
|
||||
auto Known = Readers.find(*Dir);
|
||||
|
||||
// If we already know the answer, chase it.
|
||||
if (Known != Readers.end()) {
|
||||
++NumDirectoryCacheHits;
|
||||
|
||||
// We've been redirected to another directory for answers. Follow it.
|
||||
if (Known->second && Known->second.is<DirectoryEntryRef>()) {
|
||||
DirsVisited.insert(*Dir);
|
||||
Dir = Known->second.get<DirectoryEntryRef>();
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have the answer.
|
||||
if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
|
||||
Results.push_back(Reader);
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for API notes corresponding to this directory.
|
||||
StringRef Path = Dir->getName();
|
||||
if (llvm::sys::path::extension(Path) == ".framework") {
|
||||
// If this is a framework directory, check whether there are API notes
|
||||
// in the APINotes subdirectory.
|
||||
auto FrameworkName = llvm::sys::path::stem(Path);
|
||||
++NumFrameworksSearched;
|
||||
|
||||
// Look for API notes for both the public and private headers.
|
||||
OptionalDirectoryEntryRef PublicDir =
|
||||
loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
|
||||
OptionalDirectoryEntryRef PrivateDir =
|
||||
loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);
|
||||
|
||||
if (PublicDir || PrivateDir) {
|
||||
// We found API notes: don't ever look past the framework directory.
|
||||
Readers[*Dir] = nullptr;
|
||||
|
||||
// Pretend we found the result in the public or private directory,
|
||||
// as appropriate. All headers should be in one of those two places,
|
||||
// but be defensive here.
|
||||
if (!DirsVisited.empty()) {
|
||||
if (PublicDir && DirsVisited.back() == *PublicDir) {
|
||||
DirsVisited.pop_back();
|
||||
Dir = *PublicDir;
|
||||
} else if (PrivateDir && DirsVisited.back() == *PrivateDir) {
|
||||
DirsVisited.pop_back();
|
||||
Dir = *PrivateDir;
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the result.
|
||||
if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())
|
||||
Results.push_back(Reader);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Look for an APINotes file in this directory.
|
||||
llvm::SmallString<128> APINotesPath(Dir->getName());
|
||||
llvm::sys::path::append(
|
||||
APINotesPath, (llvm::Twine("APINotes.") + SOURCE_APINOTES_EXTENSION));
|
||||
|
||||
// If there is an API notes file here, try to load it.
|
||||
++NumDirectoriesSearched;
|
||||
if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) {
|
||||
if (!loadAPINotes(*Dir, *APINotesFile)) {
|
||||
++NumHeaderAPINotes;
|
||||
if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())
|
||||
Results.push_back(Reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find anything. Look at the parent directory.
|
||||
if (!DirsVisited.insert(*Dir)) {
|
||||
Dir = std::nullopt;
|
||||
break;
|
||||
}
|
||||
|
||||
StringRef ParentPath = llvm::sys::path::parent_path(Path);
|
||||
while (llvm::sys::path::stem(ParentPath) == "..")
|
||||
ParentPath = llvm::sys::path::parent_path(ParentPath);
|
||||
|
||||
Dir = ParentPath.empty() ? std::nullopt
|
||||
: FileMgr.getOptionalDirectoryRef(ParentPath);
|
||||
} while (Dir);
|
||||
|
||||
// Path compression for all of the directories we visited, redirecting
|
||||
// them to the directory we ended on. If no API notes were found, the
|
||||
// resulting directory will be NULL, indicating no API notes.
|
||||
for (const auto Visited : DirsVisited)
|
||||
Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry();
|
||||
|
||||
return Results;
|
||||
}
|
@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
BitstreamReader
|
||||
Support)
|
||||
add_clang_library(clangAPINotes
|
||||
APINotesManager.cpp
|
||||
APINotesReader.cpp
|
||||
APINotesTypes.cpp
|
||||
APINotesWriter.cpp
|
||||
|
@ -85,6 +85,7 @@ add_clang_library(clangBasic
|
||||
Sarif.cpp
|
||||
SourceLocation.cpp
|
||||
SourceManager.cpp
|
||||
SourceMgrAdapter.cpp
|
||||
Stack.cpp
|
||||
TargetID.cpp
|
||||
TargetInfo.cpp
|
||||
|
136
clang/lib/Basic/SourceMgrAdapter.cpp
Normal file
136
clang/lib/Basic/SourceMgrAdapter.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
//=== SourceMgrAdapter.cpp - SourceMgr to SourceManager Adapter -----------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the adapter that maps diagnostics from llvm::SourceMgr
|
||||
// to Clang's SourceManager.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Basic/SourceMgrAdapter.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &Diag,
|
||||
void *Context) {
|
||||
static_cast<SourceMgrAdapter *>(Context)->handleDiag(Diag);
|
||||
}
|
||||
|
||||
SourceMgrAdapter::SourceMgrAdapter(SourceManager &SM,
|
||||
DiagnosticsEngine &Diagnostics,
|
||||
unsigned ErrorDiagID, unsigned WarningDiagID,
|
||||
unsigned NoteDiagID,
|
||||
OptionalFileEntryRef DefaultFile)
|
||||
: SrcMgr(SM), Diagnostics(Diagnostics), ErrorDiagID(ErrorDiagID),
|
||||
WarningDiagID(WarningDiagID), NoteDiagID(NoteDiagID),
|
||||
DefaultFile(DefaultFile) {}
|
||||
|
||||
SourceMgrAdapter::~SourceMgrAdapter() {}
|
||||
|
||||
SourceLocation SourceMgrAdapter::mapLocation(const llvm::SourceMgr &LLVMSrcMgr,
|
||||
llvm::SMLoc Loc) {
|
||||
// Map invalid locations.
|
||||
if (!Loc.isValid())
|
||||
return SourceLocation();
|
||||
|
||||
// Find the buffer containing the location.
|
||||
unsigned BufferID = LLVMSrcMgr.FindBufferContainingLoc(Loc);
|
||||
if (!BufferID)
|
||||
return SourceLocation();
|
||||
|
||||
// If we haven't seen this buffer before, copy it over.
|
||||
auto Buffer = LLVMSrcMgr.getMemoryBuffer(BufferID);
|
||||
auto KnownBuffer = FileIDMapping.find(std::make_pair(&LLVMSrcMgr, BufferID));
|
||||
if (KnownBuffer == FileIDMapping.end()) {
|
||||
FileID FileID;
|
||||
if (DefaultFile) {
|
||||
// Map to the default file.
|
||||
FileID = SrcMgr.getOrCreateFileID(*DefaultFile, SrcMgr::C_User);
|
||||
|
||||
// Only do this once.
|
||||
DefaultFile = std::nullopt;
|
||||
} else {
|
||||
// Make a copy of the memory buffer.
|
||||
StringRef bufferName = Buffer->getBufferIdentifier();
|
||||
auto bufferCopy = std::unique_ptr<llvm::MemoryBuffer>(
|
||||
llvm::MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
|
||||
bufferName));
|
||||
|
||||
// Add this memory buffer to the Clang source manager.
|
||||
FileID = SrcMgr.createFileID(std::move(bufferCopy));
|
||||
}
|
||||
|
||||
// Save the mapping.
|
||||
KnownBuffer = FileIDMapping
|
||||
.insert(std::make_pair(
|
||||
std::make_pair(&LLVMSrcMgr, BufferID), FileID))
|
||||
.first;
|
||||
}
|
||||
|
||||
// Translate the offset into the file.
|
||||
unsigned Offset = Loc.getPointer() - Buffer->getBufferStart();
|
||||
return SrcMgr.getLocForStartOfFile(KnownBuffer->second)
|
||||
.getLocWithOffset(Offset);
|
||||
}
|
||||
|
||||
SourceRange SourceMgrAdapter::mapRange(const llvm::SourceMgr &LLVMSrcMgr,
|
||||
llvm::SMRange Range) {
|
||||
if (!Range.isValid())
|
||||
return SourceRange();
|
||||
|
||||
SourceLocation Start = mapLocation(LLVMSrcMgr, Range.Start);
|
||||
SourceLocation End = mapLocation(LLVMSrcMgr, Range.End);
|
||||
return SourceRange(Start, End);
|
||||
}
|
||||
|
||||
void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &Diag) {
|
||||
// Map the location.
|
||||
SourceLocation Loc;
|
||||
if (auto *LLVMSrcMgr = Diag.getSourceMgr())
|
||||
Loc = mapLocation(*LLVMSrcMgr, Diag.getLoc());
|
||||
|
||||
// Extract the message.
|
||||
StringRef Message = Diag.getMessage();
|
||||
|
||||
// Map the diagnostic kind.
|
||||
unsigned DiagID;
|
||||
switch (Diag.getKind()) {
|
||||
case llvm::SourceMgr::DK_Error:
|
||||
DiagID = ErrorDiagID;
|
||||
break;
|
||||
|
||||
case llvm::SourceMgr::DK_Warning:
|
||||
DiagID = WarningDiagID;
|
||||
break;
|
||||
|
||||
case llvm::SourceMgr::DK_Remark:
|
||||
llvm_unreachable("remarks not implemented");
|
||||
|
||||
case llvm::SourceMgr::DK_Note:
|
||||
DiagID = NoteDiagID;
|
||||
break;
|
||||
}
|
||||
|
||||
// Report the diagnostic.
|
||||
DiagnosticBuilder Builder = Diagnostics.Report(Loc, DiagID) << Message;
|
||||
|
||||
if (auto *LLVMSrcMgr = Diag.getSourceMgr()) {
|
||||
// Translate ranges.
|
||||
SourceLocation StartOfLine = Loc.getLocWithOffset(-Diag.getColumnNo());
|
||||
for (auto Range : Diag.getRanges()) {
|
||||
Builder << SourceRange(StartOfLine.getLocWithOffset(Range.first),
|
||||
StartOfLine.getLocWithOffset(Range.second));
|
||||
}
|
||||
|
||||
// Translate Fix-Its.
|
||||
for (const llvm::SMFixIt &FixIt : Diag.getFixIts()) {
|
||||
CharSourceRange Range(mapRange(*LLVMSrcMgr, FixIt.getRange()), false);
|
||||
Builder << FixItHint::CreateReplacement(Range, FixIt.getText());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user