blob: 7216b336d15f31eaaac3143f0538a9fb76797b02 [file] [log] [blame]
//===--- ModuleInterfacePrinting.cpp - Routines to print module interface -===//
//
// 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/IDE/ModuleInterfacePrinting.h"
#include "swift/IDE/Utils.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTDemangler.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/PrintOptions.h"
#include "swift/AST/SourceFile.h"
#include "swift/Basic/PrimitiveParsing.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Parse/Token.h"
#include "swift/Subsystems.h"
#include "swift/Serialization/SerializedModuleLoader.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/Module.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include <algorithm>
#include <memory>
#include <queue>
#include <string>
#include <utility>
#include <vector>
using namespace swift;
namespace {
/// Prints regular comments from clang module headers.
class ClangCommentPrinter : public ASTPrinter {
public:
ClangCommentPrinter(ASTPrinter &OtherPrinter, ClangModuleLoader &ClangLoader)
: OtherPrinter(OtherPrinter),
ClangLoader(ClangLoader) {}
private:
void printDeclPre(const Decl *D, Optional<BracketOptions> Bracket) override;
void printDeclPost(const Decl *D, Optional<BracketOptions> Bracket) override;
void avoidPrintDeclPost(const Decl *D) override;
// Forwarding implementations.
void printText(StringRef Text) override {
return OtherPrinter.printText(Text);
}
void printDeclLoc(const Decl *D) override {
return OtherPrinter.printDeclLoc(D);
}
void printDeclNameEndLoc(const Decl *D) override {
return OtherPrinter.printDeclNameEndLoc(D);
}
void printDeclNameOrSignatureEndLoc(const Decl *D) override {
return OtherPrinter.printDeclNameOrSignatureEndLoc(D);
}
void printTypePre(const TypeLoc &TL) override {
return OtherPrinter.printTypePre(TL);
}
void printTypePost(const TypeLoc &TL) override {
return OtherPrinter.printTypePost(TL);
}
void printTypeRef(Type T, const TypeDecl *TD, Identifier Name,
PrintNameContext NameContext) override {
return OtherPrinter.printTypeRef(T, TD, Name, NameContext);
}
void printModuleRef(ModuleEntity Mod, Identifier Name) override {
return OtherPrinter.printModuleRef(Mod, Name);
}
void printSynthesizedExtensionPre(const ExtensionDecl *ED,
TypeOrExtensionDecl Target,
Optional<BracketOptions> Bracket) override {
return OtherPrinter.printSynthesizedExtensionPre(ED, Target, Bracket);
}
void
printSynthesizedExtensionPost(const ExtensionDecl *ED,
TypeOrExtensionDecl Target,
Optional<BracketOptions> Bracket) override {
return OtherPrinter.printSynthesizedExtensionPost(ED, Target, Bracket);
}
void printStructurePre(PrintStructureKind Kind, const Decl *D) override {
return OtherPrinter.printStructurePre(Kind, D);
}
void printStructurePost(PrintStructureKind Kind, const Decl *D) override {
return OtherPrinter.printStructurePost(Kind, D);
}
void printNamePre(PrintNameContext Context) override {
return OtherPrinter.printNamePre(Context);
}
void printNamePost(PrintNameContext Context) override {
return OtherPrinter.printNamePost(Context);
}
// Prints regular comments of the header the clang node comes from, until
// the location of the node. Keeps track of the comments that were printed
// from the file and resumes printing for the next node from the same file.
// This expects to get passed clang nodes in source-order (at least within the
// same header).
void printCommentsUntil(ClangNode Node);
void printComment(StringRef Text, unsigned StartLocCol);
bool isDocumentationComment(clang::SourceLocation CommentLoc,
ClangNode Node) const;
unsigned getResumeOffset(clang::FileID FID) const {
auto OffsI = ResumeOffsets.find(FID);
if (OffsI != ResumeOffsets.end())
return OffsI->second;
return 0;
}
void setResumeOffset(clang::FileID FID, unsigned Offset) {
ResumeOffsets[FID] = Offset;
}
bool shouldPrintNewLineBefore(ClangNode Node) const;
void updateLastEntityLine(clang::SourceLocation Loc);
void updateLastEntityLine(clang::FileID FID, unsigned LineNo);
ASTPrinter &OtherPrinter;
ClangModuleLoader &ClangLoader;
llvm::DenseMap<clang::FileID, unsigned> ResumeOffsets;
SmallVector<StringRef, 2> PendingComments;
llvm::DenseMap<clang::FileID, unsigned> LastEntityLines;
};
} // unnamed namespace
static const clang::Module *
getUnderlyingClangModuleForImport(ImportDecl *Import) {
if (auto *ClangMod = Import->getClangModule())
return ClangMod;
if (auto Mod = Import->getModule())
if (auto *ClangMod = Mod->findUnderlyingClangModule())
return ClangMod;
return nullptr;
}
static void printTypeNameToString(Type Ty, std::string &Text) {
SmallString<128> Buffer;
llvm::raw_svector_ostream OS(Buffer);
Ty->print(OS);
Text = std::string(OS.str());
}
bool swift::ide::
printTypeInterface(ModuleDecl *M, Type Ty, ASTPrinter &Printer,
std::string &TypeName, std::string &Error) {
if (!Ty) {
if (Error.empty())
Error = "type cannot be null.";
return true;
}
Ty = Ty->getRValueType();
if (auto ND = Ty->getNominalOrBoundGenericNominal()) {
PrintOptions Options = PrintOptions::printTypeInterface(Ty.getPointer());
ND->print(Printer, Options);
printTypeNameToString(Ty, TypeName);
return false;
}
Error = "cannot find declaration of type.";
return true;
}
bool swift::ide::
printTypeInterface(ModuleDecl *M, StringRef TypeUSR, ASTPrinter &Printer,
std::string &TypeName, std::string &Error) {
return printTypeInterface(M, Demangle::getTypeForMangling(M->getASTContext(),
TypeUSR),
Printer, TypeName, Error);
}
static void adjustPrintOptions(PrintOptions &AdjustedOptions) {
// Don't print empty curly braces while printing the module interface.
AdjustedOptions.FunctionDefinitions = false;
AdjustedOptions.PrintGetSetOnRWProperties = false;
// Print var declarations separately, one variable per decl.
AdjustedOptions.ExplodePatternBindingDecls = true;
AdjustedOptions.VarInitializers = false;
}
ArrayRef<StringRef>
swift::ide::collectModuleGroups(ModuleDecl *M, std::vector<StringRef> &Scratch) {
for (auto File : M->getFiles()) {
File->collectAllGroups(Scratch);
}
std::sort(Scratch.begin(), Scratch.end(), [](StringRef L, StringRef R) {
return L.compare_lower(R) < 0;
});
return llvm::makeArrayRef(Scratch);
}
/// Determine whether the given extension has a Clang node that
/// created it (vs. being a Swift extension).
static bool extensionHasClangNode(ExtensionDecl *ext) {
return static_cast<bool>(swift::ide::extensionGetClangNode(ext));
}
Optional<StringRef>
swift::ide::findGroupNameForUSR(ModuleDecl *M, StringRef USR) {
for (auto File : M->getTopLevelModule()->getFiles()) {
if (auto Name = File->getGroupNameByUSR(USR)) {
return Name;
}
}
return None;
}
/// Prints a single decl using the \p Printer and \p Options provided. If
/// \p LeadingComment is non-empty, it will be printed verbatim before the decl
/// and any documentation comment associated with it.
///
/// \returns Whether the given decl was printed.
static bool printModuleInterfaceDecl(Decl *D,
ASTPrinter &Printer,
PrintOptions &Options,
bool PrintSynthesizedExtensions,
StringRef LeadingComment = StringRef()) {
if (!Options.shouldPrint(D)) {
Printer.callAvoidPrintDeclPost(D);
return false;
}
if (auto Ext = dyn_cast<ExtensionDecl>(D)) {
// Clang extensions (categories) are always printed in source order.
// Swift extensions are printed with their associated type unless it's
// a cross-module extension.
if (!extensionHasClangNode(Ext)) {
auto ExtendedNominal = Ext->getExtendedNominal();
if (!ExtendedNominal ||
Ext->getModuleContext() == ExtendedNominal->getModuleContext())
return false;
}
}
std::unique_ptr<SynthesizedExtensionAnalyzer> pAnalyzer;
if (auto NTD = dyn_cast<NominalTypeDecl>(D)) {
if (PrintSynthesizedExtensions) {
pAnalyzer.reset(new SynthesizedExtensionAnalyzer(NTD, Options));
Options.BracketOptions = {
NTD, true, true,
!pAnalyzer->hasMergeGroup(
SynthesizedExtensionAnalyzer::MergeGroupKind::MergeableWithTypeDef
)
};
}
}
if (!LeadingComment.empty() && Options.shouldPrint(D))
Printer << LeadingComment << "\n";
if (D->print(Printer, Options)) {
if (Options.BracketOptions.shouldCloseNominal(D))
Printer << "\n";
Options.BracketOptions = BracketOptions();
if (auto NTD = dyn_cast<NominalTypeDecl>(D)) {
std::queue<NominalTypeDecl *> SubDecls{{NTD}};
while (!SubDecls.empty()) {
auto NTD = SubDecls.front();
SubDecls.pop();
// Add sub-types of NTD.
for (auto Sub : NTD->getMembers())
if (auto N = dyn_cast<NominalTypeDecl>(Sub))
SubDecls.push(N);
// Print Ext and add sub-types of Ext.
for (auto Ext : NTD->getExtensions()) {
if (!PrintSynthesizedExtensions) {
if (!Options.shouldPrint(Ext)) {
Printer.callAvoidPrintDeclPost(Ext);
continue;
}
if (extensionHasClangNode(Ext))
continue; // will be printed in its source location, see above.
Printer << "\n";
if (!LeadingComment.empty())
Printer << LeadingComment << "\n";
Ext->print(Printer, Options);
Printer << "\n";
}
for (auto Sub : Ext->getMembers())
if (auto N = dyn_cast<NominalTypeDecl>(Sub))
SubDecls.push(N);
}
if (!PrintSynthesizedExtensions)
continue;
bool IsTopLevelDecl = D == NTD;
// If printed Decl is the top-level, merge the constraint-free extensions
// into the main body.
if (IsTopLevelDecl) {
// Print the part that should be merged with the type decl.
pAnalyzer->forEachExtensionMergeGroup(
SynthesizedExtensionAnalyzer::MergeGroupKind::MergeableWithTypeDef,
[&](ArrayRef<ExtensionInfo> Decls) {
for (auto ET : Decls) {
Options.BracketOptions = {
ET.Ext, false, Decls.back().Ext == ET.Ext, true
};
if (ET.IsSynthesized)
Options.initForSynthesizedExtension(NTD);
ET.Ext->print(Printer, Options);
if (ET.IsSynthesized)
Options.clearSynthesizedExtension();
if (Options.BracketOptions.shouldCloseExtension(ET.Ext))
Printer << "\n";
}
});
}
// If the printed Decl is not the top-level one, reset analyzer.
if (!IsTopLevelDecl)
pAnalyzer.reset(new SynthesizedExtensionAnalyzer(NTD, Options));
// Print the rest as synthesized extensions.
pAnalyzer->forEachExtensionMergeGroup(
// For top-level decls, only constraint extensions need to be
// printed, since the rest are merged into the main body.
IsTopLevelDecl
? SynthesizedExtensionAnalyzer::MergeGroupKind::UnmergeableWithTypeDef
: SynthesizedExtensionAnalyzer::MergeGroupKind::All,
[&](ArrayRef<ExtensionInfo> Decls) {
// Whether we've started the extension merge group in printing.
bool Opened = false;
for (auto ET : Decls) {
Options.BracketOptions = {
ET.Ext, !Opened, Decls.back().Ext == ET.Ext, true
};
if (Options.BracketOptions.shouldOpenExtension(ET.Ext)) {
Printer << "\n";
if (Options.shouldPrint(ET.Ext) && !LeadingComment.empty())
Printer << LeadingComment << "\n";
}
if (ET.IsSynthesized) {
if (ET.EnablingExt)
Options.initForSynthesizedExtension(ET.EnablingExt);
else
Options.initForSynthesizedExtension(NTD);
}
// Set opened if we actually printed this extension.
Opened |= ET.Ext->print(Printer, Options);
if (ET.IsSynthesized)
Options.clearSynthesizedExtension();
if (Options.BracketOptions.shouldCloseExtension(ET.Ext))
Printer << "\n";
}
});
Options.BracketOptions = BracketOptions();
}
}
return true;
}
return false;
}
/// Sorts import declarations for display.
static bool compareImports(ImportDecl *LHS, ImportDecl *RHS) {
return LHS->getImportPath() < RHS->getImportPath();
};
/// Sorts Swift declarations for display.
static bool compareSwiftDecls(Decl *LHS, Decl *RHS) {
auto *LHSValue = dyn_cast<ValueDecl>(LHS);
auto *RHSValue = dyn_cast<ValueDecl>(RHS);
if (LHSValue && RHSValue) {
auto LHSName = LHSValue->getBaseName();
auto RHSName = RHSValue->getBaseName();
if (int Ret = LHSName.compare(RHSName))
return Ret < 0;
// FIXME: not sufficient to establish a total order for overloaded decls.
}
return LHS->getKind() < RHS->getKind();
};
static std::pair<ArrayRef<Decl*>, ArrayRef<Decl*>>
getDeclsFromCrossImportOverlay(ModuleDecl *Overlay, ModuleDecl *Declaring,
SmallVectorImpl<Decl *> &Decls,
AccessLevel AccessFilter) {
Overlay->getDisplayDecls(Decls);
// Collect the imports of the underlying module so we can filter them out.
SmallPtrSet<ModuleDecl *, 8> PrevImported;
SmallVector<Decl*, 1> DeclaringDecls;
Declaring->getDisplayDecls(DeclaringDecls);
for (auto *D: DeclaringDecls) {
if (auto *ID = dyn_cast<ImportDecl>(D))
PrevImported.insert(ID->getModule());
}
// Filter out inaccessible decls and any imports of, or shared with the
// underlying module.
auto NewEnd = std::partition(Decls.begin(), Decls.end(), [&](Decl *D) {
if (auto *ID = dyn_cast<ImportDecl>(D)) {
ModuleDecl *Imported = ID->getModule();
if (!Imported)
return true;
// Ignore imports of the underlying module, or any cross-import
// that would map back to it.
if (Imported == Declaring || Imported->isCrossImportOverlayOf(Declaring))
return false;
// Ignore an imports of modules also imported by the underlying module.
if (PrevImported.find(Imported) != PrevImported.end())
return false;
}
if (auto *VD = dyn_cast<ValueDecl>(D)) {
if (AccessFilter > AccessLevel::Private &&
VD->getFormalAccess() < AccessFilter)
return false;
}
return true;
});
if (NewEnd != Decls.end())
Decls.erase(NewEnd, Decls.end());
// Separate out the import declarations and sort
MutableArrayRef<Decl*> Imports, Remainder;
auto ImportsEnd = std::partition(Decls.begin(), Decls.end(), [](Decl *D) {
return isa<ImportDecl>(D);
});
if (ImportsEnd != Decls.begin()) {
Imports = {Decls.begin(), ImportsEnd};
Remainder = {ImportsEnd, Decls.end()};
std::sort(Imports.begin(), Imports.end(), [](Decl *LHS, Decl *RHS) {
return compareImports(cast<ImportDecl>(LHS), cast<ImportDecl>(RHS));
});
} else {
Remainder = Decls;
}
std::sort(Remainder.begin(), Remainder.end(), compareSwiftDecls);
return {Imports, Remainder};
}
static void printCrossImportOverlays(ModuleDecl *Declaring, ASTContext &Ctx,
ASTPrinter &Printer,
PrintOptions Options,
bool PrintSynthesizedExtensions) {
SmallVector<Decl *, 1> OverlayDecls;
SmallVector<Identifier, 1> Bystanders;
auto PrintDecl = [&](Decl *D, StringRef LeadingComment = StringRef()) {
return printModuleInterfaceDecl(D, Printer, Options,
PrintSynthesizedExtensions,
LeadingComment);
};
SmallVector<ModuleDecl *, 1> OverlayModules;
Declaring->findDeclaredCrossImportOverlaysTransitive(OverlayModules);
std::sort(OverlayModules.begin(), OverlayModules.end(),
[](ModuleDecl *LHS, ModuleDecl *RHS) {
return LHS->getNameStr() < RHS->getNameStr();
});
for (auto *Overlay: OverlayModules) {
OverlayDecls.clear();
auto DeclLists = getDeclsFromCrossImportOverlay(Overlay, Declaring,
OverlayDecls,
Options.AccessFilter);
// Ignore overlays without any decls
if (OverlayDecls.empty())
continue;
Bystanders.clear();
auto BystandersValid =
Overlay->getRequiredBystandersIfCrossImportOverlay(Declaring, Bystanders);
// Ignore badly formed overlays that don't import their declaring module.
if (!BystandersValid)
continue;
std::sort(Bystanders.begin(), Bystanders.end(),
[](Identifier LHS, Identifier RHS) {
return LHS.str() < RHS.str();
});
std::string BystanderList;
for (size_t I: range(Bystanders.size())) {
if (I == Bystanders.size() - 1) {
if (I != 0)
BystanderList += " and ";
} else if (I != 0) {
BystanderList += ", ";
}
BystanderList += Bystanders[I].str();
}
Printer << "\n// MARK: - " << BystanderList << " Additions\n\n";
for (auto *Import : DeclLists.first)
PrintDecl(Import);
Printer << "\n";
std::string PerDeclComment = "// Available when " + BystanderList;
PerDeclComment += Bystanders.size() == 1 ? " is" : " are";
PerDeclComment += " imported with " + Declaring->getNameStr().str();
for (auto *D : DeclLists.second) {
if (PrintDecl(D, PerDeclComment))
Printer << "\n";
}
}
}
void swift::ide::printModuleInterface(
ModuleDecl *TargetMod,
ArrayRef<StringRef> GroupNames,
ModuleTraversalOptions TraversalOptions,
ASTPrinter &Printer,
const PrintOptions &Options,
const bool PrintSynthesizedExtensions) {
// Clang submodules aren't handled well by `getDisplayDecls()` (no decls are
// returned), so map them to their top-level module and filter out the extra
// results below.
const clang::Module *TargetClangMod = TargetMod->findUnderlyingClangModule();
ModuleDecl *TopLevelMod = TargetMod->getTopLevelModule();
bool IsSubmodule = TargetMod != TopLevelMod;
auto &SwiftContext = TopLevelMod->getASTContext();
auto &Importer =
static_cast<ClangImporter &>(*SwiftContext.getClangModuleLoader());
auto AdjustedOptions = Options;
adjustPrintOptions(AdjustedOptions);
SmallVector<Decl *, 1> Decls;
TopLevelMod->getDisplayDecls(Decls);
SmallVector<ImportDecl *, 1> ImportDecls;
llvm::DenseSet<const clang::Module *> ClangModulesForImports;
SmallVector<Decl *, 1> SwiftDecls;
llvm::DenseMap<const clang::Module *,
SmallVector<std::pair<Decl *, clang::SourceLocation>, 1>>
ClangDecls;
// If we're printing recursively, find all of the submodules to print.
if (TargetClangMod) {
if (TraversalOptions) {
SmallVector<const clang::Module *, 8> Worklist;
SmallPtrSet<const clang::Module *, 8> Visited;
Worklist.push_back(TargetClangMod);
Visited.insert(TargetClangMod);
while (!Worklist.empty()) {
const clang::Module *CM = Worklist.pop_back_val();
if (!(TraversalOptions & ModuleTraversal::VisitHidden) &&
CM->IsExplicit)
continue;
ClangDecls.insert({ CM, {} });
// If we're supposed to visit submodules, add them now.
if (TraversalOptions & ModuleTraversal::VisitSubmodules) {
for (auto Sub = CM->submodule_begin(), SubEnd = CM->submodule_end();
Sub != SubEnd; ++Sub) {
if (Visited.insert(*Sub).second)
Worklist.push_back(*Sub);
}
}
}
} else {
ClangDecls.insert({ TargetClangMod, {} });
}
}
// Collect those submodules that are actually imported but have no import
// decls in the module.
llvm::SmallPtrSet<const clang::Module *, 16> NoImportSubModules;
if (TargetClangMod) {
// Assume all submodules are missing.
for (auto It = TargetClangMod->submodule_begin();
It != TargetClangMod->submodule_end(); ++It) {
NoImportSubModules.insert(*It);
}
}
llvm::StringMap<std::vector<Decl*>> FileRangedDecls;
// Separate the declarations that we are going to print into different
// buckets.
for (Decl *D : Decls) {
// Skip declarations that are not accessible.
if (auto *VD = dyn_cast<ValueDecl>(D)) {
if (Options.AccessFilter > AccessLevel::Private &&
VD->getFormalAccess() < Options.AccessFilter)
continue;
}
auto ShouldPrintImport = [&](ImportDecl *ImportD) -> bool {
if (!TargetClangMod)
return true;
auto ImportedMod = ImportD->getClangModule();
if (!ImportedMod)
return true;
if (!ImportedMod->isSubModule())
return true;
if (ImportedMod == TargetClangMod)
return false;
// FIXME: const-ness on the clang API.
return ImportedMod->isSubModuleOf(
const_cast<clang::Module*>(TargetClangMod));
};
if (auto ID = dyn_cast<ImportDecl>(D)) {
if (ShouldPrintImport(ID)) {
if (ID->getClangModule())
// Erase those submodules that are not missing.
NoImportSubModules.erase(ID->getClangModule());
if (ID->getImportKind() == ImportKind::Module) {
// Make sure we don't print duplicate imports, due to getting imports
// for both a clang module and its overlay.
if (auto *ClangMod = getUnderlyingClangModuleForImport(ID)) {
auto P = ClangModulesForImports.insert(ClangMod);
bool IsNew = P.second;
if (!IsNew)
continue;
}
}
ImportDecls.push_back(ID);
}
continue;
}
auto addToClangDecls = [&](Decl *D, ClangNode CN) {
assert(CN && "No Clang node here");
clang::SourceLocation Loc = CN.getLocation();
auto *OwningModule = Importer.getClangOwningModule(CN);
auto I = ClangDecls.find(OwningModule);
if (I != ClangDecls.end()) {
I->second.push_back({ D, Loc });
}
};
if (auto clangNode = getEffectiveClangNode(D)) {
addToClangDecls(D, clangNode);
continue;
}
// If we have an extension containing globals imported as members,
// use the first member as the Clang node.
if (auto Ext = dyn_cast<ExtensionDecl>(D)) {
if (extensionHasClangNode(Ext)) {
addToClangDecls(Ext, extensionGetClangNode(Ext));
continue;
}
}
if (!IsSubmodule) {
// If group name is given and the decl does not belong to the group, skip it.
if (!GroupNames.empty()){
if (auto TargetGroup = D->getGroupName()) {
if (std::find(GroupNames.begin(), GroupNames.end(),
TargetGroup.getValue()) != GroupNames.end()) {
FileRangedDecls.insert({
D->getSourceFileName().getValue(),
std::vector<Decl*>()
}).first->getValue().push_back(D);
}
}
continue;
}
// Add Swift decls if we are printing the top-level module.
SwiftDecls.push_back(D);
}
}
if (!GroupNames.empty()) {
assert(SwiftDecls.empty());
for (auto &Entry : FileRangedDecls) {
auto &DeclsInFile = Entry.getValue();
std::sort(DeclsInFile.begin(), DeclsInFile.end(),
[](Decl* LHS, Decl *RHS) {
assert(LHS->getSourceOrder().hasValue());
assert(RHS->getSourceOrder().hasValue());
return LHS->getSourceOrder().getValue() <
RHS->getSourceOrder().getValue();
});
for (auto D : DeclsInFile) {
SwiftDecls.push_back(D);
}
}
}
// Create the missing import decls and add to the collector.
for (auto *SubMod : NoImportSubModules) {
ImportDecls.push_back(createImportDecl(TopLevelMod->getASTContext(),
TopLevelMod, SubMod, {}));
}
// Sort imported clang declarations in source order *within a submodule*.
auto &ClangSourceManager = Importer.getClangASTContext().getSourceManager();
for (auto &P : ClangDecls) {
std::stable_sort(P.second.begin(), P.second.end(),
[&](std::pair<Decl *, clang::SourceLocation> LHS,
std::pair<Decl *, clang::SourceLocation> RHS) -> bool {
return ClangSourceManager.isBeforeInTranslationUnit(LHS.second,
RHS.second);
});
}
// Sort Swift declarations so that we print them in a consistent order.
std::sort(ImportDecls.begin(), ImportDecls.end(), compareImports);
// If the group name is specified, we sort them according to their source order,
// which is the order preserved by getTopLevelDecls.
if (GroupNames.empty())
std::stable_sort(SwiftDecls.begin(), SwiftDecls.end(), compareSwiftDecls);
ASTPrinter *PrinterToUse = &Printer;
ClangCommentPrinter RegularCommentPrinter(Printer, Importer);
if (Options.PrintRegularClangComments)
PrinterToUse = &RegularCommentPrinter;
auto PrintDecl = [&](Decl *D) {
return printModuleInterfaceDecl(D, *PrinterToUse, AdjustedOptions,
PrintSynthesizedExtensions);
};
// Imports from the stdlib are internal details that don't need to be exposed.
if (!TargetMod->isStdlibModule()) {
for (auto *D : ImportDecls)
PrintDecl(D);
Printer << "\n";
}
{
using ModuleAndName = std::pair<const clang::Module *, std::string>;
SmallVector<ModuleAndName, 8> ClangModules;
for (auto P : ClangDecls) {
ClangModules.push_back({ P.first, P.first->getFullModuleName() });
}
// Sort modules by name.
std::sort(ClangModules.begin(), ClangModules.end(),
[](const ModuleAndName &LHS, const ModuleAndName &RHS)
-> bool {
return LHS.second < RHS.second;
});
for (auto CM : ClangModules) {
for (auto DeclAndLoc : ClangDecls[CM.first])
PrintDecl(DeclAndLoc.first);
}
}
if (!(TraversalOptions & ModuleTraversal::SkipOverlay) || !TargetClangMod) {
for (auto *D : SwiftDecls) {
if (PrintDecl(D))
Printer << "\n";
}
// If we're printing the entire target module (not specific sub-groups),
// also print the decls from any underscored Swift cross-import overlays it
// is the underlying module of, transitively.
if (GroupNames.empty()) {
printCrossImportOverlays(TargetMod, SwiftContext, *PrinterToUse,
AdjustedOptions, PrintSynthesizedExtensions);
}
}
}
static SourceLoc getDeclStartPosition(SourceFile &File) {
SourceManager &SM = File.getASTContext().SourceMgr;
SourceLoc Winner;
auto tryUpdateStart = [&](SourceLoc Loc) -> bool {
if (Loc.isInvalid())
return false;
if (Winner.isInvalid()) {
Winner = Loc;
return true;
}
if (SM.isBeforeInBuffer(Loc, Winner)) {
Winner = Loc;
return true;
}
return false;
};
for (auto D : File.getTopLevelDecls()) {
if (tryUpdateStart(D->getStartLoc())) {
tryUpdateStart(D->getAttrs().getStartLoc());
auto RawComment = D->getRawComment();
if (!RawComment.isEmpty())
tryUpdateStart(RawComment.Comments.front().Range.getStart());
}
}
return Winner;
}
static void printUntilFirstDeclStarts(SourceFile &File, ASTPrinter &Printer) {
if (!File.getBufferID().hasValue())
return;
auto BufferID = *File.getBufferID();
auto &SM = File.getASTContext().SourceMgr;
CharSourceRange TextRange = SM.getRangeForBuffer(BufferID);
auto DeclStartLoc = getDeclStartPosition(File);
if (DeclStartLoc.isValid()) {
TextRange = CharSourceRange(SM, TextRange.getStart(), DeclStartLoc);
}
Printer << SM.extractText(TextRange, BufferID);
}
void swift::ide::printSwiftSourceInterface(SourceFile &File,
ASTPrinter &Printer,
const PrintOptions &Options) {
// We print all comments before the first line of Swift code.
printUntilFirstDeclStarts(File, Printer);
File.print(Printer, Options);
}
static Decl* getTopLevelDecl(Decl *D) {
while (!D->getDeclContext()->isModuleScopeContext()) {
auto *ParentD = D->getDeclContext()->getAsDecl();
if (!ParentD)
break;
D = ParentD;
}
return D;
}
void swift::ide::printHeaderInterface(
StringRef Filename,
ASTContext &Ctx,
ASTPrinter &Printer,
const PrintOptions &Options) {
auto AdjustedOptions = Options;
adjustPrintOptions(AdjustedOptions);
auto &Importer = static_cast<ClangImporter &>(*Ctx.getClangModuleLoader());
auto &ClangSM = Importer.getClangASTContext().getSourceManager();
auto headerFilter = [&](ClangNode ClangN) -> bool {
return true; // no need for filtering.
};
SmallVector<Decl *, 32> ClangDecls;
llvm::SmallPtrSet<Decl *, 32> SeenDecls;
auto headerReceiver = [&](Decl *D) {
if (SeenDecls.insert(getTopLevelDecl(D)).second)
ClangDecls.push_back(D);
};
Importer.lookupDeclsFromHeader(Filename, headerFilter, headerReceiver);
// Sort imported declarations in source order.
std::sort(ClangDecls.begin(), ClangDecls.end(),
[&](Decl *LHS, Decl *RHS) -> bool {
return ClangSM.isBeforeInTranslationUnit(
getEffectiveClangNode(LHS).getLocation(),
getEffectiveClangNode(RHS).getLocation());
});
ASTPrinter *PrinterToUse = &Printer;
ClangCommentPrinter RegularCommentPrinter(Printer, Importer);
if (Options.PrintRegularClangComments)
PrinterToUse = &RegularCommentPrinter;
for (auto *D : ClangDecls) {
// Even though the corresponding clang decl should be top-level, its
// equivalent Swift decl may not be. E.g. a top-level function may be mapped
// to a property accessor in Swift.
D = getTopLevelDecl(D);
ASTPrinter &Printer = *PrinterToUse;
if (!AdjustedOptions.shouldPrint(D)) {
Printer.callAvoidPrintDeclPost(D);
continue;
}
if (D->print(Printer, AdjustedOptions))
Printer << "\n";
}
}
void ClangCommentPrinter::avoidPrintDeclPost(const Decl *D) {
auto CD = D->getClangDecl();
if (!CD)
return;
const auto &Ctx = ClangLoader.getClangASTContext();
const auto &SM = Ctx.getSourceManager();
auto EndLoc = CD->getSourceRange().getEnd();
if (EndLoc.isInvalid())
return;
clang::FileID FID = SM.getFileID(EndLoc);
if (FID.isInvalid())
return;
auto Loc = EndLoc;
for (unsigned Line = SM.getSpellingLineNumber(EndLoc);
Loc.isValid() && SM.getSpellingLineNumber(Loc) == Line;
Loc = Loc.getLocWithOffset(1));
if (Loc.isInvalid())
return;
if (SM.getFileOffset(Loc) > getResumeOffset(FID))
setResumeOffset(FID, SM.getFileOffset(Loc));
}
void ClangCommentPrinter::printDeclPre(const Decl *D,
Optional<BracketOptions> Bracket) {
// Skip parameters, since we do not gracefully handle nested declarations on a
// single line.
// FIXME: we should fix that, since it also affects struct members, etc.
if (!isa<ParamDecl>(D)) {
if (auto ClangN = swift::ide::getEffectiveClangNode(D)) {
printCommentsUntil(ClangN);
if (shouldPrintNewLineBefore(ClangN)) {
*this << "\n";
printIndent();
}
updateLastEntityLine(ClangN.getSourceRange().getBegin());
}
}
return OtherPrinter.printDeclPre(D, Bracket);
}
void ClangCommentPrinter::printDeclPost(const Decl *D,
Optional<BracketOptions> Bracket) {
OtherPrinter.printDeclPost(D, Bracket);
// Skip parameters; see printDeclPre().
if (isa<ParamDecl>(D))
return;
for (auto CommentText : PendingComments) {
*this << " " << ASTPrinter::sanitizeUtf8(CommentText);
}
PendingComments.clear();
if (auto ClangN = swift::ide::getEffectiveClangNode(D))
updateLastEntityLine(ClangN.getSourceRange().getEnd());
}
void ClangCommentPrinter::printCommentsUntil(ClangNode Node) {
const auto &Ctx = ClangLoader.getClangASTContext();
const auto &SM = Ctx.getSourceManager();
clang::SourceLocation NodeLoc =
SM.getFileLoc(Node.getSourceRange().getBegin());
if (NodeLoc.isInvalid())
return;
unsigned NodeLineNo = SM.getSpellingLineNumber(NodeLoc);
clang::FileID FID = SM.getFileID(NodeLoc);
if (FID.isInvalid())
return;
clang::SourceLocation FileLoc = SM.getLocForStartOfFile(FID);
StringRef Text = SM.getBufferData(FID);
if (Text.empty())
return;
const char *BufStart = Text.data();
const char *BufPtr = BufStart + getResumeOffset(FID);
const char *BufEnd = BufStart + Text.size();
assert(BufPtr <= BufEnd);
if (BufPtr == BufEnd)
return; // nothing left.
clang::Lexer Lex(FileLoc, Ctx.getLangOpts(), BufStart, BufPtr, BufEnd);
Lex.SetCommentRetentionState(true);
unsigned &LastPrintedLineNo = LastEntityLines[FID];
clang::Token Tok;
do {
BufPtr = Lex.getBufferLocation();
Lex.LexFromRawLexer(Tok);
if (Tok.is(clang::tok::eof))
break;
if (Tok.isNot(clang::tok::comment))
continue;
// Reached a comment.
clang::SourceLocation CommentLoc = Tok.getLocation();
std::pair<clang::FileID, unsigned> LocInfo =
SM.getDecomposedLoc(CommentLoc);
assert(LocInfo.first == FID);
unsigned LineNo = SM.getLineNumber(LocInfo.first, LocInfo.second);
if (LineNo > NodeLineNo)
break; // Comment is past the clang node.
bool IsDocComment = isDocumentationComment(CommentLoc, Node);
// Print out the comment.
StringRef CommentText(BufStart + LocInfo.second, Tok.getLength());
// Check if comment is on same line but after the declaration.
if (SM.isBeforeInTranslationUnit(NodeLoc, Tok.getLocation())) {
if (!IsDocComment)
PendingComments.push_back(CommentText);
continue;
}
if (LastPrintedLineNo && LineNo - LastPrintedLineNo > 1) {
*this << "\n";
printIndent();
}
if (!IsDocComment) {
unsigned StartLocCol = SM.getSpellingColumnNumber(Tok.getLocation());
printComment(CommentText, StartLocCol);
}
LastPrintedLineNo =
SM.getLineNumber(LocInfo.first, LocInfo.second + Tok.getLength());
} while (true);
// Resume printing comments from this point.
setResumeOffset(FID, BufPtr - BufStart);
}
void ClangCommentPrinter::printComment(StringRef RawText, unsigned StartCol) {
unsigned WhitespaceToTrim = StartCol ? StartCol - 1 : 0;
SmallVector<StringRef, 8> Lines;
trimLeadingWhitespaceFromLines(RawText, WhitespaceToTrim, Lines);
for (auto Line : Lines) {
*this << ASTPrinter::sanitizeUtf8(Line) << "\n";
printIndent();
}
}
bool ClangCommentPrinter::isDocumentationComment(
clang::SourceLocation CommentLoc, ClangNode Node) const {
const clang::Decl *D = Node.getAsDecl();
if (!D)
return false;
const auto &Ctx = ClangLoader.getClangASTContext();
const auto &SM = Ctx.getSourceManager();
const clang::RawComment *RC = Ctx.getRawCommentForAnyRedecl(D);
if (!RC)
return false;
clang::SourceRange DocRange = RC->getSourceRange();
if (SM.isBeforeInTranslationUnit(CommentLoc, DocRange.getBegin()) ||
SM.isBeforeInTranslationUnit(DocRange.getEnd(), CommentLoc))
return false;
return true;
}
bool ClangCommentPrinter::shouldPrintNewLineBefore(ClangNode Node) const {
assert(Node);
const auto &Ctx = ClangLoader.getClangASTContext();
const auto &SM = Ctx.getSourceManager();
clang::SourceLocation NodeLoc =
SM.getFileLoc(Node.getSourceRange().getBegin());
if (NodeLoc.isInvalid())
return false;
unsigned NodeLineNo = SM.getSpellingLineNumber(NodeLoc);
clang::FileID FID = SM.getFileID(NodeLoc);
if (FID.isInvalid())
return false;
unsigned LastEntiyLine = 0;
auto It = LastEntityLines.find(FID);
if (It != LastEntityLines.end())
LastEntiyLine = It->second;
return (NodeLineNo > LastEntiyLine) && NodeLineNo - LastEntiyLine > 1;
}
void ClangCommentPrinter::updateLastEntityLine(clang::SourceLocation Loc) {
if (Loc.isInvalid())
return;
const auto &Ctx = ClangLoader.getClangASTContext();
const auto &SM = Ctx.getSourceManager();
unsigned LineNo = SM.getSpellingLineNumber(Loc);
clang::FileID FID = SM.getFileID(Loc);
if (FID.isInvalid())
return;
updateLastEntityLine(FID, LineNo);
}
void ClangCommentPrinter::updateLastEntityLine(clang::FileID FID,
unsigned LineNo) {
assert(!FID.isInvalid());
unsigned &LastEntiyLine = LastEntityLines[FID];
if (LineNo > LastEntiyLine)
LastEntiyLine = LineNo;
}