Skip to content

Commit fe79c40

Browse files
authored
Merge pull request #80972 from tshortli/member-import-visibility-cross-import-overlay
Sema: Improve MemberImportVisibility diagnostics for cross import overlays
2 parents 918bb5c + 9e67cf0 commit fe79c40

File tree

6 files changed

+194
-32
lines changed

6 files changed

+194
-32
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,13 @@ ERROR(init_candidate_inaccessible,none,
169169

170170
ERROR(candidate_from_missing_import,none,
171171
"%kind0 is not available due to missing import of defining module %1",
172-
(const ValueDecl *, ModuleDecl *))
172+
(const ValueDecl *, const ModuleDecl *))
173+
ERROR(candidate_from_missing_imports_2_or_more,none,
174+
"%kind0 is not available due to missing imports of defining modules "
175+
"%2%select{ and|, }1 %3%select{|, and others}1",
176+
(const ValueDecl *, bool, const ModuleDecl *, const ModuleDecl *))
173177
NOTE(candidate_add_import,none,
174-
"add import of module %0", (ModuleDecl *))
178+
"add import of module %0", (const ModuleDecl *))
175179

176180
ERROR(cannot_pass_rvalue_mutating_subelement,none,
177181
"cannot use mutating member on immutable value: %0",
@@ -6049,7 +6053,7 @@ ERROR(actor_isolation_multiple_attr_2,none,
60496053
"%kind0 has multiple actor-isolation attributes (%1 and %2)",
60506054
(const Decl *, DeclAttribute, DeclAttribute))
60516055
ERROR(actor_isolation_multiple_attr_3,none,
6052-
"%0 %1 has multiple actor-isolation attributes (%2, %3 and %4)",
6056+
"%0 %1 has multiple actor-isolation attributes (%2, %3, and %4)",
60536057
(const Decl *, DeclAttribute, DeclAttribute, DeclAttribute))
60546058
ERROR(actor_isolation_multiple_attr_4,none,
60556059
"%0 %1 has multiple actor-isolation attributes (%2, %3, %4, and %5)",

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 116 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "TypoCorrection.h"
2222
#include "swift/AST/ConformanceLookup.h"
2323
#include "swift/AST/ExistentialLayout.h"
24+
#include "swift/AST/ImportCache.h"
2425
#include "swift/AST/Initializer.h"
2526
#include "swift/AST/NameLookup.h"
2627
#include "swift/AST/NameLookupRequests.h"
@@ -798,7 +799,50 @@ TypoCorrectionResults::claimUniqueCorrection() {
798799
return SyntacticTypoCorrection(WrittenName, Loc, uniqueCorrectedName);
799800
}
800801

802+
/// Returns a sorted vector of modules that are not imported in the given
803+
/// `SourceFile` and must be in order to make declarations from \p owningModule
804+
/// visible.
805+
static SmallVector<ModuleDecl *, 2>
806+
missingImportsForDefiningModule(ModuleDecl *owningModule, SourceFile &sf) {
807+
SmallVector<ModuleDecl *, 2> result;
808+
auto &ctx = sf.getASTContext();
809+
810+
if (auto *declaringModule =
811+
owningModule->getDeclaringModuleIfCrossImportOverlay()) {
812+
// If the module that owns the declaration is a cross import overlay the
813+
// fix-its should suggest importing the declaring and bystanding modules,
814+
// not the overlay module.
815+
result.push_back(declaringModule);
816+
817+
SmallVector<Identifier, 2> bystanders;
818+
if (owningModule->getRequiredBystandersIfCrossImportOverlay(declaringModule,
819+
bystanders)) {
820+
for (auto bystander : bystanders) {
821+
if (auto bystanderModule = ctx.getModuleByIdentifier(bystander))
822+
result.push_back(bystanderModule);
823+
}
824+
}
825+
826+
// Remove the modules that are already imported by the source file.
827+
auto &importCache = ctx.getImportCache();
828+
const DeclContext *dc = &sf;
829+
llvm::erase_if(result, [&](ModuleDecl *candidate) {
830+
return importCache.isImportedBy(candidate, dc);
831+
});
832+
} else {
833+
// Just the module that owns the declaration is required.
834+
result.push_back(owningModule);
835+
}
836+
837+
std::sort(result.begin(), result.end(), [](ModuleDecl *LHS, ModuleDecl *RHS) {
838+
return LHS->getNameStr() < LHS->getNameStr();
839+
});
840+
841+
return result;
842+
}
843+
801844
struct MissingImportFixItInfo {
845+
const ModuleDecl *moduleToImport = nullptr;
802846
OptionSet<ImportFlags> flags;
803847
std::optional<AccessLevel> accessLevel;
804848
};
@@ -807,15 +851,13 @@ class MissingImportFixItCache {
807851
SourceFile &sf;
808852
llvm::DenseMap<const ModuleDecl *, MissingImportFixItInfo> infos;
809853

810-
public:
811-
MissingImportFixItCache(SourceFile &sf) : sf(sf){};
812-
813-
MissingImportFixItInfo getInfo(const ModuleDecl *mod) {
854+
MissingImportFixItInfo getFixItInfo(ModuleDecl *mod) {
814855
auto existing = infos.find(mod);
815856
if (existing != infos.end())
816857
return existing->getSecond();
817858

818859
MissingImportFixItInfo info;
860+
info.moduleToImport = mod;
819861

820862
// Find imports of the defining module in other source files and aggregate
821863
// the attributes and access level usage on those imports collectively. This
@@ -845,33 +887,49 @@ class MissingImportFixItCache {
845887
infos[mod] = info;
846888
return info;
847889
}
848-
};
849890

850-
static void diagnoseMissingImportForMember(const ValueDecl *decl,
851-
SourceFile *sf, SourceLoc loc) {
852-
auto &ctx = sf->getASTContext();
853-
auto definingModule = decl->getModuleContextForNameLookup();
854-
ctx.Diags.diagnose(loc, diag::candidate_from_missing_import, decl,
855-
definingModule);
856-
}
891+
public:
892+
MissingImportFixItCache(SourceFile &sf) : sf(sf) {};
857893

858-
static void
859-
diagnoseAndFixMissingImportForMember(const ValueDecl *decl, SourceFile *sf,
860-
SourceLoc loc,
861-
MissingImportFixItCache &fixItCache) {
894+
std::pair<SmallVector<ModuleDecl *, 2>,
895+
SmallVector<MissingImportFixItInfo, 2>>
896+
getModulesAndFixIts(ModuleDecl *mod) {
897+
auto modulesToImport = missingImportsForDefiningModule(mod, sf);
898+
SmallVector<MissingImportFixItInfo, 2> fixItInfos;
862899

863-
diagnoseMissingImportForMember(decl, sf, loc);
900+
for (auto *mod : modulesToImport) {
901+
fixItInfos.emplace_back(getFixItInfo(mod));
902+
}
864903

904+
return {modulesToImport, fixItInfos};
905+
}
906+
};
907+
908+
static void
909+
diagnoseMissingImportsForMember(const ValueDecl *decl,
910+
SmallVectorImpl<ModuleDecl *> &modulesToImport,
911+
SourceFile *sf, SourceLoc loc) {
865912
auto &ctx = sf->getASTContext();
866-
auto definingModule = decl->getModuleContextForNameLookup();
867-
SourceLoc bestLoc = ctx.Diags.getBestAddImportFixItLoc(decl, sf);
868-
if (!bestLoc.isValid())
869-
return;
913+
auto count = modulesToImport.size();
914+
ASSERT(count > 0);
915+
916+
if (count > 1) {
917+
ctx.Diags.diagnose(loc, diag::candidate_from_missing_imports_2_or_more,
918+
decl, bool(count > 2), modulesToImport[0],
919+
modulesToImport[1]);
920+
} else {
921+
ctx.Diags.diagnose(loc, diag::candidate_from_missing_import, decl,
922+
modulesToImport.front());
923+
}
924+
}
870925

926+
static void emitMissingImportFixIt(SourceLoc loc,
927+
const MissingImportFixItInfo &fixItInfo,
928+
const ValueDecl *decl) {
929+
ASTContext &ctx = decl->getASTContext();
871930
llvm::SmallString<64> importText;
872931

873932
// Add flags that must be used consistently on every import in every file.
874-
auto fixItInfo = fixItCache.getInfo(definingModule);
875933
if (fixItInfo.flags.contains(ImportFlags::ImplementationOnly))
876934
importText += "@_implementationOnly ";
877935
if (fixItInfo.flags.contains(ImportFlags::WeakLinked))
@@ -905,10 +963,36 @@ diagnoseAndFixMissingImportForMember(const ValueDecl *decl, SourceFile *sf,
905963
}
906964

907965
importText += "import ";
908-
importText += definingModule->getName().str();
966+
importText += fixItInfo.moduleToImport->getName().str();
909967
importText += "\n";
910-
ctx.Diags.diagnose(bestLoc, diag::candidate_add_import, definingModule)
911-
.fixItInsert(bestLoc, importText);
968+
ctx.Diags
969+
.diagnose(loc, diag::candidate_add_import, fixItInfo.moduleToImport)
970+
.fixItInsert(loc, importText);
971+
}
972+
973+
static void
974+
diagnoseAndFixMissingImportForMember(const ValueDecl *decl, SourceFile *sf,
975+
SourceLoc loc,
976+
MissingImportFixItCache &fixItCache) {
977+
978+
auto modulesAndFixits =
979+
fixItCache.getModulesAndFixIts(decl->getModuleContextForNameLookup());
980+
auto modulesToImport = modulesAndFixits.first;
981+
auto fixItInfos = modulesAndFixits.second;
982+
983+
if (modulesToImport.empty())
984+
return;
985+
986+
diagnoseMissingImportsForMember(decl, modulesToImport, sf, loc);
987+
988+
auto &ctx = sf->getASTContext();
989+
SourceLoc bestLoc = ctx.Diags.getBestAddImportFixItLoc(decl, sf);
990+
if (!bestLoc.isValid())
991+
return;
992+
993+
for (auto &fixItInfo : fixItInfos) {
994+
emitMissingImportFixIt(bestLoc, fixItInfo, decl);
995+
}
912996
}
913997

914998
bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
@@ -917,12 +1001,13 @@ bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
9171001
if (dc->isDeclImported(decl))
9181002
return false;
9191003

1004+
auto definingModule = decl->getModuleContextForNameLookup();
9201005
if (dc->getASTContext().LangOpts.EnableCXXInterop) {
9211006
// With Cxx interop enabled, there are some declarations that always belong
9221007
// to the Clang header import module which should always be implicitly
9231008
// visible. However, that module is not implicitly imported in source files
9241009
// so we need to special case it here and avoid diagnosing.
925-
if (decl->getModuleContextForNameLookup()->isClangHeaderImportModule())
1010+
if (definingModule->isClangHeaderImportModule())
9261011
return false;
9271012
}
9281013

@@ -935,7 +1020,11 @@ bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
9351020
// In lazy typechecking mode just emit the diagnostic immediately without a
9361021
// fix-it since there won't be an opportunity to emit delayed diagnostics.
9371022
if (ctx.TypeCheckerOpts.EnableLazyTypecheck) {
938-
diagnoseMissingImportForMember(decl, sf, loc);
1023+
auto modulesToImport = missingImportsForDefiningModule(definingModule, *sf);
1024+
if (modulesToImport.empty())
1025+
return false;
1026+
1027+
diagnoseMissingImportsForMember(decl, modulesToImport, sf, loc);
9391028
return true;
9401029
}
9411030

test/CrossImport/Inputs/lib-templates/lib/swift/BystandingLibrary.swiftinterface

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33

44
import Swift
55

6-
public struct BystandingLibraryTy {}
6+
public struct BystandingLibraryTy {
7+
public init()
8+
}
79

test/CrossImport/Inputs/lib-templates/lib/swift/DeclaringLibrary.swiftinterface

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33

44
import Swift
55

6-
public struct DeclaringLibraryTy {}
6+
public struct DeclaringLibraryTy {
7+
public init()
8+
}
79
public struct ShadowTy {}

test/CrossImport/Inputs/lib-templates/lib/swift/_OverlayLibrary.swiftinterface

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,11 @@ public struct OverlayLibraryTy {
1111
}
1212

1313
public struct ShadowTy {}
14+
15+
extension DeclaringLibrary.DeclaringLibraryTy {
16+
public func overlayMember()
17+
}
18+
19+
extension BystandingLibrary.BystandingLibraryTy {
20+
public func overlayMember()
21+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: cp -r %S/Inputs/lib-templates/* %t/
3+
// RUN: split-file %s %t
4+
5+
// RUN: %target-swift-frontend -typecheck -verify -enable-cross-import-overlays \
6+
// RUN: %t/OnlyDeclaring.swift \
7+
// RUN: %t/OnlyBystanding.swift \
8+
// RUN: %t/NeitherDeclaringNorBystanding.swift \
9+
// RUN: %t/BothDeclaringAndBystanding.swift \
10+
// RUN: -I %t/include -I %t/lib/swift -F %t/Frameworks \
11+
// RUN: -enable-upcoming-feature MemberImportVisibility
12+
13+
// REQUIRES: swift_feature_MemberImportVisibility
14+
15+
//--- OnlyDeclaring.swift
16+
17+
import DeclaringLibrary
18+
// expected-note 2 {{add import of module 'BystandingLibrary'}}
19+
20+
private func test() {
21+
returnsDeclaringTy().overlayMember() // expected-error {{instance method 'overlayMember()' is not available due to missing import of defining module 'BystandingLibrary'}}
22+
returnsBystandingTy().overlayMember() // expected-error {{instance method 'overlayMember()' is not available due to missing import of defining module 'BystandingLibrary'}}
23+
}
24+
25+
//--- OnlyBystanding.swift
26+
27+
import BystandingLibrary
28+
// expected-note 2 {{add import of module 'DeclaringLibrary'}}
29+
30+
private func test() {
31+
returnsDeclaringTy().overlayMember() // expected-error {{instance method 'overlayMember()' is not available due to missing import of defining module 'DeclaringLibrary'}}
32+
returnsBystandingTy().overlayMember() // expected-error {{instance method 'overlayMember()' is not available due to missing import of defining module 'DeclaringLibrary'}}
33+
}
34+
35+
//--- NeitherDeclaringNorBystanding.swift
36+
37+
import Swift
38+
// expected-note 2 {{add import of module 'BystandingLibrary'}}
39+
// expected-note@-1 2 {{add import of module 'DeclaringLibrary'}}
40+
41+
private func test() {
42+
returnsDeclaringTy().overlayMember() // expected-error {{instance method 'overlayMember()' is not available due to missing imports of defining modules 'DeclaringLibrary' and 'BystandingLibrary'}}
43+
returnsBystandingTy().overlayMember() // expected-error {{instance method 'overlayMember()' is not available due to missing imports of defining modules 'DeclaringLibrary' and 'BystandingLibrary'}}
44+
}
45+
46+
//--- BothDeclaringAndBystanding.swift
47+
48+
import DeclaringLibrary
49+
import BystandingLibrary
50+
51+
func returnsDeclaringTy() -> DeclaringLibraryTy {
52+
return DeclaringLibraryTy()
53+
}
54+
55+
func returnsBystandingTy() -> BystandingLibraryTy {
56+
return BystandingLibraryTy()
57+
}

0 commit comments

Comments
 (0)