//===--- ParseableInterfaceSupport.cpp - swiftinterface files ------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2018 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/Decl.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Module.h" #include "swift/Frontend/ParseableInterfaceSupport.h" #include "clang/Basic/Module.h" #include "llvm/Support/Regex.h" using namespace swift; #define SWIFT_TOOLS_VERSION_KEY "swift-tools-version" #define SWIFT_MODULE_FLAGS_KEY "swift-module-flags" /// Diagnose any scoped imports in \p imports, i.e. those with a non-empty /// access path. These are not yet supported by textual interfaces, since the /// information about the declaration kind is not preserved through the binary /// serialization that happens as an intermediate step in non-whole-module /// builds. /// /// These come from declarations like `import class FooKit.MainFooController`. static void diagnoseScopedImports(DiagnosticEngine &diags, ArrayRef imports){ for (const ModuleDecl::ImportedModule &importPair : imports) { if (importPair.first.empty()) continue; diags.diagnose(importPair.first.front().second, diag::parseable_interface_scoped_import_unsupported); } } /// Prints to \p out a comment containing a tool-versions identifier as well /// as any relevant command-line flags in \p Opts used to construct \p M. static void printToolVersionAndFlagsComment(raw_ostream &out, TextualInterfaceOptions const &Opts, ModuleDecl *M) { auto &Ctx = M->getASTContext(); out << "// " SWIFT_TOOLS_VERSION_KEY ": " << Ctx.LangOpts.EffectiveLanguageVersion << "\n"; out << "// " SWIFT_MODULE_FLAGS_KEY ": " << Opts.TextualInterfaceFlags << "\n"; } llvm::Regex swift::getSwiftInterfaceToolsVersionRegex() { return llvm::Regex("^// " SWIFT_TOOLS_VERSION_KEY ": ([0-9\\.]+)$", llvm::Regex::Newline); } llvm::Regex swift::getSwiftInterfaceModuleFlagsRegex() { return llvm::Regex("^// " SWIFT_MODULE_FLAGS_KEY ": (.*)$", llvm::Regex::Newline); } /// Prints the imported modules in \p M to \p out in the form of \c import /// source declarations. static void printImports(raw_ostream &out, ModuleDecl *M) { // FIXME: This is very similar to what's in Serializer::writeInputBlock, but // it's not obvious what higher-level optimization would be factored out here. SmallVector allImports; M->getImportedModules(allImports, ModuleDecl::ImportFilter::All); ModuleDecl::removeDuplicateImports(allImports); diagnoseScopedImports(M->getASTContext().Diags, allImports); // Collect the public imports as a subset so that we can mark them with // '@_exported'. SmallVector publicImports; M->getImportedModules(publicImports, ModuleDecl::ImportFilter::Public); llvm::SmallSet publicImportSet; publicImportSet.insert(publicImports.begin(), publicImports.end()); for (auto import : allImports) { if (import.second->isStdlibModule() || import.second->isOnoneSupportModule() || import.second->isBuiltinModule()) { continue; } if (publicImportSet.count(import)) out << "@_exported "; out << "import "; import.second->getReverseFullModuleName().printForward(out); // Write the access path we should be honoring but aren't. // (See diagnoseScopedImports above.) if (!import.first.empty()) { out << "/*"; for (const auto &accessPathElem : import.first) out << "." << accessPathElem.first; out << "*/"; } out << "\n"; } } bool swift::emitParseableInterface(raw_ostream &out, TextualInterfaceOptions const &Opts, ModuleDecl *M) { assert(M); printToolVersionAndFlagsComment(out, Opts, M); printImports(out, M); const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile(); SmallVector topLevelDecls; M->getTopLevelDecls(topLevelDecls); for (const Decl *D : topLevelDecls) { if (!D->shouldPrintInContext(printOptions)) continue; D->print(out, printOptions); out << "\n"; } return false; }