diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d8ba393ded6fb..57514cb26bd90 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -875,6 +875,8 @@ class alignas(1 << DeclAlignInBits) Decl { LLVM_READONLY const GenericContext *getAsGenericContext() const; + bool hasUnderscoredNaming() const; + bool isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic = true) const; AvailabilityContext getAvailabilityForLinkage() const; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 6a142e02da510..55aff0da73aca 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -380,6 +380,13 @@ struct PrintOptions { ArgAndParamPrintingMode ArgAndParamPrinting = ArgAndParamPrintingMode::MatchSource; + /// Whether to print the default argument value string + /// representation. + bool PrintDefaultArgumentValue = true; + + /// Whether to print "_" placeholders for empty arguments. + bool PrintEmptyArgumentNames = true; + /// Whether to print documentation comments attached to declarations. /// Note that this may print documentation comments from related declarations /// (e.g. the overridden method in the superclass) if such comment is found. diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index b72397379bb68..d927f3908a14f 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -158,7 +158,8 @@ class Driver { Interactive, // swift Batch, // swiftc AutolinkExtract, // swift-autolink-extract - SwiftIndent // swift-indent + SwiftIndent, // swift-indent + SymbolGraph // swift-symbolgraph }; class InputInfoMap; diff --git a/include/swift/SymbolGraphGen/SymbolGraphGen.h b/include/swift/SymbolGraphGen/SymbolGraphGen.h new file mode 100644 index 0000000000000..324a505c45e39 --- /dev/null +++ b/include/swift/SymbolGraphGen/SymbolGraphGen.h @@ -0,0 +1,41 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "swift/AST/AttrKind.h" + +namespace swift { + +class ModuleDecl; + +namespace symbolgraphgen { + +struct SymbolGraphOptions { + /// The path to output the symbol graph JSON. + StringRef OutputPath; + + /// The target of the module. + llvm::Triple Target; + + /// Pretty-print the JSON with newlines and indentation. + bool PrettyPrint; + + /// The minimum access level that symbols must have in order to be + /// included in the graph. + AccessLevel MinimumAccessLevel; +}; + +/// Emit a Symbol Graph JSON file for a module. +int emitSymbolGraphForModule(ModuleDecl *M, const SymbolGraphOptions &Options); + +} // end namespace symbolgraphgen +} // end namespace swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 99a0ca8c982a8..992f27e7b8c19 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2632,6 +2632,9 @@ void PrintAST::printOneParameter(const ParamDecl *param, // Else, print the argument only. LLVM_FALLTHROUGH; case PrintOptions::ArgAndParamPrintingMode::ArgumentOnly: + if (ArgName.empty() && !Options.PrintEmptyArgumentNames) { + return; + } Printer.printName(ArgName, PrintNameContext::FunctionParameterExternal); if (!ArgNameIsAPIByDefault && !ArgName.empty()) @@ -2686,7 +2689,7 @@ void PrintAST::printOneParameter(const ParamDecl *param, if (param->isVariadic()) Printer << "..."; - if (param->isDefaultArgument()) { + if (param->isDefaultArgument() && Options.PrintDefaultArgumentValue) { SmallString<128> scratch; auto defaultArgStr = param->getDefaultValueStringRepresentation(scratch); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 73b488298f153..ecc8f250d82ed 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -697,6 +697,56 @@ bool ParameterList::hasInternalParameter(StringRef Prefix) const { return false; } +bool Decl::hasUnderscoredNaming() const { + const Decl *D = this; + if (const auto AFD = dyn_cast(D)) { + // If it's a function with a parameter with leading underscore, it's a + // private function. + if (AFD->getParameters()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto SubscriptD = dyn_cast(D)) { + if (SubscriptD->getIndices()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto PD = dyn_cast(D)) { + if (PD->getAttrs().hasAttribute()) { + return false; + } + StringRef NameStr = PD->getNameStr(); + if (NameStr.startswith("_Builtin")) { + return true; + } + if (NameStr.startswith("_ExpressibleBy")) { + return true; + } + } + + if (const auto ImportD = dyn_cast(D)) { + if (const auto *Mod = ImportD->getModule()) { + if (Mod->isSwiftShimsModule()) { + return true; + } + } + } + + const auto VD = dyn_cast(D); + if (!VD || !VD->hasName()) { + return false; + } + + if (!VD->getBaseName().isSpecial() && + VD->getBaseName().getIdentifier().str().startswith("_")) { + return true; + } + + return false; +} + bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { const Decl *D = this; if (auto ExtD = dyn_cast(D)) { @@ -718,47 +768,12 @@ bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { FU->getKind() != FileUnitKind::SerializedAST) return false; - if (auto AFD = dyn_cast(D)) { - // If it's a function with a parameter with leading underscore, it's a - // private function. - if (AFD->getParameters()->hasInternalParameter("_")) - return true; - } - - if (auto SubscriptD = dyn_cast(D)) { - if (SubscriptD->getIndices()->hasInternalParameter("_")) - return true; - } - if (auto PD = dyn_cast(D)) { - if (PD->getAttrs().hasAttribute()) - return false; - StringRef NameStr = PD->getNameStr(); - if (NameStr.startswith("_Builtin")) - return true; - if (NameStr.startswith("_ExpressibleBy")) - return true; if (treatNonBuiltinProtocolsAsPublic) return false; } - if (auto ImportD = dyn_cast(D)) { - if (auto *Mod = ImportD->getModule()) { - if (Mod->isSwiftShimsModule()) - return true; - } - } - - auto VD = dyn_cast(D); - if (!VD || !VD->hasName()) - return false; - - // If the name has leading underscore then it's a private symbol. - if (!VD->getBaseName().isSpecial() && - VD->getBaseName().getIdentifier().str().startswith("_")) - return true; - - return false; + return hasUnderscoredNaming(); } AvailabilityContext Decl::getAvailabilityForLinkage() const { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b06020ecb4f44..e3a21225fdb66 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -42,6 +42,7 @@ add_subdirectory(SwiftRemoteMirror) add_subdirectory(SIL) add_subdirectory(SILGen) add_subdirectory(SILOptimizer) +add_subdirectory(SymbolGraphGen) add_subdirectory(Syntax) add_subdirectory(SyntaxParse) add_subdirectory(TBDGen) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index e3a6e2a3361cf..34db9ebb425d8 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -98,6 +98,7 @@ void Driver::parseDriverKind(ArrayRef Args) { .Case("swiftc", DriverKind::Batch) .Case("swift-autolink-extract", DriverKind::AutolinkExtract) .Case("swift-indent", DriverKind::SwiftIndent) + .Case("swift-symbolgraph-extract", DriverKind::SymbolGraph) .Default(None); if (Kind.hasValue()) @@ -3252,6 +3253,7 @@ void Driver::printHelp(bool ShowHidden) const { case DriverKind::Batch: case DriverKind::AutolinkExtract: case DriverKind::SwiftIndent: + case DriverKind::SymbolGraph: ExcludedFlagsBitmask |= options::NoBatchOption; break; } diff --git a/lib/Markup/LineList.cpp b/lib/Markup/LineList.cpp index 0afeb5eec995d..a89488b8c4213 100644 --- a/lib/Markup/LineList.cpp +++ b/lib/Markup/LineList.cpp @@ -115,8 +115,6 @@ LineList MarkupContext::getLineList(swift::RawComment RC) { // Determine if we have leading decorations in this block comment. bool HasASCIIArt = false; if (swift::startsWithNewline(Cleaned)) { - Builder.addLine(Cleaned.substr(0, 0), { C.Range.getStart(), - C.Range.getStart() }); unsigned NewlineBytes = swift::measureNewline(Cleaned); Cleaned = Cleaned.drop_front(NewlineBytes); CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(NewlineBytes); diff --git a/lib/SymbolGraphGen/CMakeLists.txt b/lib/SymbolGraphGen/CMakeLists.txt new file mode 100644 index 0000000000000..2780330587fb2 --- /dev/null +++ b/lib/SymbolGraphGen/CMakeLists.txt @@ -0,0 +1,13 @@ +add_swift_host_library(swiftSymbolGraphGen STATIC + DeclarationFragmentPrinter.cpp + Edge.cpp + JSON.cpp + Symbol.cpp + SymbolGraph.cpp + SymbolGraphGen.cpp + SymbolGraphASTWalker.cpp) + +target_link_libraries(swiftSymbolGraphGen + swiftAST + swiftFrontend + swiftMarkup) diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp new file mode 100644 index 0000000000000..2e8db85e2d3b2 --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp @@ -0,0 +1,140 @@ +//===--- DeclarationFragmentPrinter.cpp - Declaration Fragment Printer ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void DeclarationFragmentPrinter::openFragment(FragmentKind Kind) { + assert(Kind != FragmentKind::None); + if (this->Kind != Kind) { + closeFragment(); + this->Kind = Kind, + Spelling.clear(); + USR.clear(); + } +} + +StringRef +DeclarationFragmentPrinter::getKindSpelling(FragmentKind Kind) const { + switch (Kind) { + case FragmentKind::Keyword: + return "keyword"; + case FragmentKind::Attribute: + return "attribute"; + case FragmentKind::NumberLiteral: + return "number"; + case FragmentKind::StringLiteral: + return "string"; + case FragmentKind::Identifier: + return "identifier"; + case FragmentKind::TypeIdentifier: + return "typeIdentifier"; + case FragmentKind::GenericParameter: + return "genericParameter"; + case FragmentKind::Text: + return "text"; + case FragmentKind::None: + llvm_unreachable("Fragment kind of 'None' has no spelling"); + } +} + +void DeclarationFragmentPrinter::closeFragment() { + if (Kind == FragmentKind::None) { + return; + } + + if (!Spelling.empty()) { + OS.object([&](){ + OS.attribute("kind", getKindSpelling(Kind)); + OS.attribute("spelling", Spelling.str()); + if (!USR.empty()) { + OS.attribute("preciseIdentifier", USR.str()); + } + }); + } + + Spelling.clear(); + USR.clear(); + Kind = FragmentKind::None; +} + +void DeclarationFragmentPrinter::printDeclLoc(const Decl *D) { + switch (D->getKind()) { + case DeclKind::Constructor: + case DeclKind::Destructor: + case DeclKind::Subscript: + openFragment(FragmentKind::Keyword); + break; + default: + openFragment(FragmentKind::Identifier); + break; + } +} + +void +DeclarationFragmentPrinter::printNamePre(PrintNameContext Context) { + switch (Context) { + case PrintNameContext::Keyword: + openFragment(FragmentKind::Keyword); + break; + case PrintNameContext::GenericParameter: + openFragment(FragmentKind::GenericParameter); + break; + case PrintNameContext::Attribute: + openFragment(FragmentKind::Attribute); + break; + case PrintNameContext::ClassDynamicSelf: + case PrintNameContext::FunctionParameterExternal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::FunctionParameterLocal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::TupleElement: + case PrintNameContext::TypeMember: + case PrintNameContext::Normal: + break; + } +} + +void DeclarationFragmentPrinter::printStructurePre(PrintStructureKind Kind, + const Decl *D) { + switch (Kind) { + case PrintStructureKind::NumberLiteral: + openFragment(FragmentKind::NumberLiteral); + break; + case PrintStructureKind::StringLiteral: + openFragment(FragmentKind::StringLiteral); + break; + default: + break; + } +} + +void DeclarationFragmentPrinter::printTypeRef(Type T, const TypeDecl *RefTo, + Identifier Name, + PrintNameContext NameContext) { + openFragment(FragmentKind::TypeIdentifier); + printText(Name.str()); + USR = Walker.getUSR(RefTo); + closeFragment(); +} + +void DeclarationFragmentPrinter::printText(StringRef Text) { + if (Kind == FragmentKind::None) { + openFragment(FragmentKind::Text); + } + Spelling.append(Text); +} diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.h b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h new file mode 100644 index 0000000000000..2e5ec449bd34f --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h @@ -0,0 +1,123 @@ +//===--- DeclarationFragmentPrinter.h - Declaration Fragment Printer ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H +#define SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/Basic/LLVM.h" + +namespace swift { + +class Decl; +class Type; +class TypeDecl; + +namespace symbolgraphgen { + +struct SymbolGraphASTWalker; + +/// Prints AST nodes as a stream of tagged fragments for syntax highlighting. +/// +/// These fragments are meant to display a somewhat abbreviated part of the +/// declaration for display in documentation, ignoring things like member and +/// function bodies. +/// +/// For example, a function: +/// +/// ```swift +/// func foo() { +/// print("Hello, world!") +/// } +/// ``` +/// +/// Will have fragments representing the `func foo()` part. +class DeclarationFragmentPrinter : public ASTPrinter { + enum class FragmentKind { + None, + Keyword, + Attribute, + NumberLiteral, + StringLiteral, + Identifier, + TypeIdentifier, + GenericParameter, + Text, + }; + + SymbolGraphASTWalker &Walker; + + /// The output stream to print fragment objects to. + llvm::json::OStream &OS; + + /// The current fragment being considered. + FragmentKind Kind; + + /// The actual source text of the fragment. + SmallString<256> Spelling; + + SmallString<256> USR; + + StringRef getKindSpelling(FragmentKind Kind) const; + + /// Open a new kind of fragment without committing its spelling. + void openFragment(FragmentKind Kind); + + /// Close the current fragment if there is one, and commit it for display. + void closeFragment(); + +public: + DeclarationFragmentPrinter(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS, + Optional Key = None) + : Walker(Walker), + OS(OS), + Kind(FragmentKind::None) { + if (Key) { + OS.attributeBegin(*Key); + OS.arrayBegin(); + } else { + OS.arrayBegin(); + } + } + + void printDeclLoc(const Decl *D) override; + + void printDeclNameEndLoc(const Decl *D) override { + closeFragment(); + } + + void printNamePre(PrintNameContext Context) override; + + void printStructurePre(PrintStructureKind Kind, const Decl *D) override; + + void printNamePost(PrintNameContext Context) override { + closeFragment(); + } + + void printTypeRef(Type T, const TypeDecl *RefTo, Identifier Name, + PrintNameContext NameContext) override; + + void printText(StringRef Text) override; + + ~DeclarationFragmentPrinter() { + closeFragment(); + OS.arrayEnd(); + OS.attributeEnd(); + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp new file mode 100644 index 0000000000000..296cc0960fee2 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.cpp @@ -0,0 +1,40 @@ +//===--- Edge.cpp - Symbol Graph Edge -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "Edge.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Edge::serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("kind", Kind.Name); + OS.attribute("source", Walker->getUSR(Source)); + OS.attribute("target", Walker->getUSR(Target)); + + // In case a dependent module isn't available, serialize a fallback name. + auto TargetModuleName = Target->getModuleContext()->getName().str(); + if (TargetModuleName != Walker->M.getName().str()) { + auto TargetSymbolIdentifier = Walker->getSymbolIdentifier(Target); + auto TargetComponents = TargetSymbolIdentifier.SimpleComponents; + SmallString<128> Scratch(TargetModuleName); + for (auto it = TargetComponents.begin(); + it != TargetComponents.end(); ++it) { + Scratch.push_back('.'); + Scratch.append(*it); + } + OS.attribute("targetFallback", Scratch.str()); + } + }); +} diff --git a/lib/SymbolGraphGen/Edge.h b/lib/SymbolGraphGen/Edge.h new file mode 100644 index 0000000000000..e22d2880151a7 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.h @@ -0,0 +1,167 @@ +//===--- Edge.h - Symbol Graph Edge ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_EDGE_H +#define SWIFT_SYMBOLGRAPHGEN_EDGE_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Decl.h" +#include "swift/Basic/LLVM.h" + +#include "JSON.h" +#include "Symbol.h" + +namespace swift { +namespace symbolgraphgen { + +/// The kind of relationship, tagging an edge in the graph. +struct RelationshipKind { + StringRef Name; + + RelationshipKind(llvm::StringRef Name) : Name(Name) {} + + /** + A symbol A is a member of another symbol B. + + For example, a method or field of a class would be a member of that class. + + The implied inverse of this relationship is a symbol B is the owner + of a member symbol A. + */ + static inline RelationshipKind MemberOf() { + return RelationshipKind { "memberOf" }; + } + + /** + A symbol A conforms to an interface/protocol symbol B. + + For example, a class `C` that conforms to protocol `P` in Swift would use + this relationship. + + The implied inverse of this relationship is a symbol B that has + a conformer A. + */ + static inline RelationshipKind ConformsTo() { + return RelationshipKind { "conformsTo" }; + } + /** + A symbol A inherits from another symbol B. + + For example, a derived class inherits from a base class, or a protocol + that refines another protocol would use this relationship. + + The implied inverse of this relationship is a symbol B is a base + of another symbol A. + */ + static inline RelationshipKind InheritsFrom() { + return RelationshipKind { "inheritsFrom" }; + } + /** + A symbol A serves as a default implementation of an interface requirement B. + + The implied inverse of this relationship is an interface requirement B + has a default implementation of A. + */ + static inline RelationshipKind DefaultImplementationOf() { + return RelationshipKind { "defaultImplementationOf" }; + } + /** + A symbol A overrides another symbol B, such as through inheritance. + + The implied inverse of this relationship is a symbol A is the base + of symbol B. + */ + static inline RelationshipKind Overrides() { + return RelationshipKind { "overrides" }; + } + /** + A symbol A is a requirement of interface B. + + The implied inverse of this relationship is an interface B + has a requirement of A. + */ + static inline RelationshipKind RequirementOf() { + return RelationshipKind { "requirementOf" }; + } + /** + A symbol A is an optional requirement of interface B. + + The implied inverse of this relationship is an interface B + has an optional requirement of A. + */ + static inline RelationshipKind OptionalRequirementOf() { + return RelationshipKind { "optionalRequirementOf" }; + } + + bool operator==(const RelationshipKind &Other) const { + return Name == Other.Name; + } + + bool operator<(const RelationshipKind &Other) const { + return Name < Other.Name; + } +}; + +/// A relationship between two symbols: an edge in a directed graph. +struct Edge { + SymbolGraphASTWalker *Walker; + + /// The kind of relationship this edge represents. + RelationshipKind Kind; + + /// The precise identifier of the source symbol node. + const ValueDecl *Source; + + /// The precise identifier of the target symbol node. + const ValueDecl *Target; + + void serialize(llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +namespace llvm { +using Edge = swift::symbolgraphgen::Edge; +template <> struct DenseMapInfo { + static inline Edge getEmptyKey() { + return { + nullptr, + { "Empty" }, + nullptr, + nullptr, + }; + } + static inline Edge getTombstoneKey() { + return { + nullptr, + { "Tombstone" }, + nullptr, + nullptr, + }; + } + static unsigned getHashValue(const Edge E) { + unsigned H = 0; + H ^= DenseMapInfo::getHashValue(E.Kind.Name); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Source)); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Target)); + return H; + } + static bool isEqual(const Edge LHS, const Edge RHS) { + return LHS.Kind == RHS.Kind && + LHS.Source == RHS.Source && + LHS.Target == RHS.Target; + } +}; +} // end namespace llvm + +#endif // SWIFT_SYMBOLGRAPHGEN_EDGE_H diff --git a/lib/SymbolGraphGen/FormatVersion.h b/lib/SymbolGraphGen/FormatVersion.h new file mode 100644 index 0000000000000..74ac1c196c5b4 --- /dev/null +++ b/lib/SymbolGraphGen/FormatVersion.h @@ -0,0 +1,20 @@ +//===--- FormatVersion.h - Symbol Graph Format Version 00------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H +#define SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H + +#define SWIFT_SYMBOLGRAPH_FORMAT_MAJOR 0 +#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 1 +#define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 0 + +#endif // SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H diff --git a/lib/SymbolGraphGen/JSON.cpp b/lib/SymbolGraphGen/JSON.cpp new file mode 100644 index 0000000000000..6bb117daaeeb4 --- /dev/null +++ b/lib/SymbolGraphGen/JSON.cpp @@ -0,0 +1,57 @@ +//===--- JSON.cpp - Symbol Graph JSON Helpers -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#include "JSON.h" + +void swift::symbolgraphgen::serialize(const llvm::VersionTuple &VT, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("major", VT.getMajor()); + if (VT.getMinor()) { + OS.attribute("minor", *VT.getMinor()); + } + if (VT.getSubminor()) { + OS.attribute("patch", *VT.getSubminor()); + } + // Despite the name, + // this is not Semantic Versioning "build metadata" + if (VT.getBuild()) { + OS.attribute("prerelease", *VT.getBuild()); + } + }); +} + +void swift::symbolgraphgen::serialize(const llvm::Triple &T, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("architecture", T.getArchName()); + if (!T.getEnvironmentName().empty()) { + OS.attribute("environment", T.getEnvironmentName()); + } + OS.attribute("vendor", T.getVendorName()); + OS.attributeObject("operatingSystem", [&](){ + OS.attribute("name", T.getOSTypeName(T.getOS())); + + unsigned Major; + unsigned Minor; + unsigned Patch; + T.getOSVersion(Major, Minor, Patch); + llvm::VersionTuple OSVersion(Major, Minor, Patch); + + OS.attributeBegin("minimumVersion"); + serialize(OSVersion, OS); + OS.attributeEnd(); + }); + }); +} diff --git a/lib/SymbolGraphGen/JSON.h b/lib/SymbolGraphGen/JSON.h new file mode 100644 index 0000000000000..0c0018da724cb --- /dev/null +++ b/lib/SymbolGraphGen/JSON.h @@ -0,0 +1,45 @@ +//===--- JSON.h - Symbol Graph JSON Helpers -------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_JSON_H +#define SWIFT_SYMBOLGRAPHGEN_JSON_H + +#include "llvm/ADT/Triple.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/AST/GenericSignature.h" + +namespace swift { +namespace symbolgraphgen { + +struct AttributeRAII { + StringRef Key; + llvm::json::OStream &OS; + AttributeRAII(StringRef Key, llvm::json::OStream &OS) + : Key(Key), OS(OS) { + OS.attributeBegin(Key); + } + + ~AttributeRAII() { + OS.attributeEnd(); + } +}; + +void serialize(const llvm::VersionTuple &VT, llvm::json::OStream &OS); +void serialize(const llvm::Triple &T, llvm::json::OStream &OS); + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_JSON_H diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp new file mode 100644 index 0000000000000..441a636454051 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -0,0 +1,380 @@ +//===--- Symbol.cpp - Symbol Graph Node -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/Basic/SourceManager.h" +#include "JSON.h" +#include "Symbol.h" +#include "SymbolGraph.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Symbol::serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("identifier", Identifier); + OS.attribute("displayName", DisplayName); + }); +} + +void Symbol::serializeKind(llvm::json::OStream &OS) const { + AttributeRAII A("kind", OS); + switch (VD->getKind()) { + case swift::DeclKind::Class: + serializeKind("swift.class", "Class", OS); + break; + case swift::DeclKind::Struct: + serializeKind("swift.struct", "Structure", OS); + break; + case swift::DeclKind::Enum: + serializeKind("swift.enum", "Enumeration", OS); + break; + case swift::DeclKind::EnumElement: + serializeKind("swift.enum.case", "Case", OS); + break; + case swift::DeclKind::Protocol: + serializeKind("swift.protocol", "Protocol", OS); + break; + case swift::DeclKind::Constructor: + serializeKind("swift.initializer", "Initializer", OS); + break; + case swift::DeclKind::Func: + serializeKind("swift.function", "Function", OS); + break; + case swift::DeclKind::Var: + serializeKind("swift.variable", "Variable", OS); + break; + case swift::DeclKind::TypeAlias: + serializeKind("swift.typealias", "Type Alias", OS); + break; + case swift::DeclKind::InfixOperator: + serializeKind("swift.infixOperator", "Infix Operator", OS); + break; + case swift::DeclKind::PrefixOperator: + serializeKind("swift.prefixOperator", "Prefix Operator", OS); + break; + case swift::DeclKind::PostfixOperator: + serializeKind("swift.postfixOperator", "Postfix Operator", OS); + break; + default: + llvm_unreachable("Unsupported declaration kind for symbol graph"); + } +} + +void Symbol::serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + AttributeRAII A("identifier", OS); + Walker.getSymbolIdentifier(VD).serialize(OS); +} + +void Symbol::serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("names", [&](){ + auto Identifier = Walker.getSymbolIdentifier(VD); + OS.attribute("title", Identifier.SimpleComponents.back()); + // "navigator": null + Walker.serializeSubheadingDeclarationFragments("subheading", VD, OS); + // "prose": null + }); +} + +void Symbol::serializePosition(StringRef Key, + unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const { + OS.attributeObject(Key, [&](){ + OS.attribute("line", Line); + OS.attribute("character", ByteOffset); + }); +} + +void Symbol::serializeRange(size_t InitialIndentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const { + OS.attributeObject("range", [&](){ + auto StartLineAndColumn = SourceMgr.getLineAndColumn(Range.Start); + auto StartLine = StartLineAndColumn.first; + auto StartColumn = StartLineAndColumn.second + InitialIndentation; + serializePosition("start", StartLine, StartColumn, OS); + + auto EndLineAndColumn = SourceMgr.getLineAndColumn(Range.End); + auto EndLine = EndLineAndColumn.first; + auto EndColumn = EndLineAndColumn.second + InitialIndentation; + serializePosition("end", EndLine, EndColumn, OS); + }); +} + +void Symbol::serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("docComment", [&](){ + auto LL = Walker.Ctx.getLineList(VD->getRawComment()); + size_t InitialIndentation = LL.getLines().empty() + ? 0 + : markup::measureIndentation(LL.getLines().front().Text); + OS.attributeArray("lines", [&](){ + for (const auto &Line : LL.getLines()) { + // Line object + OS.object([&](){ + // Trim off any initial indentation from the line's + // text and start of its source range, if it has one. + if (Line.Range.isValid()) { + serializeRange(InitialIndentation, + Line.Range, Walker.M.getASTContext().SourceMgr, OS); + } + auto TrimmedLine = Line.Text.drop_front(std::min(InitialIndentation, + Line.FirstNonspaceOffset)); + OS.attribute("text", TrimmedLine); + }); + } + }); // end lines: [] + }); // end docComment: +} + +void Symbol::serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *FD = dyn_cast_or_null(VD)) { + OS.attributeObject("functionSignature", [&](){ + + // Parameters + if (const auto *ParamList = FD->getParameters()) { + if (ParamList->size()) { + OS.attributeArray("parameters", [&](){ + for (const auto *Param : *ParamList) { + auto ExternalName = Param->getArgumentName().str(); + auto InternalName = Param->getParameterName().str(); + + OS.object([&](){ + if (ExternalName.empty()) { + OS.attribute("name", InternalName); + } else { + OS.attribute("name", ExternalName); + if (ExternalName != InternalName && + !InternalName.empty()) { + OS.attribute("internalName", InternalName); + } + } + Walker.serializeDeclarationFragments("declarationFragments", + Param, OS); + }); // end parameter object + } + }); // end parameters: + } + } + + // Returns + if (const auto ReturnType = FD->getResultInterfaceType()) { + Walker.serializeDeclarationFragments("returns", ReturnType, OS); + } + }); + } +} + +void Symbol::serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("name", Param.getName().str()); + OS.attribute("index", Param.getIndex()); + OS.attribute("depth", Param.getDepth()); + }); +} + +void Symbol::serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const { + OS.object([&](){ + switch (Req.getKind()) { + case swift::RequirementKind::Conformance: + OS.attribute("kind", "conformance"); + break; + case swift::RequirementKind::Superclass: + OS.attribute("kind", "superclass"); + break; + case swift::RequirementKind::SameType: + OS.attribute("kind", "sameType"); + break; + case swift::RequirementKind::Layout: + return; + } + OS.attribute("lhs", Req.getFirstType()->getString()); + OS.attribute("rhs", Req.getSecondType()->getString()); + }); +} + +void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { + if (const auto *GC = VD->getAsGenericContext()) { + if (const auto Generics = GC->getGenericSignature()) { + + OS.attributeObject("swiftGenerics", [&](){ + if (!Generics->getGenericParams().empty()) { + OS.attributeArray("parameters", [&](){ + for (const auto Param : Generics->getGenericParams()) { + if (const auto *D = Param->getDecl()) { + if (D->isImplicit()) { + continue; + } + } + serializeGenericParam(*Param, OS); + } + }); // end parameters: + } + + if (!Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + + }); // end swiftGenerics: + } + } +} + +void Symbol::serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *Extension + = dyn_cast_or_null(VD->getInnermostDeclContext())) { + OS.attributeObject("swiftExtension", [&](){ + OS.attribute("definedInModule", Walker.M.getNameStr()); + auto Generics = Extension->getGenericSignature(); + if (Generics && !Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + }); // end swiftExtension: + } +} + +void Symbol::serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + Walker.serializeDeclarationFragments("declarationFragments", VD, OS); +} + +void Symbol::serializeAccessLevelMixin(llvm::json::OStream &OS) const { + OS.attribute("accessLevel", getAccessLevelSpelling(VD->getFormalAccess())); +} + +llvm::Optional +Symbol::getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const { + switch (AgnosticKind) { + // SPM- and Swift-specific availability. + case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + return { "SwiftPM" }; + case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: + case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + return { "Swift" }; + // Although these are in the agnostic kinds, they are actually a signal + // that there is either platform-specific or completely platform-agnostic. + // They'll be handled below. + case PlatformAgnosticAvailabilityKind::Deprecated: + case PlatformAgnosticAvailabilityKind::Unavailable: + case PlatformAgnosticAvailabilityKind::None: + break; + } + + // Platform-specific availability. + switch (Kind) { + case swift::PlatformKind::iOS: + return { "iOS" }; + case swift::PlatformKind::OSX: + return { "macOS" }; + case swift::PlatformKind::tvOS: + return { "tvOS" }; + case swift::PlatformKind::watchOS: + return { "watchOS" }; + case swift::PlatformKind::iOSApplicationExtension: + return { "iOSAppExtension" }; + case swift::PlatformKind::OSXApplicationExtension: + return { "macOSAppExtension" }; + case swift::PlatformKind::tvOSApplicationExtension: + return { "tvOSAppExtension" }; + case swift::PlatformKind::watchOSApplicationExtension: + return { "watchOSAppExtension" }; + // Platform-agnostic availability, such as "unconditionally deprecated" + // or "unconditionally obsoleted". + case swift::PlatformKind::none: + return None; + } +} + +void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { + SmallVector Availabilities; + for (const auto *Attr : VD->getAttrs()) { + if (const auto *AvAttr = dyn_cast(Attr)) { + Availabilities.push_back(AvAttr); + } + } + if (Availabilities.empty()) { + return; + } + + OS.attributeArray("availability", [&](){ + for (const auto *AvAttr : Availabilities) { + OS.object([&](){ + auto Domain = getDomain(AvAttr->getPlatformAgnosticAvailability(), + AvAttr->Platform); + if (Domain) { + OS.attribute("domain", *Domain); + } + if (AvAttr->Introduced) { + AttributeRAII Introduced("introduced", OS); + symbolgraphgen::serialize(*AvAttr->Introduced, OS); + } + if (AvAttr->Deprecated) { + AttributeRAII Deprecated("deprecated", OS); + symbolgraphgen::serialize(*AvAttr->Deprecated, OS); + } + if (AvAttr->Obsoleted) { + AttributeRAII Obsoleted("obsoleted", OS); + symbolgraphgen::serialize(*AvAttr->Obsoleted, OS); + } + if (!AvAttr->Message.empty()) { + OS.attribute("message", AvAttr->Message); + } + if (!AvAttr->Rename.empty()) { + OS.attribute("renamed", AvAttr->Rename); + } + if (AvAttr->isUnconditionallyDeprecated()) { + OS.attribute("isUnconditionallyDeprecated", true); + } + if (AvAttr->isUnconditionallyUnavailable()) { + OS.attribute("isUnconditionallyUnavailable", true); + } + }); // end availability object + } + }); // end availability: [] +} + +void Symbol::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + serializeKind(OS); + serializeIdentifier(Walker, OS); + serializeNames(Walker, OS); + serializeDocComment(Walker, OS); + + // "Mixins" + serializeFunctionSignature(Walker, OS); + serializeSwiftGenericMixin(OS); + serializeSwiftExtensionMixin(Walker, OS); + serializeDeclarationFragmentMixin(Walker, OS); + serializeAccessLevelMixin(OS); + serializeAvailabilityMixin(OS); + }); +} diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h new file mode 100644 index 0000000000000..9cdbca5757164 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.h @@ -0,0 +1,128 @@ +//===--- Symbol.h- Symbol Graph Node --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOL_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOL_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Attr.h" +#include "swift/Basic/LLVM.h" +#include "swift/Markup/Markup.h" + +namespace swift { +namespace symbolgraphgen { + +struct AvailabilityDomain; +struct SymbolGraphASTWalker; + +/** + An identifier for a symbol that provides a globally unique identifier suitable for + internal lookups and a locally unique path for human use, such as a URL. + */ +struct SymbolIdentifier { + /** + A string that uniquely identifies a symbol within a module in the event of + ambiguities. A precise identifier need not be human readable. + */ + StringRef PreciseIdentifier; + + /** + The components for a "fully qualified" identifier. + */ + ArrayRef SimpleComponents; + + SymbolIdentifier(llvm::StringRef PreciseIdentifier, + ArrayRef SimpleComponents) + : PreciseIdentifier(PreciseIdentifier), + SimpleComponents(SimpleComponents) { + assert(!PreciseIdentifier.empty()); + } + + void serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("precise", PreciseIdentifier); + OS.attributeArray("simpleComponents", [&](){ + for (auto Component : SimpleComponents) { + OS.value(Component); + } + }); + }); + } + + bool operator==(const SymbolIdentifier &Other) const { + return PreciseIdentifier == Other.PreciseIdentifier && + SimpleComponents == Other.SimpleComponents; + } +}; + +/// A symbol from a module: a node in a graph. +struct Symbol { + const ValueDecl *VD; + + void serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const; + + void serializeKind(llvm::json::OStream &OS) const; + + void serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializePosition(StringRef Key, unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const; + + void serializeRange(size_t InitialIdentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const; + + void serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const; + + void serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const; + + void serializeSwiftGenericMixin(llvm::json::OStream &OS) const; + + void serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeAccessLevelMixin(llvm::json::OStream &OS) const; + + llvm::Optional + getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const; + + void serializeAvailabilityMixin(llvm::json::OStream &OS) const; + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + bool operator==(const Symbol &Other) const { + return VD == Other.VD; + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOL_H diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp new file mode 100644 index 0000000000000..0d41bdcf4a638 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -0,0 +1,68 @@ +//===--- SymbolGraph.cpp - Symbol Graph Data Structure -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "swift/Basic/Version.h" + +#include "FormatVersion.h" +#include "SymbolGraph.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraph::SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion) +: M(M), Target(Target), ModuleVersion(ModuleVersion) {} + +void SymbolGraph::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attributeObject("metadata", [&](){ + { + AttributeRAII FV("formatVersion", OS); + llvm::VersionTuple FormatVersion(SWIFT_SYMBOLGRAPH_FORMAT_MAJOR, + SWIFT_SYMBOLGRAPH_FORMAT_MINOR, + SWIFT_SYMBOLGRAPH_FORMAT_PATCH); + symbolgraphgen::serialize(FormatVersion, OS); + } // end formatVersion: + + auto VersionString = version::getSwiftFullVersion(); + StringRef VersionStringRef(VersionString.c_str(), VersionString.size()); + OS.attribute("generator", VersionStringRef); + }); // end metadata: + + OS.attributeObject("module", [&](){ + OS.attribute("name", M.getNameStr()); + AttributeRAII Platform("platform", OS); + symbolgraphgen::serialize(Target, OS); + }); + + if (ModuleVersion) { + AttributeRAII MV("moduleVersion", OS); + symbolgraphgen::serialize(*ModuleVersion, OS); + } + + OS.attributeArray("symbols", [&](){ + for (const auto *VD: Nodes) { + Symbol S { VD }; + S.serialize(Walker, OS); + } + }); + + OS.attributeArray("relationships", [&](){ + for (const auto Relationship : Edges) { + Relationship.serialize(OS); + } + }); + + }); +} diff --git a/lib/SymbolGraphGen/SymbolGraph.h b/lib/SymbolGraphGen/SymbolGraph.h new file mode 100644 index 0000000000000..1e893780e35f3 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.h @@ -0,0 +1,64 @@ +//===--- SymbolGraph.h - Symbol Graph Data Structure ----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/Basic/LLVM.h" +#include "Edge.h" +#include "JSON.h" + +namespace swift { +namespace symbolgraphgen { + +/// A graph of symbols and the relationships between them. +struct SymbolGraph { + /** + The module this symbol graph represents. + */ + ModuleDecl &M; + + /** + The module's target triple. + */ + llvm::Triple Target; + + /** + The semantic version of the module that this symbol graph describes, + if known. + */ + Optional ModuleVersion; + + /** + The symbols in a module: the nodes in the graph. + */ + llvm::SmallPtrSet Nodes; + + /** + The relationships between symbols: the edges in the graph. + */ + llvm::DenseSet Edges; + + SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion = None); + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp new file mode 100644 index 0000000000000..2ce1337d65468 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -0,0 +1,364 @@ +//===--- SymbolGraphASTWalker.cpp - Symbol Graph AST Walker ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclObjC.h" +#include "llvm/ADT/StringSwitch.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/USRGeneration.h" +#include "swift/Basic/PrimitiveParsing.h" +#include "swift/Markup/Markup.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M, + const SymbolGraphOptions &Options) + : Options(Options), + M(M), + Graph(M, Options.Target) {} + +/// Returns `true` if the symbol should be included as a node in the graph. +bool SymbolGraphASTWalker::shouldIncludeNode(const Decl *D) const { + // If this decl isn't in this module, don't record it, + // as it will appear elsewhere in its module's symbol graph. + if (D->getModuleContext()->getName() != M.getName()) { + return false; + } + + // Implicit declarations are probably not going to have documentation, + // so don't record it in the symbol graph. + if (D->isImplicit()) { + return false; + } + + // At this point, the declaration must be a ValueDecl. + auto VD = cast(D); + + // Don't record unconditionally private declarations + if (VD->isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic=*/false)) { + return false; + } + + // Don't record effectively internal declarations if specified + if (Options.MinimumAccessLevel > AccessLevel::Internal && + VD->hasUnderscoredNaming()) { + return false; + } + + // Symbols must meet the minimum access level to be included in the graph. + if (VD->getFormalAccess() < Options.MinimumAccessLevel) { + return false; + } + + // Special cases + + auto BaseName = VD->getBaseName().userFacingName(); + + // ${MODULE}Version{Number,String} in ${Module}.h + SmallString<32> VersionNameIdentPrefix { M.getName().str() }; + VersionNameIdentPrefix.append("Version"); + + if (BaseName.startswith(VersionNameIdentPrefix.str())) { + return false; + } + + // Automatically mapped SIMD types + bool ShouldInclude = llvm::StringSwitch(BaseName) +#define MAP_SIMD_TYPE(C_TYPE, _, __) \ + .Case("swift_" #C_TYPE "2", false) \ + .Case("swift_" #C_TYPE "3", false) \ + .Case("swift_" #C_TYPE "4", false) +#include "swift/ClangImporter/SIMDMappedTypes.def" + .Case("SWIFT_TYPEDEFS", false) + .Case("char16_t", false) + .Case("char32_t", false) + .Default(true); + + return ShouldInclude; +} + +bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) { + + switch (D->getKind()) { + // We'll record nodes for the following kinds of declarations. + case swift::DeclKind::Class: + case swift::DeclKind::Struct: + case swift::DeclKind::Enum: + case swift::DeclKind::EnumElement: + case swift::DeclKind::Protocol: + case swift::DeclKind::Constructor: + case swift::DeclKind::Func: + case swift::DeclKind::Var: + case swift::DeclKind::TypeAlias: + break; + + // We'll descend into everything else. + default: + return true; + } + + if (!shouldIncludeNode(D)) { + return false; + } + + auto *VD = cast(D); + Graph.Nodes.insert(VD); + + // Record all of the possible relationships (edges) originating + // with this declaration. + recordMemberRelationship(VD); + recordConformanceRelationships(VD); + recordInheritanceRelationships(VD); + recordDefaultImplementationRelationships(VD); + recordOverrideRelationship(VD); + recordRequirementRelationships(VD); + recordOptionalRequirementRelationships(VD); + + return true; +} + +StringRef SymbolGraphASTWalker::getUSR(const ValueDecl *VD) { + auto Found = USRCache.find(VD); + if (Found != USRCache.end()) { + return Found->second; + } + llvm::SmallString<32> Scratch; + llvm::raw_svector_ostream OS(Scratch); + ide::printDeclUSR(VD, OS); + auto USR = Ctx.allocateCopy(Scratch.str()); + USRCache.insert({VD, USR}); + return USR; +} + +SymbolIdentifier +SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { + // Look in the symbol identifier cache for this declartion. + auto Found = SymbolIdentifierCache.find(VD); + if (Found != SymbolIdentifierCache.end()) { + return Found->getSecond(); + } + + // Not found; need to build a symbol identifier and add it to the cache. + auto PreciseIdentifier = getUSR(VD); + llvm::SmallVector SimpleIdentifierChain; + + // Collect the spellings of the fully qualified identifier components. + auto Decl = VD; + while (Decl && !isa(Decl)) { + SmallString<32> Scratch; + Decl->getFullName().getString(Scratch); + SimpleIdentifierChain.push_back(Ctx.allocateCopy(Scratch.str())); + if (const auto *DC = Decl->getDeclContext()) { + if (const auto *Proto = DC->getExtendedProtocolDecl()) { + Decl = Proto; + } else if (const auto *Ext = dyn_cast_or_null(DC->getAsDecl())) { + Decl = Ext->getExtendedNominal(); + } else { + Decl = dyn_cast_or_null(DC->getAsDecl()); + } + } else { + Decl = nullptr; + } + } + + // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. + std::reverse(SimpleIdentifierChain.begin(), SimpleIdentifierChain.end()); + + SymbolIdentifier Identifier { + PreciseIdentifier, + Ctx.allocateCopy(llvm::makeArrayRef(SimpleIdentifierChain)) + }; + + SymbolIdentifierCache.insert({VD, Identifier}); + return Identifier; +} + +PrintOptions SymbolGraphASTWalker::getDeclarationFragmentsPrintOptions() const { + PrintOptions Opts; + Opts.FunctionDefinitions = false; + Opts.ArgAndParamPrinting = + PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; + Opts.PrintGetSetOnRWProperties = false; + Opts.PrintPropertyAccessors = false; + Opts.PrintSubscriptAccessors = false; + Opts.SkipUnderscoredKeywords = true; + Opts.SkipAttributes = true; + Opts.PrintOverrideKeyword = true; + Opts.PrintImplicitAttrs = false; + Opts.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; + Opts.PrintUserInaccessibleAttrs = false; + Opts.SkipPrivateStdlibDecls = true; + Opts.SkipUnderscoredStdlibProtocols = true; + + Opts.ExclusiveAttrList.clear(); + +#define DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) Opts.ExcludeAttrList.push_back(DAK_##CLASS); +#define TYPE_ATTR(X) Opts.ExcludeAttrList.push_back(TAK_##X); +#include "swift/AST/Attr.def" + + return Opts; +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + VD->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void +SymbolGraphASTWalker::serializeSubheadingDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + auto Options = getDeclarationFragmentsPrintOptions(); + Options.VarInitializers = false; + Options.PrintDefaultArgumentValue = false; + Options.PrintEmptyArgumentNames = false; + Options.PrintOverrideKeyword = false; + VD->print(Printer, Options); +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + T->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void SymbolGraphASTWalker::recordEdge(const ValueDecl *Source, + const ValueDecl *Target, + RelationshipKind Kind) { + if (Target->isPrivateStdlibDecl( + /*treatNonBuiltinProtocolsAsPublic = */false)) { + return; + } + + // There might be relationships on implicit declarations, + // such as overriding implicit @objc init(). + if (Target->isImplicit()) { + return; + } + + Graph.Nodes.insert(Source); + Graph.Nodes.insert(Target); + + Graph.Edges.insert({this, Kind, Source, Target}); +} + +void SymbolGraphASTWalker::recordMemberRelationship(const ValueDecl *VD) { + auto *DC = VD->getDeclContext(); + switch (DC->getContextKind()) { + case DeclContextKind::GenericTypeDecl: + case DeclContextKind::ExtensionDecl: + case swift::DeclContextKind::EnumElementDecl: + return recordEdge(VD, VD->getDeclContext()->getSelfNominalTypeDecl(), + RelationshipKind::MemberOf()); + case swift::DeclContextKind::AbstractClosureExpr: + case swift::DeclContextKind::Initializer: + case swift::DeclContextKind::TopLevelCodeDecl: + case swift::DeclContextKind::SubscriptDecl: + case swift::DeclContextKind::AbstractFunctionDecl: + case swift::DeclContextKind::SerializedLocal: + case swift::DeclContextKind::Module: + case swift::DeclContextKind::FileUnit: + break; + } +} + +void +SymbolGraphASTWalker::recordInheritanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto &InheritanceLoc : NTD->getInherited()) { + auto Ty = InheritanceLoc.getType(); + if (!Ty) { + continue; + } + auto *InheritedTypeDecl = + dyn_cast_or_null(Ty->getAnyNominal()); + if (!InheritedTypeDecl) { + continue; + } + + recordEdge(VD, InheritedTypeDecl, RelationshipKind::InheritsFrom()); + } + } +} + +void SymbolGraphASTWalker::recordDefaultImplementationRelationships( + const ValueDecl *VD) { + if (const auto *Extension = dyn_cast(VD->getDeclContext())) { + if (const auto *Protocol = Extension->getExtendedProtocolDecl()) { + for (const auto *Member : Protocol->getMembers()) { + if (const auto *MemberVD = dyn_cast(Member)) { + if (MemberVD->getFullName().compare(VD->getFullName()) == 0) { + recordEdge(VD, MemberVD, + RelationshipKind::DefaultImplementationOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordRequirementRelationships(const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + recordEdge(VD, Protocol, RelationshipKind::RequirementOf()); + } + } +} + +void SymbolGraphASTWalker::recordOptionalRequirementRelationships( + const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + if (const auto *ClangDecl = VD->getClangDecl()) { + if (const auto *Method = dyn_cast(ClangDecl)) { + if (Method->isOptional()) { + recordEdge(VD, Protocol, + RelationshipKind::OptionalRequirementOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordConformanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto *Conformance : NTD->getAllConformances()) { + recordEdge(VD, Conformance->getProtocol(), + RelationshipKind::ConformsTo()); + } + } +} + +void SymbolGraphASTWalker::recordOverrideRelationship(const ValueDecl *VD) { + if (const auto *Override = VD->getOverriddenDecl()) { + recordEdge(VD, Override, RelationshipKind::Overrides()); + } +} diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h new file mode 100644 index 0000000000000..fb3e3dc9b6d0d --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -0,0 +1,161 @@ +//===--- SymbolGraphASTWalker.h - Symbol Graph AST Walker -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H + +#include "swift/Basic/LLVM.h" +#include "swift/IDE/SourceEntityWalker.h" +#include "swift/Markup/Markup.h" + +#include "SymbolGraph.h" + +namespace swift { + +class Decl; +class Type; +class ValueDecl; + +namespace symbolgraphgen { + +struct SymbolIdentifier; +struct SymbolGraph; +struct SymbolGraphOptions; + +/** + The `SymbolGraphASTWalker` is the core implementation that builds a + symbol graph. It walks a module for declarations, recording facts about + symbols and relationships between them. + */ +struct SymbolGraphASTWalker : public SourceEntityWalker { + /// Options for collecting and serialization. + const SymbolGraphOptions &Options; + + /// The module that this symbol graph will represent. + const ModuleDecl &M; + + /// The symbol graph. + SymbolGraph Graph; + + /// A context for allocations. + markup::MarkupContext Ctx; + + /// A cache of identifiers for declarations that may be seen more than once. + llvm::DenseMap SymbolIdentifierCache; + + /// A cache of USRs for declarations. + llvm::DenseMap USRCache; + + // MARK: - + + SymbolGraphASTWalker(ModuleDecl &M, const SymbolGraphOptions &Options); + virtual ~SymbolGraphASTWalker() {} + + // MARK: - + + /// Returns `true` if the symbol should be included as a node in the graph. + bool shouldIncludeNode(const Decl *D) const; + + virtual bool walkToDeclPre(Decl *D, CharSourceRange Range); + + // MARK: - Utilities and Conversions + + /// Get the USR of a declaration and add it to the local allocator. + StringRef getUSR(const ValueDecl *VD); + + /// Returns a `SymbolIdentifier` for a given declaration. + SymbolIdentifier getSymbolIdentifier(const ValueDecl *VD); + + // MARK: - Declaration Fragments + + /// Get the base print options for declaration fragments. + PrintOptions getDeclarationFragmentsPrintOptions() const; + + /// Serialize the overall declaration fragments for a `ValueDecl`. + void + serializeDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration fragments for a `ValueDecl` when it is viewed + /// as a subheading and/or part of a larger group of symbol listings. + void + serializeSubheadingDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration for a type declaration. + void + serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS); + + // MARK: - Relationships (Edges) + + /** + Record a relationship between two declarations as an edge in the graph. + + \param Source The declaration serving as the source of the edge in the + directed graph. + \param Target The declaration serving as the target of the edge in the + directed graph. + \param Kind The kind of relationship the edge represents. + */ + void recordEdge(const ValueDecl *Source, const ValueDecl *Target, + RelationshipKind Kind); + + /** + Record a MemberOf relationship, if the given declaration is nested + in another. + */ + void recordMemberRelationship(const ValueDecl *VD); + + /** + Record InheritsFrom relationships for every class from which the + declaration inherits. + */ + void recordInheritanceRelationships(const ValueDecl *VD); + + /** + If the declaration is a default implementation in a protocol extension, + record a DefaultImplementationOf relationship between the declaration and + the requirement. + */ + void recordDefaultImplementationRelationships(const ValueDecl *VD); + + /** + Record a RequirementOf relationship if the declaration is a requirement + of a protocol. + */ + void recordRequirementRelationships(const ValueDecl *VD); + + /** + If the declaration is an Objective-C-based optional protocol requirement, + record an OptionalRequirementOf relationship between the declaration + and its containing protocol. + */ + void recordOptionalRequirementRelationships(const ValueDecl *VD); + + /** + Record ConformsTo relationships for each protocol conformance of + the declaration. + */ + void recordConformanceRelationships(const ValueDecl *VD); + + /** + Records an Overrides relationship if the given declaration + overrides another. + */ + void recordOverrideRelationship(const ValueDecl *VD); +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H diff --git a/lib/SymbolGraphGen/SymbolGraphGen.cpp b/lib/SymbolGraphGen/SymbolGraphGen.cpp new file mode 100644 index 0000000000000..667d7b3e328c4 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphGen.cpp @@ -0,0 +1,56 @@ +//===--- SymbolGraphGen.cpp - Symbol Graph Generator Entry Point ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" +#include "swift/AST/Module.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +// MARK: - Main Entry Point + +/// Emit a symbol graph JSON file for a `ModuleDecl`. +int +symbolgraphgen::emitSymbolGraphForModule(ModuleDecl *M, + const SymbolGraphOptions &Options) { + SymbolGraphASTWalker Walker(*M, Options); + SmallVector ModuleDecls; + M->getDisplayDecls(ModuleDecls); + + llvm::errs() << ModuleDecls.size() + << " top-level declarations in this module.\n"; + + for (auto *Decl : ModuleDecls) { + Walker.walk(Decl); + } + + llvm::errs() + << "Found " << Walker.Graph.Nodes.size() << " symbols and " + << Walker.Graph.Edges.size() << " relationships.\n"; + + std::error_code Error; + llvm::raw_fd_ostream OS(Options.OutputPath, Error, llvm::sys::fs::FA_Write); + if (Error) { + llvm::errs() << "Couldn't open output file for writing: " + << Error.message() << "\n"; + return EXIT_FAILURE; + } + + llvm::json::OStream J(OS, Options.PrettyPrint ? 2 : 0); + Walker.Graph.serialize(Walker, J); + + return EXIT_SUCCESS; +} diff --git a/test/IDE/comment_brief.swift b/test/IDE/comment_brief.swift index 1409f3e9fd36f..db447aaca1807 100644 --- a/test/IDE/comment_brief.swift +++ b/test/IDE/comment_brief.swift @@ -145,8 +145,8 @@ struct Indentation { // CHECK-NEXT: Func/briefBlockWithASCIIArt5 {{.*}} BriefComment=[Aaa. Bbb. *Ccc.] // CHECK-NEXT: Func/briefBlockWithASCIIArt6 {{.*}} BriefComment=[Aaa.] // CHECK-NEXT: Func/briefMixed1 {{.*}} BriefComment=[Aaa. Bbb.] -// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa.] -// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa.] +// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa. Bbb.] +// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa. Bbb.] // CHECK-NEXT: Struct/Indentation RawComment=none // CHECK-NEXT: Func/Indentation.briefBlockWithASCIIArt1 {{.*}} BriefComment=[Aaa.] diff --git a/test/SymbolGraph/Module.swift b/test/SymbolGraph/Module.swift new file mode 100644 index 0000000000000..dcfba01e9a43b --- /dev/null +++ b/test/SymbolGraph/Module.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SymbolGraphModule -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SymbolGraphModule -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: module +// CHECK-NEXT: "name": "SymbolGraphModule" +// CHECK-NEXT: platform +// CHECK-NEXT: architecture +// CHECK: vendor +// CHECK-NEXT: operatingSystem +// CHECK-NEXT: name +// CHECK-NEXT: minimumVersion +// CHECK-NEXT: major +// CHECK-NEXT: minor +// CHECK-NEXT: patch diff --git a/test/SymbolGraph/Relationships/ConformsTo.swift b/test/SymbolGraph/Relationships/ConformsTo.swift new file mode 100644 index 0000000000000..c895fd5f7bfc6 --- /dev/null +++ b/test/SymbolGraph/Relationships/ConformsTo.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +public struct S: P { + public var x: Int +} + +// CHECK: "kind": "conformsTo" +// CHECK-NEXT: "source": "s:10ConformsTo1SV" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/DefaultImplementationOf.swift b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift new file mode 100644 index 0000000000000..3193cfdf15200 --- /dev/null +++ b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DefaultImplementationOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DefaultImplementationOf -I %t -pretty-print -o %t/DefaultImplementationOf.symbols.json +// RUN: %FileCheck %s --input-file %t/DefaultImplementationOf.symbols.json + +public protocol P { + var x: Int { get } +} + +extension P { + public var x: Int { + return 2 + } +} + +// CHECK: "kind": "defaultImplementationOf", +// CHECK-NEXT: "source": "s:23DefaultImplementationOf1PPAAE1xSivp", +// CHECK-NEXT: "target": "s:23DefaultImplementationOf1PP1xSivp" diff --git a/test/SymbolGraph/Relationships/InheritsFrom.swift b/test/SymbolGraph/Relationships/InheritsFrom.swift new file mode 100644 index 0000000000000..feb9c4f9ec61b --- /dev/null +++ b/test/SymbolGraph/Relationships/InheritsFrom.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name InheritsFrom -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name InheritsFrom -I %t -pretty-print -o %t/InheritsFrom.symbols.json +// RUN: %FileCheck %s --input-file %t/InheritsFrom.symbols.json + +public class Base {} +public class Derived: Base {} + +// CHECK: "kind": "inheritsFrom" +// CHECK-NEXT: "source": "s:12InheritsFrom7DerivedC" +// CHECK-NEXT: "target": "s:12InheritsFrom4BaseC" diff --git a/test/SymbolGraph/Relationships/MemberOf.swift b/test/SymbolGraph/Relationships/MemberOf.swift new file mode 100644 index 0000000000000..f338e74aff62f --- /dev/null +++ b/test/SymbolGraph/Relationships/MemberOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MemberOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MemberOf -I %t -pretty-print -o %t/MemberOf.symbols.json +// RUN: %FileCheck %s --input-file %t/MemberOf.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: "kind": "memberOf" +// CHECK-NEXT: "source": "s:8MemberOf1SV1xSivp" +// CHECK-NEXT: "target": "s:8MemberOf1SV" diff --git a/test/SymbolGraph/Relationships/Overrides.swift b/test/SymbolGraph/Relationships/Overrides.swift new file mode 100644 index 0000000000000..85d2f7afb57f2 --- /dev/null +++ b/test/SymbolGraph/Relationships/Overrides.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Overrides -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Overrides -I %t -pretty-print -o %t/Overrides.symbols.json +// RUN: %FileCheck %s --input-file %t/Overrides.symbols.json + +public class Base { + public var x: Int { + return 1 + } +} + +public class Derived: Base { + public override var x: Int { + return 2 + } +} + +// CHECK: "kind": "overrides" +// CHECK-NEXT: "source": "s:9Overrides7DerivedC1xSivp" +// CHECK-NEXT: "target": "s:9Overrides4BaseC1xSivp" diff --git a/test/SymbolGraph/Relationships/RequirementOf.swift b/test/SymbolGraph/Relationships/RequirementOf.swift new file mode 100644 index 0000000000000..a06507b523b7e --- /dev/null +++ b/test/SymbolGraph/Relationships/RequirementOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +// CHECK: "kind": "requirementOf" +// CHECK-NEXT: "source": "s:10ConformsTo1PP1xSivp" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/TargetFallback.swift b/test/SymbolGraph/Relationships/TargetFallback.swift new file mode 100644 index 0000000000000..274c423d0f748 --- /dev/null +++ b/test/SymbolGraph/Relationships/TargetFallback.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name TargetFallback -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name TargetFallback -I %t -pretty-print -o %t/TargetFallback.symbols.json +// RUN: %FileCheck %s --input-file %t/TargetFallback.symbols.json + +public struct S: CustomStringConvertible { + public var x: Int + public var description: String { + return x.description + } +} + +// CHECK: "kind": "conformsTo", +// CHECK-NEXT: "source": "s:14TargetFallback1SV", +// CHECK-NEXT: "target": "s:s23CustomStringConvertibleP", +// CHECK-NEXT: "targetFallback": "Swift.CustomStringConvertible" diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift new file mode 100644 index 0000000000000..eb6e3bb15633f --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IncludeInternal -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IncludeInternal -I %t -pretty-print -o %t/IncludeInternal.symbols.json -minimum-access-level internal +// RUN: %FileCheck %s --input-file %t/IncludeInternal.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldAlsoAppear { + internal var x: Int +} + +private struct ShouldntAppear { + var x: Int +} + +// CHECK: ShouldAppear +// CHECK: ShouldAlsoAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift new file mode 100644 index 0000000000000..258b29058f1c1 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name PublicDefault -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name PublicDefault -I %t -pretty-print -o %t/PublicDefault.symbols.json +// RUN: %FileCheck %s --input-file %t/PublicDefault.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldntAppear { + internal var x: Int +} + +// CHECK: ShouldAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevels.swift b/test/SymbolGraph/Symbols/AccessLevels.swift new file mode 100644 index 0000000000000..e7d3daec46132 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevels.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name AccessLevels -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name AccessLevels -I %t -pretty-print -o %t/AccessLevels.symbols.json +// RUN: %FileCheck %s --input-file %t/AccessLevels.symbols.json + +// CHECK: "accessLevel": "public" + +public struct PublicStruct { + public var x: Int +} + +// CHECK-NOT: "accessLevel": "private" diff --git a/test/SymbolGraph/Symbols/DocComment.swift b/test/SymbolGraph/Symbols/DocComment.swift new file mode 100644 index 0000000000000..b9c07d2df1548 --- /dev/null +++ b/test/SymbolGraph/Symbols/DocComment.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DocComment -emit-module-path %t/DocComment.swiftmodule +// RUN: %target-swift-symbolgraph-extract -module-name DocComment -I %t -pretty-print -o %t/DocComment.symbols.json +// RUN: %FileCheck %s --input-file %t/DocComment.symbols.json + +// CHECK: "text": "Single line." + +/// Single line. +public struct S1 {} + +// CHECK: "text": "Two" +// CHECK: "text": "lines." + +/// Two +/// lines. +public struct S2 {} + +// CHECK: "text": "There are" +// CHECK: "text": "three lines" +// CHECK: "text": "here." + +/// There are +/// three lines +/// here. +public struct S3 {} + +// CHECK: "text": "Comment" +// CHECK: "text": "block." + +/** + Comment + block. +*/ +public struct S4 {} + +// CHECK: "text": "Comment block" +// CHECK: "text": "with more indentation." + +/** + Comment block + with more indentation. + */ +public struct S5 {} + +// CHECK: "text": "With" +// CHECK: "text": "ASCII" +// CHECK: "text": "art." + +/** + * With + * ASCII + * art. + */ +public struct S6 {} diff --git a/test/SymbolGraph/Symbols/Kinds.swift b/test/SymbolGraph/Symbols/Kinds.swift new file mode 100644 index 0000000000000..ec79235016659 --- /dev/null +++ b/test/SymbolGraph/Symbols/Kinds.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Kinds -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Kinds -I %t -pretty-print -o %t/Kinds.symbols.json +// RUN: %FileCheck %s --input-file %t/Kinds.symbols.json + +// CHECK: "identifier": "swift.class" +// CHECK-NEXT: "displayName": "Class" +public class C {} + +// CHECK: "identifier": "swift.struct" +// CHECK-NEXT: "displayName": "Structure" +public struct S { + // CHECK: "identifier": "swift.variable" + // CHECK-NEXT: "displayName": "Variable" + public var x: Int + + // CHECK: "identifier": "swift.initializer" + // CHECK-NEXT: "displayName": "Initializer" + public init(x: Int) { + self.x = x + } + + // CHECK: "identifier": "swift.function" + // CHECK-NEXT: "displayName": "Function" + public func foo() {} +} + +// CHECK: "identifier": "swift.enum" +// CHECK-NEXT: "displayName": "Enumeration" +public enum E { + // CHECK: "identifier": "swift.enum.case" + // CHECK-NEXT: "displayName": "Case" + case oneCase +} + +// CHECK: "identifier": "swift.protocol" +// CHECK-NEXT: "displayName": "Protocol" +public protocol P {} + +// CHECK: "identifier": "swift.typealias" +// CHECK-NEXT: "displayName": "Type Alias" +public typealias Alias = S diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift new file mode 100644 index 0000000000000..d380b84e5d766 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Availability -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Availability -I %t -pretty-print -o %t/Availability.symbols.json +// RUN: %FileCheck %s --input-file %t/Availability.symbols.json + +@available(macOS, introduced: 10.9, deprecated: 10.10, obsoleted: 10.11, message: "Everyone makes mistakes", renamed: "S2") +public struct S {} + +// CHECK: "domain": "macOS" + +// CHECK: introduced +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 9 + +// CHECK: deprecated +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 10 + +// CHECK: obsoleted +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 11 + +// CHECK: "message": "Everyone makes mistakes" +// CHECK: "renamed": "S2" diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift new file mode 100644 index 0000000000000..96488cbc63d20 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyDeprecated -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyDeprecated -I %t -pretty-print -o %t/UnconditionallyDeprecated.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyDeprecated.symbols.json + +@available(*, deprecated) +public struct UnconditionallyDeprecated {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyDeprecated": true diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift new file mode 100644 index 0000000000000..9fdcb6fa779f9 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyUnavailable -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyUnavailable -I %t -pretty-print -o %t/UnconditionallyUnavailable.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyUnavailable.symbols.json + +@available(*, unavailable) +public struct UnconditionallyUnavailable {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyUnavailable": true diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift new file mode 100644 index 0000000000000..9f35f6fde8b5e --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeclarationFragments -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeclarationFragments -I %t -pretty-print -o %t/DeclarationFragments.symbols.json +// RUN: %FileCheck %s --input-file %t/DeclarationFragments.symbols.json + +public func foo(f: @escaping () -> (), x: Int = 2, s: S) {} + +// CHECK: declarationFragments + +// CHECK: "kind": "keyword", +// CHECK-NEXT: "spelling": "func" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "foo" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": "<" + +// CHECK: "kind": "genericParameter", +// CHECK-NEXT: "spelling": "S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ">(" + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "f" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": () -> (), " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "x" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": " + +// CHECK: "kind": "typeIdentifier", +// CHECK-NEXT: "spelling": "Int", +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " = 2, " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "s" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ")" diff --git a/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift new file mode 100644 index 0000000000000..ae2a4bd6c441a --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name FunctionSignature -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name FunctionSignature -I %t -pretty-print -o %t/FunctionSignature.symbols.json +// RUN: %FileCheck %s --input-file %t/FunctionSignature.symbols.json + +public func foo(_ noext: Int, ext int: Int) -> String { + return "OK" +} + +// CHECK: "name": "noext" +// CHECK-NOT: "internalName": "noext" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "noext" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "name": "ext" +// CHECK-NEXT: "internalName": "int" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "int" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: returns +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "String" +// CHECK-NEXT: "preciseIdentifier": "s:SS" diff --git a/test/SymbolGraph/Symbols/Names.swift b/test/SymbolGraph/Symbols/Names.swift new file mode 100644 index 0000000000000..4ea658167d919 --- /dev/null +++ b/test/SymbolGraph/Symbols/Names.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Names -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Names -I %t -pretty-print -o %t/Names.symbols.json +// RUN: %FileCheck %s --input-file %t/Names.symbols.json + +public struct MyStruct {} + +// CHECK: names +// CHECK-NEXT: "title": "MyStruct" diff --git a/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift new file mode 100644 index 0000000000000..e62567f069c4c --- /dev/null +++ b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SkipsPublicUnderscore -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SkipsPublicUnderscore -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct _ShouldntAppear { + public var shouldntAppear: Int +} + +// CHECK-NOT: _ShouldntAppear +// CHECK-NOT: shouldntAppear diff --git a/test/lit.cfg b/test/lit.cfg index 1b43328020d69..9ea2cb9851a07 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -275,6 +275,7 @@ if 'syntax_parser_lib' in config.available_features: config.swift_reflection_dump = inferSwiftBinary('swift-reflection-dump') config.swift_remoteast_test = inferSwiftBinary('swift-remoteast-test') config.swift_indent = inferSwiftBinary('swift-indent') +config.swift_symbolgraph_extract = inferSwiftBinary('swift-symbolgraph-extract') config.clang = inferSwiftBinary('clang') config.llvm_link = inferSwiftBinary('llvm-link') config.swift_llvm_opt = inferSwiftBinary('swift-llvm-opt') @@ -904,6 +905,9 @@ if run_vendor == 'apple': config.target_sil_opt = ( "%s %s %s %s" % (xcrun_prefix, config.sil_opt, target_options, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + "%s %s %s" % + (xcrun_prefix, config.swift_symbolgraph_extract, target_options)) config.target_swift_ide_test = ( "%s %s %s %s" % (xcrun_prefix, config.swift_ide_test, target_options, ccp_opt)) @@ -975,6 +979,10 @@ elif run_os in ['windows-msvc']: ('%r -target %s %s %s %s' % (config.sil_opt, config.variant_triple, \ resource_dir_opt, mcp_opt, \ config.sil_test_options)) + config.target_swift_symbol_graph_extract = \ + ('%r -target %s %s' % (config.swift_symbolgraph_extract, \ + config.variant_triple, \ + mcp_opt)) config.target_swift_ide_test = \ ('%r -target %s %s %s %s' % (config.swift_ide_test, \ config.variant_triple, \ @@ -1068,6 +1076,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_sil_opt = ( '%s -target %s %s %s %s' % (config.sil_opt, config.variant_triple, resource_dir_opt, mcp_opt, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + '%s -target %s %s' % + (config.swift_symbolgraph_extract, config.variant_triple, mcp_opt)) config.target_swift_ide_test = ( '%s -target %s %s %s %s' % (config.swift_ide_test, config.variant_triple, resource_dir_opt, @@ -1185,6 +1196,10 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': '-target', config.variant_triple, android_include_paths_opt, resource_dir_opt, mcp_opt, config.sil_test_options]) + config.target_swift_symbolgraph_extract = ' '.join([ + config.swift_symbolgraph_extract, + '-target', config.variant_triple, + mcp_opt]) config.target_swift_ide_test = ' '.join([ config.swift_ide_test, '-target', config.variant_triple, @@ -1613,6 +1628,8 @@ config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)', swift_version)))) config.substitutions.append(('%target-swift-ide-test', "%s -swift-version %s" % (config.target_swift_ide_test, swift_version))) +config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract)) + if not hasattr(config, 'target_swift_reflection_test'): config.target_swift_reflection_test = inferSwiftBinary(swift_reflection_test_name) diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 39aeebb29bafd..2b5eb14dc0d7a 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -3,12 +3,14 @@ add_swift_host_tool(swift autolink_extract_main.cpp modulewrap_main.cpp swift_indent_main.cpp + swift_symbolgraph_extract_main.cpp SWIFT_COMPONENT compiler ) target_link_libraries(swift PRIVATE swiftDriver - swiftFrontendTool) + swiftFrontendTool + swiftSymbolGraphGen) swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" @@ -20,6 +22,11 @@ swift_create_post_build_symlink(swift DESTINATION "swift-indent${CMAKE_EXECUTABLE_SUFFIX}" WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") +swift_create_post_build_symlink(swift + SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") + swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "swift-autolink-extract${CMAKE_EXECUTABLE_SUFFIX}" @@ -28,6 +35,7 @@ swift_create_post_build_symlink(swift add_swift_tool_symlink(swiftc swift compiler) add_swift_tool_symlink(swift-autolink-extract swift autolink-driver) add_swift_tool_symlink(swift-indent swift editor-integration) +add_swift_tool_symlink(swift-symbolgraph-extract swift toolchain-tools) # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) @@ -46,3 +54,7 @@ add_dependencies(editor-integration swift) swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-indent${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "bin" COMPONENT editor-integration) +add_dependencies(toolchain-tools swift) +swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "bin" + COMPONENT toolchain-tools) diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 0424977e4012a..17a451c76a051 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -70,6 +70,10 @@ extern int modulewrap_main(ArrayRef Args, const char *Argv0, extern int swift_indent_main(ArrayRef Args, const char *Argv0, void *MainAddr); +/// Run 'swift-symbolgraph-extract' +extern int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, +void *MainAddr); + /// Determine if the given invocation should run as a subcommand. /// /// \param ExecName The name of the argv[0] we were invoked as. @@ -152,6 +156,8 @@ static int run_driver(StringRef ExecName, return swift_indent_main( TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::SymbolGraph: + return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); default: break; } diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/tools/driver/swift_symbolgraph_extract_main.cpp new file mode 100644 index 0000000000000..ef01acf5c3e58 --- /dev/null +++ b/tools/driver/swift_symbolgraph_extract_main.cpp @@ -0,0 +1,177 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Extracts a Symbol Graph from a .swiftmodule file. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/Version.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" + +using namespace swift; +using namespace llvm::opt; + +namespace options { +static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options"); + +static llvm::cl::opt +ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category)); + +static llvm::cl::list +FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +LibrarySearchPaths("L", llvm::cl::desc("Add a directory to the library search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +ImportSearchPaths("I", llvm::cl::desc("Add directory to the import search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::opt +ModuleCachePath("module-cache-path", llvm::cl::desc("Specifies a path to cache modules"), llvm::cl::cat(Category)); + +static llvm::cl::opt +SDK("sdk", llvm::cl::desc("Path to the SDK"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +Target("target", llvm::cl::desc("Target triple"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +SwiftVersion("swift-version", llvm::cl::desc("Interpret input according to a specific Swift language version number"), llvm::cl::cat(Category)); + +static llvm::cl::opt +PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Graph JSON"), llvm::cl::cat(Category)); + +static llvm::cl::opt +MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category)); + +static llvm::cl::opt +OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category)); +} // end namespace options + +int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, void *MainAddr) { + INITIALIZE_LLVM(); + + llvm::cl::HideUnrelatedOptions(options::Category); + + // LLVM Command Line expects to trim off argv[0]. + SmallVector ArgsWithArgv0 { Argv0 }; + ArgsWithArgv0.append(Args.begin(), Args.end()); + + llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(), + llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n"); + + CompilerInvocation Invocation; + + Invocation.setMainExecutablePath( + llvm::sys::fs::getMainExecutable(Argv0, MainAddr)); + Invocation.setModuleName("swift_symbolgraph_extract"); + Invocation.setSDKPath(options::SDK); + Invocation.setTargetTriple(options::Target); + + std::vector FrameworkSearchPaths; + for (const auto &Path : options::FrameworkSearchPaths) { + FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false}); + } + Invocation.setFrameworkSearchPaths(FrameworkSearchPaths); + Invocation.getSearchPathOptions().LibrarySearchPaths = options::LibrarySearchPaths; + Invocation.setImportSearchPaths(options::ImportSearchPaths); + + Invocation.getLangOptions().EnableObjCInterop = llvm::Triple(options::Target).isOSDarwin(); + Invocation.getLangOptions().DebuggerSupport = true; + + Invocation.getFrontendOptions().EnableLibraryEvolution = true; + + Invocation.setClangModuleCachePath(options::ModuleCachePath); + Invocation.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath; + + if (!options::SwiftVersion.empty()) { + using version::Version; + bool isValid = false; + if (auto Version = Version::parseVersionString(options::SwiftVersion, + SourceLoc(), nullptr)) { + if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) { + Invocation.getLangOptions().EffectiveLanguageVersion = *Effective; + isValid = true; + } + } + if (!isValid) { + llvm::errs() << "Unsupported Swift Version.\n"; + return EXIT_FAILURE; + } + } + + symbolgraphgen::SymbolGraphOptions Options { + options::OutputPath, + llvm::Triple(options::Target), + options::PrettyPrint, + AccessLevel::Public, + }; + + if (!options::MinimumAccessLevel.empty()) { + Options.MinimumAccessLevel = + llvm::StringSwitch(options::MinimumAccessLevel) + .Case("open", AccessLevel::Open) + .Case("public", AccessLevel::Public) + .Case("internal", AccessLevel::Internal) + .Case("fileprivate", AccessLevel::FilePrivate) + .Case("private", AccessLevel::Private) + .Default(AccessLevel::Public); + } + + PrintingDiagnosticConsumer DiagPrinter; + + CompilerInstance CI; + CI.getDiags().addConsumer(DiagPrinter); + + if (CI.setup(Invocation)) { + llvm::outs() << "Failed to setup compiler instance\n"; + return EXIT_FAILURE; + } + + auto M = CI.getASTContext().getModuleByName(options::ModuleName); + if (!M) { + llvm::errs() + << "Couldn't load module '" << options::ModuleName << '\'' + << " in the current SDK and search paths.\n"; + SmallVector VisibleModuleNames; + CI.getASTContext().getVisibleTopLevelModuleNames(VisibleModuleNames); + + if (VisibleModuleNames.empty()) { + llvm::errs() << "Could not find any modules.\n"; + } else { + std::sort(VisibleModuleNames.begin(), VisibleModuleNames.end(), + [](const Identifier &A, const Identifier &B) -> bool { + return A.str() < B.str(); + }); + llvm::errs() << "Current visible modules:\n"; + for (const auto &ModuleName : VisibleModuleNames) { + llvm::errs() << ModuleName.str() << "\n"; + } + } + return EXIT_FAILURE; + } + + return symbolgraphgen::emitSymbolGraphForModule(M, + Options); +}