Skip to content

Commit ccf244b

Browse files
committed
Add ability to load access notes
1 parent bcfafc1 commit ccf244b

File tree

12 files changed

+972
-2
lines changed

12 files changed

+972
-2
lines changed

include/swift/AST/AccessNotes.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===--- AccessNotes.h - Access Notes ---------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Implements access notes, which allow certain modifiers or attributes to be
14+
// added to the declarations in a module.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef ACCESSNOTES_H
19+
#define ACCESSNOTES_H
20+
21+
#include "swift/AST/Identifier.h"
22+
#include "swift/Basic/NullablePtr.h"
23+
#include "llvm/Support/Error.h"
24+
#include "llvm/Support/MemoryBuffer.h"
25+
#include "llvm/Support/raw_ostream.h"
26+
#include <vector>
27+
28+
namespace swift {
29+
class ASTContext;
30+
class ValueDecl;
31+
32+
class AccessNote {
33+
public:
34+
DeclName name;
35+
std::vector<AccessNote> members; // can't be SmallVector due to recursion
36+
37+
Optional<bool> ObjC;
38+
Optional<bool> Dynamic;
39+
Optional<ObjCSelector> ObjCName;
40+
41+
void dump(llvm::raw_ostream &os, int indent = 0) const;
42+
SWIFT_DEBUG_DUMP;
43+
};
44+
45+
class AccessNotes {
46+
public:
47+
std::string reason;
48+
std::vector<AccessNote> notes;
49+
50+
static llvm::Expected<AccessNotes> load(ASTContext &ctx,
51+
llvm::MemoryBuffer *buffer);
52+
53+
NullablePtr<const AccessNote> lookup(ValueDecl *VD) const;
54+
55+
void dump(llvm::raw_ostream &os) const;
56+
SWIFT_DEBUG_DUMP;
57+
};
58+
59+
}
60+
61+
#endif

include/swift/AST/Module.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_MODULE_H
1818
#define SWIFT_MODULE_H
1919

