Skip to content

Commit 083e95c

Browse files
author
Nathan Hawes
authored
Merge pull request #31227 from nathawes/cross-import-tooling-fixes
[IDE][AST] Handle frameworks with traditional overlays where the underlying module declares cross imports in sourcekit
2 parents 4b8db7c + e311480 commit 083e95c

16 files changed

+846
-32
lines changed

include/swift/AST/FileUnit.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ class FileUnit : public DeclContext {
272272
return false;
273273
}
274274

275+
virtual ModuleDecl *getUnderlyingModuleIfOverlay() const {
276+
return nullptr;
277+
}
278+
275279
/// Returns the associated clang module if one exists.
276280
virtual const clang::Module *getUnderlyingClangModule() const {
277281
return nullptr;

include/swift/AST/Module.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,16 @@ class ModuleDecl : public DeclContext, public TypeDecl {
406406
/// present overlays as if they were part of their underlying module.
407407
std::pair<ModuleDecl *, Identifier> getDeclaringModuleAndBystander();
408408

409+
/// If this is a traditional (non-cross-import) overlay, get its underlying
410+
/// module if one exists.
411+
ModuleDecl *getUnderlyingModuleIfOverlay() const;
412+
409413
public:
410414

411415
/// Returns true if this module is an underscored cross import overlay
412-
/// declared by \p other, either directly or transitively (via intermediate
413-
/// cross-import overlays - for cross-imports involving more than two
414-
/// modules).
416+
/// declared by \p other or its underlying clang module, either directly or
417+
/// transitively (via intermediate cross-import overlays - for cross-imports
418+
/// involving more than two modules).
415419
bool isCrossImportOverlayOf(ModuleDecl *other);
416420

417421
/// If this module is an underscored cross-import overlay, returns the
@@ -420,16 +424,18 @@ class ModuleDecl : public DeclContext, public TypeDecl {
420424
/// cross-imports involving more than two modules).
421425
ModuleDecl *getDeclaringModuleIfCrossImportOverlay();
422426

423-
/// If this module is an underscored cross-import overlay of \p declaring
424-
/// either directly or transitively, populates \p bystanderNames with the set
425-
/// of bystander modules that must be present alongside \p declaring for
426-
/// the overlay to be imported and returns true. Returns false otherwise.
427+
/// If this module is an underscored cross-import overlay of \p declaring or
428+
/// its underlying clang module, either directly or transitively, populates
429+
/// \p bystanderNames with the set of bystander modules that must be present
430+
/// alongside \p declaring for the overlay to be imported and returns true.
431+
/// Returns false otherwise.
427432
bool getRequiredBystandersIfCrossImportOverlay(
428433
ModuleDecl *declaring, SmallVectorImpl<Identifier> &bystanderNames);
429434

430435

431436
/// Walks and loads the declared, underscored cross-import overlays of this
432-
/// module, transitively, to find all overlays this module underlies.
437+
/// module and its underlying clang module, transitively, to find all cross
438+
/// import overlays this module underlies.
433439
///
434440
/// This is used by tooling to present these overlays as part of this module.
435441
void findDeclaredCrossImportOverlaysTransitive(

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,8 @@ class SerializedASTFile final : public LoadedFile {
416416

417417
virtual const clang::Module *getUnderlyingClangModule() const override;
418418

419+
virtual ModuleDecl *getUnderlyingModuleIfOverlay() const override;
420+
419421
virtual bool getAllGenericSignatures(
420422
SmallVectorImpl<GenericSignature> &genericSignatures)
421423
override;

lib/AST/Module.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,14 @@ bool ModuleDecl::walk(ASTWalker &Walker) {
17241724
return false;
17251725
}
17261726

1727+
ModuleDecl *ModuleDecl::getUnderlyingModuleIfOverlay() const {
1728+
for (auto *FU : getFiles()) {
1729+
if (auto *Mod = FU->getUnderlyingModuleIfOverlay())
1730+
return Mod;
1731+
}
1732+
return nullptr;
1733+
}
1734+
17271735
const clang::Module *ModuleDecl::findUnderlyingClangModule() const {
17281736
for (auto *FU : getFiles()) {
17291737
if (auto *Mod = FU->getUnderlyingClangModule())
@@ -1827,6 +1835,9 @@ void ModuleDecl::findDeclaredCrossImportOverlaysTransitive(
18271835
SourceLoc unused;
18281836

18291837
worklist.push_back(this);
1838+
if (auto *clangModule = getUnderlyingModuleIfOverlay())
1839+
worklist.push_back(clangModule);
1840+
18301841
while (!worklist.empty()) {
18311842
ModuleDecl *current = worklist.back();
18321843
worklist.pop_back();
@@ -1846,13 +1857,37 @@ void ModuleDecl::findDeclaredCrossImportOverlaysTransitive(
18461857
if (seen.insert(overlayMod).second) {
18471858
overlayModules.push_back(overlayMod);
18481859
worklist.push_back(overlayMod);
1860+
if (auto *clangModule = overlayMod->getUnderlyingModuleIfOverlay())
1861+
worklist.push_back(clangModule);
18491862
}
18501863
}
18511864
}
18521865
}
18531866
}
18541867
}
18551868

1869+
namespace {
1870+
using CrossImportMap =
1871+
llvm::SmallDenseMap<Identifier, SmallVector<OverlayFile *, 1>>;
1872+
1873+
1874+
Identifier getBystanderIfDeclaring(ModuleDecl *mod, ModuleDecl *overlay,
1875+
CrossImportMap modCrossImports) {
1876+
auto ret = std::find_if(modCrossImports.begin(), modCrossImports.end(),
1877+
[&](CrossImportMap::iterator::value_type &pair) {
1878+
for (OverlayFile *file: std::get<1>(pair)) {
1879+
ArrayRef<Identifier> overlays = file->getOverlayModuleNames(
1880+
mod, SourceLoc(), std::get<0>(pair));
1881+
if (std::find(overlays.begin(), overlays.end(),
1882+
overlay->getName()) != overlays.end())
1883+
return true;
1884+
}
1885+
return false;
1886+
});
1887+
return ret != modCrossImports.end() ? ret->first : Identifier();
1888+
}
1889+
}
1890+
18561891
std::pair<ModuleDecl *, Identifier>
18571892
ModuleDecl::getDeclaringModuleAndBystander() {
18581893
if (declaringModuleAndBystander)
@@ -1875,25 +1910,19 @@ ModuleDecl::getDeclaringModuleAndBystander() {
18751910
if (!seen.insert(importedModule).second)
18761911
continue;
18771912

1878-
using CrossImportMap =
1879-
llvm::SmallDenseMap<Identifier, SmallVector<OverlayFile *, 1>>;
1880-
CrossImportMap &crossImports = importedModule->declaredCrossImports;
1881-
1882-
// If any of MD's cross imports declare this module as its overlay, report
1883-
// MD as the underlying module.
1884-
auto ret = std::find_if(crossImports.begin(), crossImports.end(),
1885-
[&](CrossImportMap::iterator::value_type &pair) {
1886-
for (OverlayFile *file: std::get<1>(pair)) {
1887-
ArrayRef<Identifier> overlays = file->getOverlayModuleNames(
1888-
importedModule, SourceLoc(), std::get<0>(pair));
1889-
if (std::find(overlays.begin(), overlays.end(),
1890-
overlayModule->getName()) != overlays.end())
1891-
return true;
1892-
}
1893-
return false;
1894-
});
1895-
if (ret != crossImports.end())
1896-
return *(declaringModuleAndBystander = {importedModule, ret->first});
1913+
Identifier bystander = getBystanderIfDeclaring(
1914+
importedModule, overlayModule, importedModule->declaredCrossImports);
1915+
if (!bystander.empty())
1916+
return *(declaringModuleAndBystander = {importedModule, bystander});
1917+
1918+
// Also check the imported module's underlying module if it's a traditional
1919+
// overlay (i.e. not a cross-import overlay).
1920+
if (auto *clangModule = importedModule->getUnderlyingModuleIfOverlay()) {
1921+
Identifier bystander = getBystanderIfDeclaring(
1922+
clangModule, overlayModule, clangModule->declaredCrossImports);
1923+
if (!bystander.empty())
1924+
return *(declaringModuleAndBystander = {clangModule, bystander});
1925+
}
18971926

18981927
if (!importedModule->hasUnderscoredNaming())
18991928
continue;
@@ -1909,8 +1938,9 @@ ModuleDecl::getDeclaringModuleAndBystander() {
19091938

19101939
bool ModuleDecl::isCrossImportOverlayOf(ModuleDecl *other) {
19111940
ModuleDecl *current = this;
1941+
ModuleDecl *otherClang = other->getUnderlyingModuleIfOverlay();
19121942
while ((current = current->getDeclaringModuleAndBystander().first)) {
1913-
if (current == other)
1943+
if (current == other || current == otherClang)
19141944
return true;
19151945
}
19161946
return false;
@@ -1925,10 +1955,11 @@ ModuleDecl *ModuleDecl::getDeclaringModuleIfCrossImportOverlay() {
19251955

19261956
bool ModuleDecl::getRequiredBystandersIfCrossImportOverlay(
19271957
ModuleDecl *declaring, SmallVectorImpl<Identifier> &bystanderNames) {
1958+
auto *clangModule = declaring->getUnderlyingModuleIfOverlay();
19281959
auto current = std::make_pair(this, Identifier());
19291960
while ((current = current.first->getDeclaringModuleAndBystander()).first) {
19301961
bystanderNames.push_back(current.second);
1931-
if (current.first == declaring)
1962+
if (current.first == declaring || current.first == clangModule)
19321963
return true;
19331964
}
19341965
return false;

lib/IDE/ModuleInterfacePrinting.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ getDeclsFromCrossImportOverlay(ModuleDecl *Overlay, ModuleDecl *Declaring,
429429
auto NewEnd = std::partition(Decls.begin(), Decls.end(), [&](Decl *D) {
430430
if (auto *ID = dyn_cast<ImportDecl>(D)) {
431431
ModuleDecl *Imported = ID->getModule();
432+
if (!Imported)
433+
return true;
434+
432435
// Ignore imports of the underlying module, or any cross-import
433436
// that would map back to it.
434437
if (Imported == Declaring || Imported->isCrossImportOverlayOf(Declaring))
@@ -497,8 +500,13 @@ static void printCrossImportOverlays(ModuleDecl *Declaring, ASTContext &Ctx,
497500
continue;
498501

499502
Bystanders.clear();
500-
Overlay->getRequiredBystandersIfCrossImportOverlay(Declaring, Bystanders);
501-
assert(!Bystanders.empty() && "Overlay with no bystanders?");
503+
auto BystandersValid =
504+
Overlay->getRequiredBystandersIfCrossImportOverlay(Declaring, Bystanders);
505+
506+
// Ignore badly formed overlays that don't import their declaring module.
507+
if (!BystandersValid)
508+
continue;
509+
502510
std::sort(Bystanders.begin(), Bystanders.end(),
503511
[](Identifier LHS, Identifier RHS) {
504512
return LHS.str() < RHS.str();

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,10 @@ StringRef SerializedASTFile::getTargetTriple() const {
12181218
return File.getTargetTriple();
12191219
}
12201220

1221+
ModuleDecl *SerializedASTFile::getUnderlyingModuleIfOverlay() const {
1222+
return File.getUnderlyingModule();
1223+
}
1224+
12211225
const clang::Module *SerializedASTFile::getUnderlyingClangModule() const {
12221226
if (auto *UnderlyingModule = File.getUnderlyingModule())
12231227
return UnderlyingModule->findUnderlyingClangModule();

test/CrossImport/Inputs/rewrite-module-triples.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ def rewrite(parent, names, copy_fn, rm_fn):
4343
else:
4444
copy_fn(path, new_path)
4545

46-
rm_fn(path)
46+
if platform.system() == 'Windows':
47+
rm_fn(u'\\'.join([u'\\\\?', os.path.normpath(path)]))
48+
else:
49+
rm_fn(path)
4750

4851

4952
for parent, dirs, files in os.walk(root_dir, topdown=False):
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: cp -r %S/../../CrossImport/Inputs/lib-templates/* %t/
3+
// RUN: %{python} %S/../../CrossImport/Inputs/rewrite-module-triples.py %t %module-target-triple
4+
5+
import BystandingLibrary
6+
7+
8+
import SwiftFramework
9+
10+
fromSwiftFramework()
11+
fromSwiftFrameworkCrossImport()
12+
13+
14+
import ClangFramework
15+
16+
fromClangFramework()
17+
fromClangFrameworkCrossImport()
18+
19+
20+
import OverlaidClangFramework
21+
22+
fromOverlaidClangFramework()
23+
fromOverlaidClangFrameworkOverlay()
24+
fromOverlaidClangFrameworkCrossImport()
25+
26+
27+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=10:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKSWIFT %s
28+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=11:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKSWIFT %s
29+
30+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=16:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKCLANG %s
31+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=17:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKCLANG %s
32+
33+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=22:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKOVERLAID %s
34+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=23:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKOVERLAID %s
35+
// RUN: %sourcekitd-test -req=cursor -print-raw-response -pos=24:1 %s -- -target %target-triple -Xfrontend -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks %s | %FileCheck -check-prefix=CHECKOVERLAID %s
36+
37+
// CHECKSWIFT: key.modulename: "SwiftFramework"
38+
// CHECKCLANG: key.modulename: "ClangFramework"
39+
// CHECKOVERLAID: key.modulename: "OverlaidClangFramework"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/mcp)
3+
// RUN: cp -r %S/../../CrossImport/Inputs/lib-templates/* %t/
4+
// RUN: %{python} %S/../../CrossImport/Inputs/rewrite-module-triples.py %t %module-target-triple
5+
6+
7+
// RUN: %sourcekitd-test -req=doc-info -module SwiftFramework -- -target %target-triple -I %t/include -I %t/lib/swift -F %t/Frameworks -module-cache-path %t/mcp > %t.response
8+
// RUN: %diff -u %s.SwiftFramework.response %t.response
9+
10+
// RUN: %sourcekitd-test -req=doc-info -module ClangFramework -- -target %target-triple -I %t/include -I %t/lib/swift -F %t/Frameworks -module-cache-path %t/mcp > %t.response
11+
// RUN: %diff -u %s.ClangFramework.response %t.response
12+
13+
// RUN: %sourcekitd-test -req=doc-info -module OverlaidClangFramework -- -target %target-triple -I %t/include -I %t/lib/swift -F %t/Frameworks -module-cache-path %t/mcp > %t.response
14+
// RUN: %diff -u %s.OverlaidClangFramework.response %t.response
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
2+
func fromClangFramework()
3+
4+
// MARK: - BystandingLibrary Additions
5+
6+
import SwiftOnoneSupport
7+
8+
// Available when BystandingLibrary is imported with ClangFramework
9+
func fromClangFrameworkCrossImport()
10+
11+
12+
[
13+
{
14+
key.kind: source.lang.swift.syntaxtype.keyword,
15+
key.offset: 1,
16+
key.length: 4
17+
},
18+
{
19+
key.kind: source.lang.swift.syntaxtype.identifier,
20+
key.offset: 6,
21+
key.length: 18
22+
},
23+
{
24+
key.kind: source.lang.swift.syntaxtype.comment,
25+
key.offset: 28,
26+
key.length: 39
27+
},
28+
{
29+
key.kind: source.lang.swift.syntaxtype.comment.mark,
30+
key.offset: 31,
31+
key.length: 35
32+
},
33+
{
34+
key.kind: source.lang.swift.syntaxtype.keyword,
35+
key.offset: 68,
36+
key.length: 6
37+
},
38+
{
39+
key.kind: source.lang.swift.syntaxtype.identifier,
40+
key.offset: 75,
41+
key.length: 17
42+
},
43+
{
44+
key.kind: source.lang.swift.syntaxtype.comment,
45+
key.offset: 94,
46+
key.length: 68
47+
},
48+
{
49+
key.kind: source.lang.swift.syntaxtype.keyword,
50+
key.offset: 162,
51+
key.length: 4
52+
},
53+
{
54+
key.kind: source.lang.swift.syntaxtype.identifier,
55+
key.offset: 167,
56+
key.length: 29
57+
}
58+
]
59+
[
60+
{
61+
key.kind: source.lang.swift.decl.function.free,
62+
key.name: "fromClangFramework()",
63+
key.usr: "c:@F@fromClangFramework",
64+
key.offset: 1,
65+
key.length: 25,
66+
key.fully_annotated_decl: "<decl.function.free><syntaxtype.keyword>func</syntaxtype.keyword> <decl.name>fromClangFramework</decl.name>()</decl.function.free>"
67+
},
68+
{
69+
key.kind: source.lang.swift.decl.function.free,
70+
key.name: "fromClangFrameworkCrossImport()",
71+
key.usr: "s:33_ClangFramework_BystandingLibrary04fromaB11CrossImportyyF",
72+
key.offset: 162,
73+
key.length: 36,
74+
key.fully_annotated_decl: "<decl.function.free><syntaxtype.keyword>func</syntaxtype.keyword> <decl.name>fromClangFrameworkCrossImport</decl.name>()</decl.function.free>",
75+
key.required_bystanders: [
76+
"BystandingLibrary"
77+
]
78+
}
79+
]

0 commit comments

Comments
 (0)