Skip to content

Commit b3f296d

Browse files
author
Nathan Hawes
committed
[SourceKit/InterfaceGen] Also print the cross-import overlays of a module in its interface.
When printing the generated interface of a module, also print the decls from any underscored cross-import overlays it is the direct, or indirect underlying module of. Declarations are grouped by overlay, with a descriptive `MARK:` comment introducing each overlay, and a regular comment above each decl listing the required bystander modules that must be imported for the decl to be available. In addition in each overlay: - import declarations of any underlying modules are filtered out, since they are either other underscored cross-import overlays, or the target module they are being presented as being part of. - import declarations that are also in the target module are filtered out, since the overlay is being presented as a conditional part of the target module. Resolves rdar://problem/59445385
1 parent 4dec4ab commit b3f296d

File tree

12 files changed

+633
-38
lines changed

12 files changed

+633
-38
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ struct PrintOptions {
432432
/// The information for converting archetypes to specialized types.
433433
llvm::Optional<TypeTransformContext> TransformContext;
434434

435+
/// Before printing the name of a ModuleDecl, this callback will be called and
436+
/// the name of the ModuleDecl it returns will be printed instead. This is
437+
/// currently used to present cross import overlays as if they were their
438+
/// underlying module.
439+
std::function<const ModuleDecl*(const ModuleDecl *)> mapModuleToUnderlying =
440+
[] (const ModuleDecl *D) { return D; };
441+
435442
bool PrintAsMember = false;
436443

437444
/// Whether to print parameter specifiers as 'let' and 'var'.

lib/AST/ASTPrinter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3586,7 +3586,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
35863586
template <typename T>
35873587
void printModuleContext(T *Ty) {
35883588
FileUnit *File = cast<FileUnit>(Ty->getDecl()->getModuleScopeContext());
3589-
ModuleDecl *Mod = File->getParentModule();
3589+
const ModuleDecl *Mod =
3590+
Options.mapModuleToUnderlying(File->getParentModule());
35903591

35913592
Identifier Name = Mod->getName();
35923593
if (Options.UseExportedModuleNames)

lib/IDE/ModuleInterfacePrinting.cpp

Lines changed: 260 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,199 @@ swift::ide::findGroupNameForUSR(ModuleDecl *M, StringRef USR) {
238238
return None;
239239
}
240240

241+
/// Sorts import declarations for display.
242+
static bool compareImports(ImportDecl *LHS, ImportDecl *RHS) {
243+
auto LHSPath = LHS->getFullAccessPath();
244+
auto RHSPath = RHS->getFullAccessPath();
245+
for (unsigned i: range(std::min(LHSPath.size(), RHSPath.size()))) {
246+
if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str()))
247+
return Ret < 0;
248+
}
249+
return false;
250+
};
251+
252+
/// Sorts Swift declarations for display.
253+
static bool compareSwiftDecls(Decl *LHS, Decl *RHS) {
254+
auto *LHSValue = dyn_cast<ValueDecl>(LHS);
255+
auto *RHSValue = dyn_cast<ValueDecl>(RHS);
256+
257+
if (LHSValue && RHSValue) {
258+
auto LHSName = LHSValue->getBaseName();
259+
auto RHSName = RHSValue->getBaseName();
260+
if (int Ret = LHSName.compare(RHSName))
261+
return Ret < 0;
262+
// FIXME: not sufficient to establish a total order for overloaded decls.
263+
}
264+
return LHS->getKind() < RHS->getKind();
265+
};
266+
267+
/// Represents a single cross-import overlay of the target module whose
268+
/// declarations are conditionally available when a bystander module is
269+
/// imported alongside the target module.
270+
class OverlayItem {
271+
friend class OverlayCollector;
272+
private:
273+
ModuleDecl *Module; ///< The overlay module.
274+
StringRef BystanderName; ///< The name of the required bystander module.
275+
OverlayItem *Underlying; ///< The underlying overlay, or target module.
276+
277+
public:
278+
OverlayItem(ModuleDecl *Overlay, StringRef BystanderName,
279+
OverlayItem *Underlying)
280+
: Module(Overlay), BystanderName(BystanderName), Underlying(Underlying) {}
281+
282+
/// Gets the set of bystander modules that need to be imported along with the
283+
/// target module for this overlay to be applied.
284+
void getBystanders(SmallVectorImpl<StringRef> &Bystanders) const {
285+
if (Underlying)
286+
Underlying->getBystanders(Bystanders);
287+
if (!BystanderName.empty())
288+
Bystanders.push_back(BystanderName);
289+
}
290+
291+
/// Gets the set of accessible declarations specified in this overlay, divided
292+
/// into two groups: the import decls, and everything else.
293+
std::pair<ArrayRef<Decl*>, ArrayRef<Decl*>>
294+
getDecls(SmallVectorImpl<Decl *> &Decls, AccessLevel AccessFilter) const {
295+
Module->getDisplayDecls(Decls);
296+
297+
// Filter out parent imports and inaccessible decls.
298+
SmallPtrSet<ModuleDecl *, 8> PrevImported;
299+
getUnderlyingImports(PrevImported);
300+
auto NewEnd = std::partition(Decls.begin(), Decls.end(), [&](Decl *D) {
301+
if (auto *ID = dyn_cast<ImportDecl>(D)) {
302+
if (isUnderlying(ID->getModule()))
303+
return false;
304+
if (PrevImported.find(ID->getModule()) != PrevImported.end())
305+
return false;
306+
}
307+
if (auto *VD = dyn_cast<ValueDecl>(D)) {
308+
if (AccessFilter > AccessLevel::Private &&
309+
VD->getFormalAccess() < AccessFilter)
310+
return false;
311+
}
312+
return true;
313+
});
314+
if (NewEnd != Decls.end())
315+
Decls.erase(NewEnd, Decls.end());
316+
317+
// Separate out the import declarations and sort
318+
MutableArrayRef<Decl*> Imports, Remainder;
319+
auto ImportsEnd = std::partition(Decls.begin(), Decls.end(), [](Decl *D) {
320+
return isa<ImportDecl>(D);
321+
});
322+
if (ImportsEnd != Decls.begin()) {
323+
Imports = {Decls.begin(), ImportsEnd};
324+
Remainder = {ImportsEnd, Decls.end()};
325+
std::sort(Imports.begin(), Imports.end(), [](Decl *LHS, Decl *RHS) {
326+
return compareImports(cast<ImportDecl>(LHS), cast<ImportDecl>(RHS));
327+
});
328+
} else {
329+
Remainder = Decls;
330+
}
331+
std::sort(Remainder.begin(), Remainder.end(), compareSwiftDecls);
332+
return {Imports, Remainder};
333+
}
334+
335+
private:
336+
/// Checks whether the given module is an underlying module of this overlay,
337+
/// transitively.
338+
bool isUnderlying(ModuleDecl *M) const {
339+
if (M == Module)
340+
return true;
341+
return Underlying && Underlying->isUnderlying(M);
342+
}
343+
344+
/// Populates \p Imported with all ModuleDecls directly imported by the base
345+
/// underlying module (ignoring those of any intermediate underlying modules).
346+
void getUnderlyingImports(SmallPtrSet<ModuleDecl *, 8> &Imported) const {
347+
if (Underlying) {
348+
Underlying->getUnderlyingImports(Imported);
349+
} else {
350+
SmallVector<Decl*, 1> Decls;
351+
Module->getDisplayDecls(Decls);
352+
for (auto *D: Decls) {
353+
if (auto *ID = dyn_cast<ImportDecl>(D))
354+
Imported.insert(ID->getModule());
355+
}
356+
}
357+
}
358+
};
359+
360+
// Collects the set of underscored cross-import overlay modules of a given
361+
// target module, transitively, reporting the set of bystander modules
362+
// necessary for their inclusion (on top of the target module).
363+
//
364+
// E.g. if module A has a cross-import overlay _ABAdditions that is imported if
365+
// a bystander module B is also imported, and _ABAdditions has a cross-import
366+
// overlay __ABAdditionsCAdditions imported when a bystander module C is
367+
// imported, the collected overlays will include _ABAdditions with bystanders B,
368+
// and __ABAdditionsCAdditions with bystanders B and C.
369+
class OverlayCollector {
370+
ASTContext &Ctx; ///< ASTContext to use for module lookups.
371+
SmallVector<OverlayItem, 1> Overlays; ///< The collected overlays.
372+
OverlayItem TargetModuleItem; /// < The initial worklist item.
373+
374+
public:
375+
OverlayCollector(ModuleDecl *Target, ASTContext &Ctx)
376+
: Ctx(Ctx), TargetModuleItem(Target, StringRef(), nullptr) {
377+
SmallVector<OverlayItem *, 1> Worklist;
378+
SmallPtrSet<ModuleDecl *, 1> Seen;
379+
380+
Worklist.push_back(&TargetModuleItem);
381+
while(!Worklist.empty()) {
382+
OverlayItem &Item = *Worklist.back();
383+
Worklist.pop_back();
384+
if (!Seen.insert(Item.Module).second)
385+
continue;
386+
processItem(Item, Worklist);
387+
}
388+
std::sort(Overlays.begin(), Overlays.end(),
389+
[](OverlayItem &LHS, OverlayItem &RHS) {
390+
return LHS.Module->getNameStr() < RHS.Module->getNameStr();
391+
});
392+
}
393+
394+
/// Gets the underscored cross-import overlays.
395+
ArrayRef<OverlayItem> getOverlays() {
396+
return Overlays;
397+
}
398+
399+
/// Maps the given module to the target module if it's one of its underscored
400+
/// cross-import overlays.
401+
const ModuleDecl *mapToUnderlyingModule(const ModuleDecl *M) {
402+
auto i = std::find_if(Overlays.begin(), Overlays.end(),
403+
[M](OverlayItem &Item) {
404+
return Item.Module == M;
405+
});
406+
return i == Overlays.end() ? M : TargetModuleItem.Module;
407+
}
408+
409+
private:
410+
void processItem(OverlayItem &Current,
411+
SmallVectorImpl<OverlayItem*> &Worklist) {
412+
SmallVector<Identifier, 1> BystanderNames, OverlayNames;
413+
Current.Module->getDeclaredCrossImportBystanders(BystanderNames);
414+
415+
for (Identifier BystanderName: BystanderNames) {
416+
OverlayNames.clear();
417+
Current.Module->findDeclaredCrossImportOverlays(BystanderName,
418+
OverlayNames,
419+
SourceLoc());
420+
for (Identifier OverlayName: OverlayNames) {
421+
StringRef OverlayNameStr = OverlayName.str();
422+
if (!OverlayNameStr.startswith("_"))
423+
continue;
424+
ModuleDecl *Overlay = Ctx.getModuleByName(OverlayNameStr);
425+
if (!Overlay)
426+
continue;
427+
Overlays.emplace_back(Overlay, BystanderName.str(), &Current);
428+
Worklist.push_back(&Overlays.back());
429+
}
430+
}
431+
}
432+
};
433+
241434
void swift::ide::printSubmoduleInterface(
242435
ModuleDecl *M,
243436
ArrayRef<StringRef> FullModuleName,
@@ -246,18 +439,26 @@ void swift::ide::printSubmoduleInterface(
246439
ASTPrinter &Printer,
247440
const PrintOptions &Options,
248441
const bool PrintSynthesizedExtensions) {
442+
auto &SwiftContext = M->getASTContext();
443+
auto &Importer =
444+
static_cast<ClangImporter &>(*SwiftContext.getClangModuleLoader());
445+
249446
auto AdjustedOptions = Options;
250447
adjustPrintOptions(AdjustedOptions);
251448

449+
// If we end up printing decls from any cross-import overlay modules, make
450+
// sure we map any qualifying module references to the underlying module.
451+
Optional<OverlayCollector> OverlayCollector;
452+
AdjustedOptions.mapModuleToUnderlying = [&](const ModuleDecl *M) {
453+
return OverlayCollector
454+
? OverlayCollector->mapToUnderlyingModule(M)
455+
: M;
456+
};
457+
252458
SmallVector<Decl *, 1> Decls;
253459
M->getDisplayDecls(Decls);
254460

255-
auto &SwiftContext = M->getASTContext();
256-
auto &Importer =
257-
static_cast<ClangImporter &>(*SwiftContext.getClangModuleLoader());
258-
259461
const clang::Module *InterestingClangModule = nullptr;
260-
261462
SmallVector<ImportDecl *, 1> ImportDecls;
262463
llvm::DenseSet<const clang::Module *> ClangModulesForImports;
263464
SmallVector<Decl *, 1> SwiftDecls;
@@ -429,9 +630,8 @@ void swift::ide::printSubmoduleInterface(
429630
ImportDecls.push_back(createImportDecl(M->getASTContext(), M, SM, {}));
430631
}
431632

633+
// Sort imported clang declarations in source order *within a submodule*.
432634
auto &ClangSourceManager = Importer.getClangASTContext().getSourceManager();
433-
434-
// Sort imported declarations in source order *within a submodule*.
435635
for (auto &P : ClangDecls) {
436636
std::stable_sort(P.second.begin(), P.second.end(),
437637
[&](std::pair<Decl *, clang::SourceLocation> LHS,
@@ -442,39 +642,12 @@ void swift::ide::printSubmoduleInterface(
442642
}
443643

444644
// Sort Swift declarations so that we print them in a consistent order.
445-
std::sort(ImportDecls.begin(), ImportDecls.end(),
446-
[](ImportDecl *LHS, ImportDecl *RHS) -> bool {
447-
auto LHSPath = LHS->getFullAccessPath();
448-
auto RHSPath = RHS->getFullAccessPath();
449-
for (unsigned i = 0, e = std::min(LHSPath.size(), RHSPath.size()); i != e;
450-
i++) {
451-
if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str()))
452-
return Ret < 0;
453-
}
454-
return false;
455-
});
645+
std::sort(ImportDecls.begin(), ImportDecls.end(), compareImports);
456646

457647
// If the group name is specified, we sort them according to their source order,
458648
// which is the order preserved by getTopLevelDecls.
459-
if (GroupNames.empty()) {
460-
std::stable_sort(SwiftDecls.begin(), SwiftDecls.end(),
461-
[&](Decl *LHS, Decl *RHS) -> bool {
462-
auto *LHSValue = dyn_cast<ValueDecl>(LHS);
463-
auto *RHSValue = dyn_cast<ValueDecl>(RHS);
464-
465-
if (LHSValue && RHSValue) {
466-
auto LHSName = LHSValue->getBaseName();
467-
auto RHSName = RHSValue->getBaseName();
468-
if (int Ret = LHSName.compare(RHSName))
469-
return Ret < 0;
470-
// FIXME: this is not sufficient to establish a total order for overloaded
471-
// decls.
472-
return LHS->getKind() < RHS->getKind();
473-
}
474-
475-
return LHS->getKind() < RHS->getKind();
476-
});
477-
}
649+
if (GroupNames.empty())
650+
std::stable_sort(SwiftDecls.begin(), SwiftDecls.end(), compareSwiftDecls);
478651

479652
ASTPrinter *PrinterToUse = &Printer;
480653

@@ -646,6 +819,56 @@ void swift::ide::printSubmoduleInterface(
646819
if (PrintDecl(D))
647820
Printer << "\n";
648821
}
822+
823+
// If we're printing the entire target module (not specific sub-groups),
824+
// also print the decls from any underscored Swift cross-import overlays it
825+
// is the underlying module of, transitively.
826+
if (GroupNames.empty()) {
827+
OverlayCollector.emplace(M, SwiftContext);
828+
829+
SmallVector<Decl *, 1> OverlayDecls;
830+
SmallVector<StringRef, 1> Bystanders;
831+
832+
for (auto &Overlay: OverlayCollector->getOverlays()) {
833+
OverlayDecls.clear();
834+
auto DeclLists = Overlay.getDecls(OverlayDecls, Options.AccessFilter);
835+
836+
// Ignore overlays without any decls
837+
if (OverlayDecls.empty())
838+
continue;
839+
840+
Bystanders.clear();
841+
Overlay.getBystanders(Bystanders);
842+
assert(!Bystanders.empty() && "Overlay with no bystanders?");
843+
844+
std::string BystanderList;
845+
for (size_t I: range(Bystanders.size())) {
846+
if (I == Bystanders.size() - 1) {
847+
if (I != 0)
848+
BystanderList += " and ";
849+
} else if (I != 0) {
850+
BystanderList += ", ";
851+
}
852+
BystanderList += Bystanders[I].str();
853+
}
854+
855+
Printer << "\n// MARK: - " << BystanderList << " Additions\n\n";
856+
for (auto *Import : DeclLists.first)
857+
PrintDecl(Import);
858+
Printer << "\n";
859+
860+
std::string PerDeclComment = "// Available when " + BystanderList;
861+
PerDeclComment += Bystanders.size() == 1 ? " is" : " are";
862+
PerDeclComment += " imported with " + M->getNameStr().str();
863+
864+
for (auto *D : DeclLists.second) {
865+
// FIXME: only print this comment if the decl is actually printed.
866+
Printer << PerDeclComment << "\n";
867+
if (PrintDecl(D))
868+
Printer << "\n";
869+
}
870+
}
871+
}
649872
}
650873
}
651874

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
%YAML 1.2
2+
---
3+
version: 1
4+
modules:
5+
- name: _ABAdditions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -swift-version 5 -enable-library-evolution -module-name A
3+
4+
import Swift
5+
6+
public func fromA()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -swift-version 5 -enable-library-evolution -module-name B
3+
4+
import Swift
5+
6+
public func fromB()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -swift-version 5 -enable-library-evolution -module-name C
3+
4+
import Swift
5+
6+
public func fromC()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
%YAML 1.2
2+
---
3+
version: 1
4+
modules:
5+
- name: __ABAdditionsCAdditions

0 commit comments

Comments
 (0)