| //===--- TextualInterfaceGeneration.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 "TextualInterfaceGeneration.h" |
| |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/DiagnosticsFrontend.h" |
| #include "swift/AST/Module.h" |
| #include "clang/Basic/Module.h" |
| |
| using namespace swift; |
| |
| /// 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<ModuleDecl::ImportedModule> imports){ |
| for (const ModuleDecl::ImportedModule &importPair : imports) { |
| if (importPair.first.empty()) |
| continue; |
| diags.diagnose(importPair.first.front().second, |
| diag::textual_interface_scoped_import_unsupported); |
| } |
| } |
| |
| /// 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<ModuleDecl::ImportedModule, 8> 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<ModuleDecl::ImportedModule, 8> publicImports; |
| M->getImportedModules(publicImports, ModuleDecl::ImportFilter::Public); |
| llvm::SmallSet<ModuleDecl::ImportedModule, 8, |
| ModuleDecl::OrderImportedModules> 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::emitModuleInterface(raw_ostream &out, ModuleDecl *M) { |
| assert(M); |
| |
| printImports(out, M); |
| |
| const PrintOptions printOptions = PrintOptions::printTextualInterfaceFile(); |
| SmallVector<Decl *, 16> topLevelDecls; |
| M->getTopLevelDecls(topLevelDecls); |
| for (const Decl *D : topLevelDecls) { |
| if (!D->shouldPrintInContext(printOptions)) |
| continue; |
| D->print(out, printOptions); |
| out << "\n"; |
| } |
| return false; |
| } |