20+
#include "swift/AST/AccessNotes.h"
2021
#include "swift/AST/Decl.h"
2122
#include "swift/AST/DeclContext.h"
2223
#include "swift/AST/Identifier.h"
@@ -249,6 +250,8 @@ class ModuleDecl : public DeclContext, public TypeDecl {
249250
/// \see EntryPointInfoTy
250251
EntryPointInfoTy EntryPointInfo;
251252

253+
AccessNotes accessNotes;
254+
252255
ModuleDecl(Identifier name, ASTContext &ctx, ImplicitImportInfo importInfo);
253256

254257
public:
@@ -279,6 +282,9 @@ class ModuleDecl : public DeclContext, public TypeDecl {
279282
/// imports.
280283
ImplicitImportList getImplicitImports() const;
281284

285+
AccessNotes &getAccessNotes() { return accessNotes; }
286+
const AccessNotes &getAccessNotes() const { return accessNotes; }
287+
282288
ArrayRef<FileUnit *> getFiles() {
283289
assert(!Files.empty() || failedToLoad());
284290
return Files;

include/swift/Basic/SourceManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class SourceManager {
7878
FileSystem = FS;
7979
}
8080

81-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getFileSystem() {
81+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getFileSystem() const {
8282
return FileSystem;
8383
}
8484

include/swift/Frontend/Frontend.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,9 @@ class CompilerInstance {
467467
DiagnosticEngine &getDiags() { return Diagnostics; }
468468
const DiagnosticEngine &getDiags() const { return Diagnostics; }
469469

470-
llvm::vfs::FileSystem &getFileSystem() { return *SourceMgr.getFileSystem(); }
470+
llvm::vfs::FileSystem &getFileSystem() const {
471+
return *SourceMgr.getFileSystem();
472+
}
471473

472474
ASTContext &getASTContext() { return *Context; }
473475
const ASTContext &getASTContext() const { return *Context; }

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ class FrontendOptions {
7070
/// The path to which we should store indexing data, if any.
7171
std::string IndexStorePath;
7272

73+
/// The path to load access notes from.
74+
std::string AccessNotesPath;
75+
7376
/// The path to look in when loading a module interface file, to see if a
7477
/// binary module has already been built for use by the compiler.
7578
std::string PrebuiltModuleCachePath;

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ def supplementary_output_file_map : Separate<["-"], "supplementary-output-file-m
3333
def frontend_parseable_output : Flag<["-"], "frontend-parseable-output">,
3434
HelpText<"Emit textual output in a parseable format">;
3535

36+
def access_notes : Separate<["-"], "access-notes">, MetaVarName<"<path>">,
37+
HelpText<"Specify YAML file to override attributes on Swift declarations in this module">;
38+
3639
def emit_module_doc : Flag<["-"], "emit-module-doc">,
3740
HelpText<"Emit a module documentation file based on documentation "
3841
"comments">;

lib/AST/AccessNotes.cpp

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
//===--- AccessNotes.cpp - Access Notes -------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Implements access notes, which allow certain modifiers or attributes to be
14+
// added to the declarations in a module.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#include "swift/AST/AccessNotes.h"
19+
#include "swift/AST/Attr.h"
20+
#include "swift/AST/Decl.h"
21+
#include "swift/Parse/Parser.h"
22+
#include "llvm/ADT/STLExtras.h"
23+
#include "llvm/ADT/StringRef.h"
24+
#include "llvm/Support/YAMLTraits.h"
25+
26+
// FIXME: Copied from MiscDiagnostics--don't do that.
27+
static llvm::Optional<swift::ObjCSelector>
28+
parseObjCSelector(swift::ASTContext &ctx, llvm::StringRef string) {
29+
using namespace swift;
30+
31+
// Find the first colon.
32+
auto colonPos = string.find(':');
33+
34+
// If there is no colon, we have a nullary selector.
35+
if (colonPos == StringRef::npos) {
36+
if (string.empty() || !Lexer::isIdentifier(string)) return None;
37+
return ObjCSelector(ctx, 0, { ctx.getIdentifier(string) });
38+
}
39+
40+
SmallVector<Identifier, 2> pieces;
41+
do {
42+
// Check whether we have a valid selector piece.
43+
auto piece = string.substr(0, colonPos);
44+
if (piece.empty()) {
45+
pieces.push_back(Identifier());
46+
} else {
47+
if (!Lexer::isIdentifier(piece)) return None;
48+
pieces.push_back(ctx.getIdentifier(piece));
49+
}
50+
51+
// Move to the next piece.
52+
string = string.substr(colonPos+1);
53+
colonPos = string.find(':');
54+
} while (colonPos != StringRef::npos);
55+
56+
// If anything remains of the string, it's not a selector.
57+
if (!string.empty()) return None;
58+
59+
return ObjCSelector(ctx, pieces.size(), pieces);
60+
}
61+
62+
namespace swift {
63+
64+
NullablePtr<const AccessNote> AccessNotes::lookup(ValueDecl *VD) const {
65+
assert(VD != nullptr);
66+
67+
auto lookupVD = VD;
68+
if (auto accessor = dyn_cast<AccessorDecl>(VD))
69+
VD = accessor->getStorage();
70+
71+
auto lookupName = lookupVD->getName();
72+
73+
// FIXME: parseDeclName() doesn't handle the special `subscript` name.
74+
// Fixing this without affecting existing uses in import-as-member will need
75+
// a bit of work. Hack around this by changing to a normal identifier.
76+
if (isa<SubscriptDecl>(lookupVD) &&
77+
lookupName.getBaseName() == DeclBaseName::createSubscript()) {
78+
ASTContext &ctx = lookupVD->getASTContext();
79+
lookupName = DeclName(ctx, ctx.getIdentifier("subscript"),
80+
lookupName.getArgumentNames());
81+
}
82+
83+
const std::vector<AccessNote> *notesToSearch = &notes;
84+
85+
// If nested, look at the parent context's notes.
86+
if (auto parent = lookupVD->getDeclContext()->getSelfNominalTypeDecl()) {
87+
if (auto parentNote = lookup(parent))
88+
notesToSearch = &(parentNote.get()->members);
89+
else
90+
return nullptr;
91+
}
92+
93+
auto iter = llvm::find_if(*notesToSearch, [&](const AccessNote &note) -> bool {
94+
return lookupName.matchesRef(note.name);
95+
});
96+
return NullablePtr<const AccessNote>(iter == notesToSearch->end() ? nullptr : &*iter);
97+
}
98+
99+
void AccessNotes::dump() const {
100+
dump(llvm::dbgs());
101+
llvm::dbgs() << "\n";
102+
}
103+
void AccessNote::dump() const {
104+
dump(llvm::dbgs());
105+
llvm::dbgs() << "\n";
106+
}
107+
108+
void AccessNotes::dump(llvm::raw_ostream &os) const {
109+
os << "(access_notes reason='" << reason << "'";
110+
for (const auto &note : notes) {
111+
os << "\n";
112+
note.dump(os, /*indent=*/2);
113+
}
114+
}
115+
116+
void AccessNote::dump(llvm::raw_ostream &os, int indent) const {
117+
os.indent(indent) << "(note name='" << name << "'";
118+
if (name.getBaseName().isSpecial())
119+
os << " is_special_name";
120+
121+
if (ObjC)
122+
os << " objc=" << *ObjC;
123+
if (ObjCName)
124+
os << " objc_name='" << *ObjCName << "'";
125+
if (Dynamic)
126+
os << " dynamic=" << *Dynamic;
127+
128+
if (!members.empty()) {
129+
os << "\n";
130+
os.indent(indent + 2) << "(members";
131+
for (const auto &member : members) {
132+
os << "\n";
133+
member.dump(os, indent + 4);
134+
}
135+
os << ")";
136+
}
137+
138+
os << ")";
139+
}
140+
141+
}
142+
143+
LLVM_YAML_DECLARE_SCALAR_TRAITS(swift::DeclName, QuotingType::Single)
144+
LLVM_YAML_DECLARE_SCALAR_TRAITS(swift::ObjCSelector, QuotingType::Single);
145+
LLVM_YAML_IS_SEQUENCE_VECTOR(swift::AccessNote)
146+
LLVM_YAML_DECLARE_MAPPING_TRAITS(swift::AccessNotes)
147+
148+
// Not using macro to avoid validation issues.
149+
template <> struct llvm::yaml::MappingTraits<swift::AccessNote> {
150+
static void mapping(IO &IO, swift::AccessNote &Obj);
151+
static StringRef validate(IO &IO, swift::AccessNote &Obj);
152+
};
153+
154+
namespace swift {
155+
llvm::Expected<AccessNotes>
156+
AccessNotes::load(ASTContext &ctx, llvm::MemoryBuffer *buffer) {
157+
llvm::yaml::Input yamlIn(buffer->getBuffer(), (void *)&ctx);
158+
159+
AccessNotes notes;
160+
yamlIn >> notes;
161+
162+
if (yamlIn.error())
163+
return llvm::errorCodeToError(yamlIn.error());
164+
165+
return notes;
166+
}
167+
}
168+
169+
170+
namespace llvm {
171+
namespace yaml {
172+
173+
using AccessNote = swift::AccessNote;
174+
using AccessNotes = swift::AccessNotes;
175+
using ASTContext = swift::ASTContext;
176+
using DeclName = swift::DeclName;
177+
using ObjCSelector = swift::ObjCSelector;
178+
179+
void ScalarTraits<DeclName>::output(const DeclName &name, void *ctxPtr,
180+
raw_ostream &os) {
181+
name.print(os, /*skipEmptyArgumentNames=*/false);
182+
}
183+
184+
StringRef ScalarTraits<DeclName>::input(StringRef str, void *ctxPtr,
185+
DeclName &name) {
186+
ASTContext &ctx = *static_cast<ASTContext *>(ctxPtr);
187+
name = parseDeclName(ctx, str);
188+
return name ? "" : "invalid declaration name";
189+
}
190+
191+
void ScalarTraits<ObjCSelector>::output(const ObjCSelector &selector,
192+
void *ctxPtr, raw_ostream &os) {
193+
os << selector;
194+
}
195+
196+
StringRef ScalarTraits<ObjCSelector>::input(StringRef str, void *ctxPtr,
197+
ObjCSelector &selector) {
198+
ASTContext &ctx = *static_cast<ASTContext *>(ctxPtr);
199+
200+
if (auto sel = parseObjCSelector(ctx, str)) {
201+
selector = *sel;
202+
return "";
203+
}
204+
205+
return "invalid selector";
206+
}
207+
208+
void MappingTraits<AccessNote>::mapping(IO &io, AccessNote &note) {
209+
io.mapRequired("Name", note.name);
210+
211+
io.mapOptional("ObjC", note.ObjC);
212+
io.mapOptional("Dynamic", note.Dynamic);
213+
io.mapOptional("ObjCName", note.ObjCName);
214+
215+
io.mapOptional("Members", note.members);
216+
}
217+
218+
StringRef MappingTraits<AccessNote>::validate(IO &io, AccessNote &note) {
219+
if (!note.ObjC.getValueOr(true) && note.ObjCName.hasValue())
220+
return "cannot have an 'ObjCName' if 'ObjC' is false";
221+
222+
return "";
223+
}
224+
225+
void MappingTraits<AccessNotes>::mapping(IO &io, AccessNotes &notes) {
226+
io.mapRequired("Reason", notes.reason);
227+
io.mapRequired("Notes", notes.notes);
228+
}
229+
230+
}
231+
}

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set_swift_llvm_is_available()
1313

1414
add_swift_host_library(swiftAST STATIC
1515
AbstractSourceFileDepGraphFactory.cpp
16+
AccessNotes.cpp
1617
AccessRequests.cpp
1718
ASTContext.cpp
1819
ASTDemangler.cpp

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ bool ArgsToFrontendOptionsConverter::convert(
216216
if (const Arg *A = Args.getLastArg(OPT_module_link_name))
217217
Opts.ModuleLinkName = A->getValue();
218218

219+
if (const Arg *A = Args.getLastArg(OPT_access_notes))
220+
Opts.AccessNotesPath = A->getValue();
221+
219222
if (const Arg *A = Args.getLastArg(OPT_serialize_debugging_options,
220223
OPT_no_serialize_debugging_options)) {
221224
Opts.SerializeOptionsForDebugging =

0 commit comments

Comments
 (0)