Skip to content

Commit 7a3a0a9

Browse files
committed
Symbol graph support
Adds a tool `swift-symbolgraph-extract` that reads an existing Swift module and prints a platform- and language-agnostic JSON description of the module, primarly for documentation. Adds a small sub-library `SymbolGraphGen` which houses the core implementation for collecting relevant information about declarations. The main entry point is integrated directly into the driver as a mode: the tool is meant to be run outside of the normal edit-compile-run/test workflow to avoid impacting build times. Along with common options for other tools, unique options include `pretty-print` for debugging, and a `minimum-access-level` options for including internal documentation. A symbol graph is a directed graph where the nodes are symbols in a module and the edges are relationships between them. For example, a `struct S` may have a member `var x`. The graph would have two nodes for `S` and `x`, and one "member-of" relationship edge. Other relationship kinds include "inherits-from" or "conforms to". The data format for a symbol graph is still under development and may change without notice until a specificiation and versioning scheme is published. Various aspects about a symbol are recorded in the nodes, such as availability, documentation comments, or data needed for printing the shapes of declarations without having to understand specifics about the langauge. Implicit and public-underscored stdlib declarations are not included by default. rdar://problem/55346798
1 parent b05a622 commit 7a3a0a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2574
-43
lines changed

include/swift/AST/Decl.h

+2
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,8 @@ class alignas(1 << DeclAlignInBits) Decl {
875875
LLVM_READONLY
876876
const GenericContext *getAsGenericContext() const;
877877

878+
bool hasUnderscoredNaming() const;
879+
878880
bool isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic = true) const;
879881

880882
AvailabilityContext getAvailabilityForLinkage() const;

include/swift/AST/PrintOptions.h

