Skip to content

Commit f049395

Browse files
authored
[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
1 parent ec42d54 commit f049395

File tree

10 files changed

+877
-0
lines changed

10 files changed

+877
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
//===--- APINotesManager.h - Manage API Notes Files -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H
10+
#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H
11+
12+
#include "clang/Basic/Module.h"
13+
#include "clang/Basic/SourceLocation.h"
14+
#include "llvm/ADT/ArrayRef.h"
15+
#include "llvm/ADT/DenseMap.h"
16+
#include "llvm/ADT/PointerUnion.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Support/VersionTuple.h"
19+
#include <memory>
20+
#include <string>
21+
22+
namespace clang {
23+
24+
class DirectoryEntry;
25+
class FileEntry;
26+
class LangOptions;
27+
class SourceManager;
28+
29+
namespace api_notes {
30+
31+
class APINotesReader;
32+
33+
/// The API notes manager helps find API notes associated with declarations.
34+
///
35+
/// API notes are externally-provided annotations for declarations that can
36+
/// introduce new attributes (covering availability, nullability of
37+
/// parameters/results, and so on) for specific declarations without directly
38+
/// modifying the headers that contain those declarations.
39+
///
40+
/// The API notes manager is responsible for finding and loading the
41+
/// external API notes files that correspond to a given header. Its primary
42+
/// operation is \c findAPINotes(), which finds the API notes reader that
43+
/// provides information about the declarations at that location.
44+
class APINotesManager {
45+
using ReaderEntry = llvm::PointerUnion<DirectoryEntryRef, APINotesReader *>;
46+
47+
SourceManager &SM;
48+
49+
/// Whether to implicitly search for API notes files based on the
50+
/// source file from which an entity was declared.
51+
bool ImplicitAPINotes;
52+
53+
/// The Swift version to use when interpreting versioned API notes.
54+
llvm::VersionTuple SwiftVersion;
55+
56+
enum ReaderKind : unsigned { Public = 0, Private = 1 };
57+
58+
/// API notes readers for the current module.
59+
///
60+
/// There can be up to two of these, one for public headers and one
61+
/// for private headers.
62+
///
63+
/// Not using std::unique_ptr to store these, since the reader pointers are
64+
/// also stored in llvm::PointerUnion below.
65+
APINotesReader *CurrentModuleReaders[2] = {nullptr, nullptr};
66+
67+
/// A mapping from header file directories to the API notes reader for
68+
/// that directory, or a redirection to another directory entry that may
69+
/// have more information, or NULL to indicate that there is no API notes
70+
/// reader for this directory.
71+
llvm::DenseMap<const DirectoryEntry *, ReaderEntry> Readers;
72+
73+
/// Load the API notes associated with the given file, whether it is
74+
/// the binary or source form of API notes.
75+
///
76+
/// \returns the API notes reader for this file, or null if there is
77+
/// a failure.
78+
std::unique_ptr<APINotesReader> loadAPINotes(FileEntryRef APINotesFile);
79+
80+
/// Load the API notes associated with the given buffer, whether it is
81+
/// the binary or source form of API notes.
82+
///
83+
/// \returns the API notes reader for this file, or null if there is
84+
/// a failure.
85+
std::unique_ptr<APINotesReader> loadAPINotes(StringRef Buffer);
86+
87+
/// Load the given API notes file for the given header directory.
88+
///
89+
/// \param HeaderDir The directory at which we
90+
///
91+
/// \returns true if an error occurred.
92+
bool loadAPINotes(const DirectoryEntry *HeaderDir, FileEntryRef APINotesFile);
93+
94+
/// Look for API notes in the given directory.
95+
///
96+
/// This might find either a binary or source API notes.
97+
OptionalFileEntryRef findAPINotesFile(DirectoryEntryRef Directory,
98+
StringRef FileName,
99+
bool WantPublic = true);
100+
101+
/// Attempt to load API notes for the given framework. A framework will have
102+
/// the API notes file under either {FrameworkPath}/APINotes,
103+
/// {FrameworkPath}/Headers or {FrameworkPath}/PrivateHeaders, while a
104+
/// library will have the API notes simply in its directory.
105+
///
106+
/// \param FrameworkPath The path to the framework.
107+
/// \param Public Whether to load the public API notes. Otherwise, attempt
108+
/// to load the private API notes.
109+
///
110+
/// \returns the header directory entry (e.g., for Headers or PrivateHeaders)
111+
/// for which the API notes were successfully loaded, or NULL if API notes
112+
/// could not be loaded for any reason.
113+
OptionalDirectoryEntryRef loadFrameworkAPINotes(llvm::StringRef FrameworkPath,
114+
llvm::StringRef FrameworkName,
115+
bool Public);
116+
117+
public:
118+
APINotesManager(SourceManager &SM, const LangOptions &LangOpts);
119+
~APINotesManager();
120+
121+
/// Set the Swift version to use when filtering API notes.
122+
void setSwiftVersion(llvm::VersionTuple Version) {
123+
this->SwiftVersion = Version;
124+
}
125+
126+
/// Load the API notes for the current module.
127+
///
128+
/// \param M The current module.
129+
/// \param LookInModule Whether to look inside the module itself.
130+
/// \param SearchPaths The paths in which we should search for API notes
131+
/// for the current module.
132+
///
133+
/// \returns true if API notes were successfully loaded, \c false otherwise.
134+
bool loadCurrentModuleAPINotes(Module *M, bool LookInModule,
135+
ArrayRef<std::string> SearchPaths);
136+
137+
/// Get FileEntry for the APINotes of the module that is currently being
138+
/// compiled.
139+
///
140+
/// \param M The current module.
141+
/// \param LookInModule Whether to look inside the directory of the current
142+
/// module.
143+
/// \param SearchPaths The paths in which we should search for API
144+
/// notes for the current module.
145+
///
146+
/// \returns a vector of FileEntry where APINotes files are.
147+
llvm::SmallVector<FileEntryRef, 2>
148+
getCurrentModuleAPINotes(Module *M, bool LookInModule,
149+
ArrayRef<std::string> SearchPaths);
150+
151+
/// Load Compiled API notes for current module.
152+
///
153+
/// \param Buffers Array of compiled API notes.
154+
///
155+
/// \returns true if API notes were successfully loaded, \c false otherwise.
156+
bool loadCurrentModuleAPINotesFromBuffer(ArrayRef<StringRef> Buffers);
157+
158+
/// Retrieve the set of API notes readers for the current module.
159+
ArrayRef<APINotesReader *> getCurrentModuleReaders() const {
160+
bool HasPublic = CurrentModuleReaders[ReaderKind::Public];
161+
bool HasPrivate = CurrentModuleReaders[ReaderKind::Private];
162+
assert(!HasPrivate || HasPublic && "private module requires public module");
163+
if (!HasPrivate && !HasPublic)
164+
return {};
165+
return ArrayRef(CurrentModuleReaders).slice(0, HasPrivate ? 2 : 1);
166+
}
167+
168+
/// Find the API notes readers that correspond to the given source location.
169+
llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
170+
};
171+
172+
} // end namespace api_notes
173+
} // end namespace clang
174+
175+
#endif

clang/include/clang/APINotes/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,9 @@ inline bool operator!=(const TypedefInfo &LHS, const TypedefInfo &RHS) {
737737
return !(LHS == RHS);
738738
}
739739

740+
/// The file extension used for the source representation of API notes.
741+
static const constexpr char SOURCE_APINOTES_EXTENSION[] = "apinotes";
742+
740743
/// Opaque context ID used to refer to an Objective-C class or protocol or a C++
741744
/// namespace.
742745
class ContextID {

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,19 @@ def note_mt_message : Note<"[rewriter] %0">;
390390
def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">;
391391
def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">;
392392

393+
// API notes
394+
def err_apinotes_message : Error<"%0">;
395+
def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>;
396+
def note_apinotes_message : Note<"%0">;
397+
398+
class NonportablePrivateAPINotesPath : Warning<
399+
"private API notes file for module '%0' should be named "
400+
"'%0_private.apinotes', not '%1'">;
401+
def warn_apinotes_private_case : NonportablePrivateAPINotesPath,
402+
InGroup<DiagGroup<"nonportable-private-apinotes-path">>;
403+
def warn_apinotes_private_case_system : NonportablePrivateAPINotesPath,
404+
DefaultIgnore, InGroup<DiagGroup<"nonportable-private-system-apinotes-path">>;
405+
393406
// C++ for OpenCL.
394407
def err_openclcxx_not_supported : Error<
395408
"'%0' is not supported in C++ for OpenCL">;

clang/include/clang/Basic/LangOptions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ LANGOPT(XLPragmaPack, 1, 0, "IBM XL #pragma pack handling")
402402

403403
LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
404404

405+
LANGOPT(APINotes, 1, 0, "use external API notes")
406+
405407
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
406408
"field padding (0: none, 1:least "
407409
"aggressive, 2: more aggressive)")

clang/include/clang/Basic/Module.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ class alignas(8) Module {
178178
/// eventually be exposed, for use in "private" modules.
179179
std::string ExportAsModule;
180180

181+
/// For the debug info, the path to this module's .apinotes file, if any.
182+
std::string APINotesFile;
183+
181184
/// Does this Module is a named module of a standard named module?
182185
bool isNamedModule() const {
183186
switch (Kind) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//=== SourceMgrAdapter.h - SourceMgr to SourceManager Adapter ---*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file provides an adapter that maps diagnostics from llvm::SourceMgr
10+
// to Clang's SourceManager.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_CLANG_SOURCEMGRADAPTER_H
15+
#define LLVM_CLANG_SOURCEMGRADAPTER_H
16+
17+
#include "clang/Basic/SourceManager.h"
18+
#include "llvm/ADT/DenseMap.h"
19+
#include "llvm/Support/SourceMgr.h"
20+
#include <string>
21+
#include <utility>
22+
23+
namespace clang {
24+
25+
class DiagnosticsEngine;
26+
class FileEntry;
27+
28+
/// An adapter that can be used to translate diagnostics from one or more
29+
/// llvm::SourceMgr instances to a ,
30+
class SourceMgrAdapter {
31+
/// Clang source manager.
32+
SourceManager &SrcMgr;
33+
34+
/// Clang diagnostics engine.
35+
DiagnosticsEngine &Diagnostics;
36+
37+
/// Diagnostic IDs for errors, warnings, and notes.
38+
unsigned ErrorDiagID, WarningDiagID, NoteDiagID;
39+
40+
/// The default file to use when mapping buffers.
41+
OptionalFileEntryRef DefaultFile;
42+
43+
/// A mapping from (LLVM source manager, buffer ID) pairs to the
44+
/// corresponding file ID within the Clang source manager.
45+
llvm::DenseMap<std::pair<const llvm::SourceMgr *, unsigned>, FileID>
46+
FileIDMapping;
47+
48+
/// Diagnostic handler.
49+
static void handleDiag(const llvm::SMDiagnostic &Diag, void *Context);
50+
51+
public:
52+
/// Create a new \c SourceMgr adaptor that maps to the given source
53+
/// manager and diagnostics engine.
54+
SourceMgrAdapter(SourceManager &SM, DiagnosticsEngine &Diagnostics,
55+
unsigned ErrorDiagID, unsigned WarningDiagID,
56+
unsigned NoteDiagID,
57+
OptionalFileEntryRef DefaultFile = std::nullopt);
58+
59+
~SourceMgrAdapter();
60+
61+
/// Map a source location in the given LLVM source manager to its
62+
/// corresponding location in the Clang source manager.
63+
SourceLocation mapLocation(const llvm::SourceMgr &LLVMSrcMgr,
64+
llvm::SMLoc Loc);
65+
66+
/// Map a source range in the given LLVM source manager to its corresponding
67+
/// range in the Clang source manager.
68+
SourceRange mapRange(const llvm::SourceMgr &LLVMSrcMgr, llvm::SMRange Range);
69+
70+
/// Handle the given diagnostic from an LLVM source manager.
71+
void handleDiag(const llvm::SMDiagnostic &Diag);
72+
73+
/// Retrieve the diagnostic handler to use with the underlying SourceMgr.
74+
llvm::SourceMgr::DiagHandlerTy getDiagHandler() {
75+
return &SourceMgrAdapter::handleDiag;
76+
}
77+
78+
/// Retrieve the context to use with the diagnostic handler produced by
79+
/// \c getDiagHandler().
80+
void *getDiagContext() { return this; }
81+
};
82+
83+
} // end namespace clang
84+
85+
#endif

0 commit comments

Comments
 (0)