+7
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,13 @@ struct PrintOptions {
380380
ArgAndParamPrintingMode ArgAndParamPrinting =
381381
ArgAndParamPrintingMode::MatchSource;
382382

383+
/// Whether to print the default argument value string
384+
/// representation.
385+
bool PrintDefaultArgumentValue = true;
386+
387+
/// Whether to print "_" placeholders for empty arguments.
388+
bool PrintEmptyArgumentNames = true;
389+
383390
/// Whether to print documentation comments attached to declarations.
384391
/// Note that this may print documentation comments from related declarations
385392
/// (e.g. the overridden method in the superclass) if such comment is found.

include/swift/Driver/Driver.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ class Driver {
158158
Interactive, // swift
159159
Batch, // swiftc
160160
AutolinkExtract, // swift-autolink-extract
161-
SwiftIndent // swift-indent
161+
SwiftIndent, // swift-indent
162+
SymbolGraph // swift-symbolgraph
162163
};
163164

164165
class InputInfoMap;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===//
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+
#include "llvm/ADT/Triple.h"
14+
#include "swift/AST/AttrKind.h"
15+
16+
namespace swift {
17+
18+
class ModuleDecl;
19+
20+
namespace symbolgraphgen {
21+
22+
struct SymbolGraphOptions {
23+
/// The path to output the symbol graph JSON.
24+
StringRef OutputPath;
25+
26+
/// The target of the module.
27+
llvm::Triple Target;
28+
29+
/// Pretty-print the JSON with newlines and indentation.
30+
bool PrettyPrint;
31+
32+
/// The minimum access level that symbols must have in order to be
33+
/// included in the graph.
34+
AccessLevel MinimumAccessLevel;
35+
};
36+
37+
/// Emit a Symbol Graph JSON file for a module.
38+
int emitSymbolGraphForModule(ModuleDecl *M, const SymbolGraphOptions &Options);
39+
40+
} // end namespace symbolgraphgen
41+
} // end namespace swift

lib/AST/ASTPrinter.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -2632,6 +2632,9 @@ void PrintAST::printOneParameter(const ParamDecl *param,
26322632
// Else, print the argument only.
26332633
LLVM_FALLTHROUGH;
26342634
case PrintOptions::ArgAndParamPrintingMode::ArgumentOnly:
2635+
if (ArgName.empty() && !Options.PrintEmptyArgumentNames) {
2636+
return;
2637+
}
26352638
Printer.printName(ArgName, PrintNameContext::FunctionParameterExternal);
26362639

26372640
if (!ArgNameIsAPIByDefault && !ArgName.empty())
@@ -2686,7 +2689,7 @@ void PrintAST::printOneParameter(const ParamDecl *param,
26862689
if (param->isVariadic())
26872690
Printer << "...";
26882691

2689-
if (param->isDefaultArgument()) {
2692+
if (param->isDefaultArgument() && Options.PrintDefaultArgumentValue) {
26902693
SmallString<128> scratch;
26912694
auto defaultArgStr = param->getDefaultValueStringRepresentation(scratch);
26922695

lib/AST/Decl.cpp

+51-36
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,56 @@ bool ParameterList::hasInternalParameter(StringRef Prefix) const {
697697
return false;
698698
}
699699

700+
bool Decl::hasUnderscoredNaming() const {
701+
const Decl *D = this;
702+
if (const auto AFD = dyn_cast<AbstractFunctionDecl>(D)) {
703+
// If it's a function with a parameter with leading underscore, it's a
704+
// private function.
705+
if (AFD->getParameters()->hasInternalParameter("_")) {
706+
return true;
707+
}
708+
}
709+
710+
if (const auto SubscriptD = dyn_cast<SubscriptDecl>(D)) {
711+
if (SubscriptD->getIndices()->hasInternalParameter("_")) {
712+
return true;
713+
}
714+
}
715+
716+
if (const auto PD = dyn_cast<ProtocolDecl>(D)) {
717+
if (PD->getAttrs().hasAttribute<ShowInInterfaceAttr>()) {
718+
return false;
719+
}
720+
StringRef NameStr = PD->getNameStr();
721+
if (NameStr.startswith("_Builtin")) {
722+
return true;
723+
}
724+
if (NameStr.startswith("_ExpressibleBy")) {
725+
return true;
726+
}
727+
}
728+
729+
if (const auto ImportD = dyn_cast<ImportDecl>(D)) {
730+
if (const auto *Mod = ImportD->getModule()) {
731+
if (Mod->isSwiftShimsModule()) {
732+
return true;
733+
}
734+
}
735+
}
736+
737+
const auto VD = dyn_cast<ValueDecl>(D);
738+
if (!VD || !VD->hasName()) {
739+
return false;
740+
}
741+
742+
if (!VD->getBaseName().isSpecial() &&
743+
VD->getBaseName().getIdentifier().str().startswith("_")) {
744+
return true;
745+
}
746+
747+
return false;
748+
}
749+
700750
bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const {
701751
const Decl *D = this;
702752
if (auto ExtD = dyn_cast<ExtensionDecl>(D)) {
@@ -718,47 +768,12 @@ bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const {
718768
FU->getKind() != FileUnitKind::SerializedAST)
719769
return false;
720770

721-
if (auto AFD = dyn_cast<AbstractFunctionDecl>(D)) {
722-
// If it's a function with a parameter with leading underscore, it's a
723-
// private function.
724-
if (AFD->getParameters()->hasInternalParameter("_"))
725-
return true;
726-
}
727-
728-
if (auto SubscriptD = dyn_cast<SubscriptDecl>(D)) {
729-
if (SubscriptD->getIndices()->hasInternalParameter("_"))
730-
return true;
731-
}
732-
733771
if (auto PD = dyn_cast<ProtocolDecl>(D)) {
734-
if (PD->getAttrs().hasAttribute<ShowInInterfaceAttr>())
735-
return false;
736-
StringRef NameStr = PD->getNameStr();
737-
if (NameStr.startswith("_Builtin"))
738-
return true;
739-
if (NameStr.startswith("_ExpressibleBy"))
740-
return true;
741772
if (treatNonBuiltinProtocolsAsPublic)
742773
return false;
743774
}
744775

745-
if (auto ImportD = dyn_cast<ImportDecl>(D)) {
746-
if (auto *Mod = ImportD->getModule()) {
747-
if (Mod->isSwiftShimsModule())
748-
return true;
749-
}
750-
}
751-
752-
auto VD = dyn_cast<ValueDecl>(D);
753-
if (!VD || !VD->hasName())
754-
return false;
755-
756-
// If the name has leading underscore then it's a private symbol.
757-
if (!VD->getBaseName().isSpecial() &&
758-
VD->getBaseName().getIdentifier().str().startswith("_"))
759-
return true;
760-
761-
return false;
776+
return hasUnderscoredNaming();
762777
}
763778

764779
AvailabilityContext Decl::getAvailabilityForLinkage() const {

lib/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ add_subdirectory(SwiftRemoteMirror)
4242
add_subdirectory(SIL)
4343
add_subdirectory(SILGen)
4444
add_subdirectory(SILOptimizer)
45+
add_subdirectory(SymbolGraphGen)
4546
add_subdirectory(Syntax)
4647
add_subdirectory(SyntaxParse)
4748
add_subdirectory(TBDGen)

lib/Driver/Driver.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ void Driver::parseDriverKind(ArrayRef<const char *> Args) {
9898
.Case("swiftc", DriverKind::Batch)
9999
.Case("swift-autolink-extract", DriverKind::AutolinkExtract)
100100
.Case("swift-indent", DriverKind::SwiftIndent)
101+
.Case("swift-symbolgraph-extract", DriverKind::SymbolGraph)
101102
.Default(None);
102103

103104
if (Kind.hasValue())
@@ -3252,6 +3253,7 @@ void Driver::printHelp(bool ShowHidden) const {
32523253
case DriverKind::Batch:
32533254
case DriverKind::AutolinkExtract:
32543255
case DriverKind::SwiftIndent:
3256+
case DriverKind::SymbolGraph:
32553257
ExcludedFlagsBitmask |= options::NoBatchOption;
32563258
break;
32573259
}

lib/Markup/LineList.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ LineList MarkupContext::getLineList(swift::RawComment RC) {
115115
// Determine if we have leading decorations in this block comment.
116116
bool HasASCIIArt = false;
117117
if (swift::startsWithNewline(Cleaned)) {
118-
Builder.addLine(Cleaned.substr(0, 0), { C.Range.getStart(),
119-
C.Range.getStart() });
120118
unsigned NewlineBytes = swift::measureNewline(Cleaned);
121119
Cleaned = Cleaned.drop_front(NewlineBytes);
122120
CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(NewlineBytes);

lib/SymbolGraphGen/CMakeLists.txt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
add_swift_host_library(swiftSymbolGraphGen STATIC
2+
DeclarationFragmentPrinter.cpp
3+
Edge.cpp
4+
JSON.cpp
5+
Symbol.cpp
6+
SymbolGraph.cpp
7+
SymbolGraphGen.cpp
8+
SymbolGraphASTWalker.cpp)
9+
10+
target_link_libraries(swiftSymbolGraphGen
11+
swiftAST
12+
swiftFrontend
13+
swiftMarkup)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===--- DeclarationFragmentPrinter.cpp - Declaration Fragment Printer ----===//
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+
#include "DeclarationFragmentPrinter.h"
14+
#include "SymbolGraphASTWalker.h"
15+
16+
using namespace swift;
17+
using namespace symbolgraphgen;
18+
19+
void DeclarationFragmentPrinter::openFragment(FragmentKind Kind) {
20+
assert(Kind != FragmentKind::None);
21+
if (this->Kind != Kind) {
22+
closeFragment();
23+
this->Kind = Kind,
24+
Spelling.clear();
25+
USR.clear();
26+
}
27+
}
28+
29+
StringRef
30+
DeclarationFragmentPrinter::getKindSpelling(FragmentKind Kind) const {
31+
switch (Kind) {
32+
case FragmentKind::Keyword:
33+
return "keyword";
34+
case FragmentKind::Attribute:
35+
return "attribute";
36+
case FragmentKind::NumberLiteral:
37+
return "number";
38+
case FragmentKind::StringLiteral:
39+
return "string";
40+
case FragmentKind::Identifier:
41+
return "identifier";
42+
case FragmentKind::TypeIdentifier:
43+
return "typeIdentifier";
44+
case FragmentKind::GenericParameter:
45+
return "genericParameter";
46+
case FragmentKind::Text:
47+
return "text";
48+
case FragmentKind::None:
49+
llvm_unreachable("Fragment kind of 'None' has no spelling");
50+
}
51+
}
52+
53+
void DeclarationFragmentPrinter::closeFragment() {
54+
if (Kind == FragmentKind::None) {
55+
return;
56+
}
57+
58+
if (!Spelling.empty()) {
59+
OS.object([&](){
60+
OS.attribute("kind", getKindSpelling(Kind));
61+
OS.attribute("spelling", Spelling.str());
62+
if (!USR.empty()) {
63+
OS.attribute("preciseIdentifier", USR.str());
64+
}
65+
});
66+
}
67+
68+
Spelling.clear();
69+
USR.clear();
70+
Kind = FragmentKind::None;
71+
}
72+
73+
void DeclarationFragmentPrinter::printDeclLoc(const Decl *D) {
74+
switch (D->getKind()) {
75+
case DeclKind::Constructor:
76+
case DeclKind::Destructor:
77+
case DeclKind::Subscript:
78+
openFragment(FragmentKind::Keyword);
79+
break;
80+
default:
81+
openFragment(FragmentKind::Identifier);
82+
break;
83+
}
84+
}
85+
86+
void
87+
DeclarationFragmentPrinter::printNamePre(PrintNameContext Context) {
88+
switch (Context) {
89+
case PrintNameContext::Keyword:
90+
openFragment(FragmentKind::Keyword);
91+
break;
92+
case PrintNameContext::GenericParameter:
93+
openFragment(FragmentKind::GenericParameter);
94+
break;
95+
case PrintNameContext::Attribute:
96+
openFragment(FragmentKind::Attribute);
97+
break;
98+
case PrintNameContext::ClassDynamicSelf:
99+
case PrintNameContext::FunctionParameterExternal:
100+
openFragment(FragmentKind::Identifier);
101+
break;
102+
case PrintNameContext::FunctionParameterLocal:
103+
openFragment(FragmentKind::Identifier);
104+
break;
105+
case PrintNameContext::TupleElement:
106+
case PrintNameContext::TypeMember:
107+
case PrintNameContext::Normal:
108+
break;
109+
}
110+
}
111+
112+
void DeclarationFragmentPrinter::printStructurePre(PrintStructureKind Kind,
113+
const Decl *D) {
114+
switch (Kind) {
115+
case PrintStructureKind::NumberLiteral:
116+
openFragment(FragmentKind::NumberLiteral);
117+
break;
118+
case PrintStructureKind::StringLiteral:
119+
openFragment(FragmentKind::StringLiteral);
120+
break;
121+
default:
122+
break;
123+
}
124+
}
125+
126+
void DeclarationFragmentPrinter::printTypeRef(Type T, const TypeDecl *RefTo,
127+
Identifier Name,
128+
PrintNameContext NameContext) {
129+
openFragment(FragmentKind::TypeIdentifier);
130+
printText(Name.str());
131+
USR = Walker.getUSR(RefTo);
132+
closeFragment();
133+
}
134+
135+
void DeclarationFragmentPrinter::printText(StringRef Text) {
136+
if (Kind == FragmentKind::None) {
137+
openFragment(FragmentKind::Text);
138+
}
139+
Spelling.append(Text);
140+
}

0 commit comments

Comments
 (0)