Skip to content

Commit aca6046

Browse files
committed
AST/Sema: Make MemberImportVisibility a migratable feature.
The migration to `MemberImportVisibility` can be performed mechanically by adding missing import declarations, so offer automatic migration for the feature. Resolves rdar://151931597.
1 parent 522fee4 commit aca6046

15 files changed

+310
-30
lines changed

include/swift/AST/DiagnosticGroups.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version")
4949
GROUP(ExistentialAny, "existential-any")
5050
GROUP(ExistentialMemberAccess, "existential-member-access-limitations")
5151
GROUP(IsolatedConformances, "isolated-conformances")
52+
GROUP(MemberImportVisibility, "member-import-visibility")
5253
GROUP(MultipleInheritance, "multiple-inheritance")
5354
GROUP(MutableGlobalVariable, "mutable-global-variable")
5455
GROUP(NominalTypes, "nominal-types")

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,21 @@ ERROR(init_candidate_inaccessible,none,
167167
"'%select{private|fileprivate|internal|package|@_spi|@_spi}1' protection level",
168168
(Type, AccessLevel))
169169

170-
ERROR(candidate_from_missing_import,none,
170+
GROUPED_ERROR(member_from_missing_import,MemberImportVisibility,none,
171171
"%kind0 is not available due to missing import of defining module %1",
172172
(const ValueDecl *, const ModuleDecl *))
173-
ERROR(candidate_from_missing_imports_2_or_more,none,
173+
GROUPED_ERROR(member_from_missing_imports_2_or_more,MemberImportVisibility,none,
174174
"%kind0 is not available due to missing imports of defining modules "
175175
"%2%select{ and|, }1 %3%select{|, and others}1",
176176
(const ValueDecl *, bool, const ModuleDecl *, const ModuleDecl *))
177177
NOTE(candidate_add_import,none,
178178
"add import of module %0", (const ModuleDecl *))
179179

180+
GROUPED_WARNING(add_required_import_for_member,MemberImportVisibility,none,
181+
"import of module %0 is required", (const ModuleDecl *))
182+
NOTE(decl_from_module_used_here,none,
183+
"%kind0 from %1 used here", (const ValueDecl *, const ModuleDecl *))
184+
180185
ERROR(cannot_pass_rvalue_mutating_subelement,none,
181186
"cannot use mutating member on immutable value: %0",
182187
(StringRef))

include/swift/Basic/Features.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6)
287287
// Swift 7
288288
MIGRATABLE_UPCOMING_FEATURE(ExistentialAny, 335, 7)
289289
UPCOMING_FEATURE(InternalImportsByDefault, 409, 7)
290-
UPCOMING_FEATURE(MemberImportVisibility, 444, 7)
290+
MIGRATABLE_UPCOMING_FEATURE(MemberImportVisibility, 444, 7)
291291
MIGRATABLE_UPCOMING_FEATURE(InferIsolatedConformances, 470, 7)
292292
MIGRATABLE_UPCOMING_FEATURE(NonisolatedNonsendingByDefault, 461, 7)
293293

include/swift/Basic/LangOptions.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,13 +873,17 @@ namespace swift {
873873

874874
/// Returns whether the given feature is enabled.
875875
///
876-
/// If allowMigration is set, also returns true when the feature has been enabled for migration.
876+
/// If allowMigration is set, also returns true when the feature has been
877+
/// enabled for migration.
877878
bool hasFeature(Feature feature, bool allowMigration = false) const;
878879

879880
/// Returns whether a feature with the given name is enabled. Returns
880881
/// `false` if a feature by this name is not known.
881882
bool hasFeature(llvm::StringRef featureName) const;
882883

884+
/// Returns whether the given feature is enabled for migration.
885+
bool isMigratingToFeature(Feature feature) const;
886+
883887
/// Enables the given feature (enables in migration mode if `forMigration`
884888
/// is `true`).
885889
void enableFeature(Feature feature, bool forMigration = false);

lib/AST/NameLookup.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,7 +2371,9 @@ ObjCCategoryNameMap ClassDecl::getObjCCategoryNameMap() {
23712371
/// the given context.
23722372
static bool shouldRequireImportsInContext(const DeclContext *lookupContext) {
23732373
auto &ctx = lookupContext->getASTContext();
2374-
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility))
2374+
2375+
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
2376+
/*allowMigration=*/true))
23752377
return false;
23762378

23772379
// Code outside of the main module (which is often synthesized) isn't subject
@@ -3591,7 +3593,8 @@ ExtendedNominalRequest::evaluate(Evaluator &evaluator,
35913593
// inaccessible due to missing imports. The missing imports will be diagnosed
35923594
// elsewhere.
35933595
if (referenced.first.empty() &&
3594-
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility)) {
3596+
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
3597+
/*allowMigration=*/true)) {
35953598
options |= DirectlyReferencedTypeLookupFlags::IgnoreMissingImports;
35963599
referenced = directReferencesForTypeRepr(evaluator, ctx, typeRepr,
35973600
ext->getParent(), options);

lib/Basic/LangOptions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ bool LangOptions::hasFeature(llvm::StringRef featureName) const {
363363
return false;
364364
}
365365

366+
bool LangOptions::isMigratingToFeature(Feature feature) const {
367+
return featureStates.getState(feature).isEnabledForMigration();
368+
}
369+
366370
void LangOptions::enableFeature(Feature feature, bool forMigration) {
367371
if (forMigration) {
368372
ASSERT(feature.isMigratable());

lib/Basic/SupportedFeatures.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ static std::vector<DiagGroupID> migratableCategories(Feature feature) {
3535
return { DiagGroupID::ExistentialAny };
3636
case Feature::InnerKind::InferIsolatedConformances:
3737
return { DiagGroupID::IsolatedConformances };
38+
case Feature::InnerKind::MemberImportVisibility:
39+
return { DiagGroupID::MemberImportVisibility };
3840
case Feature::InnerKind::NonisolatedNonsendingByDefault:
3941
return { DiagGroupID::NonisolatedNonsendingByDefault };
4042
case Feature::InnerKind::StrictMemorySafety:

lib/Sema/MiscDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6322,7 +6322,8 @@ static void diagnoseMissingMemberImports(const Expr *E, const DeclContext *DC) {
63226322
};
63236323

63246324
auto &ctx = DC->getASTContext();
6325-
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility))
6325+
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
6326+
/*allowMigration=*/true))
63266327
return;
63276328

63286329
DiagnoseWalker walker(DC);

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
279279
// Some diagnostics emitted with the `MemberImportVisibility` feature enabled
280280
// subsume these diagnostics.
281281
if (originKind == DisallowedOriginKind::MissingImport &&
282-
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility) && SF)
282+
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
283+
/*allowMigration=*/true) &&
284+
SF)
283285
return false;
284286

285287
if (auto accessor = dyn_cast<AccessorDecl>(D)) {

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,8 @@ bool swift::checkOverrides(ValueDecl *decl) {
15321532

15331533
auto &ctx = decl->getASTContext();
15341534
if (overridden.empty() &&
1535-
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility)) {
1535+
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
1536+
/*allowMigration=*/true)) {
15361537
// If we didn't find anything, try broadening the search by ignoring missing
15371538
// imports.
15381539
if (!checkPotentialOverrides(decl, overridden,
@@ -2529,7 +2530,8 @@ OverriddenDeclsRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
25292530
// If we didn't find anything, try broadening the search by ignoring missing
25302531
// imports.
25312532
if (overridden.empty() &&
2532-
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility)) {
2533+
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
2534+
/*allowMigration=*/true)) {
25332535
overridden = computeOverriddenDecls(decl, true);
25342536
if (!overridden.empty()) {
25352537
auto first = overridden.front();

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,12 @@ class MissingImportFixItCache {
852852
llvm::DenseMap<const ModuleDecl *, MissingImportFixItInfo> infos;
853853
bool internalImportsByDefaultEnabled;
854854

855+
public:
856+
MissingImportFixItCache(SourceFile &sf)
857+
: sf(sf),
858+
internalImportsByDefaultEnabled(sf.getASTContext().LangOpts.hasFeature(
859+
Feature::InternalImportsByDefault)) {};
860+
855861
MissingImportFixItInfo getFixItInfo(ModuleDecl *mod) {
856862
auto existing = infos.find(mod);
857863
if (existing != infos.end())
@@ -900,12 +906,6 @@ class MissingImportFixItCache {
900906
return info;
901907
}
902908

903-
public:
904-
MissingImportFixItCache(SourceFile &sf) : sf(sf) {
905-
internalImportsByDefaultEnabled = sf.getASTContext().LangOpts.hasFeature(
906-
Feature::InternalImportsByDefault);
907-
};
908-
909909
std::pair<SmallVector<ModuleDecl *, 2>,
910910
SmallVector<MissingImportFixItInfo, 2>>
911911
getModulesAndFixIts(ModuleDecl *mod) {
@@ -929,20 +929,17 @@ diagnoseMissingImportsForMember(const ValueDecl *decl,
929929
ASSERT(count > 0);
930930

931931
if (count > 1) {
932-
ctx.Diags.diagnose(loc, diag::candidate_from_missing_imports_2_or_more,
933-
decl, bool(count > 2), modulesToImport[0],
934-
modulesToImport[1]);
932+
ctx.Diags.diagnose(loc, diag::member_from_missing_imports_2_or_more, decl,
933+
bool(count > 2), modulesToImport[0], modulesToImport[1]);
935934
} else {
936-
ctx.Diags.diagnose(loc, diag::candidate_from_missing_import, decl,
935+
ctx.Diags.diagnose(loc, diag::member_from_missing_import, decl,
937936
modulesToImport.front());
938937
}
939938
}
940939

941-
static void emitMissingImportFixIt(SourceLoc loc,
942-
const MissingImportFixItInfo &fixItInfo,
943-
ASTContext &ctx) {
944-
llvm::SmallString<64> importText;
945-
940+
static void appendMissingImportFixIt(llvm::SmallString<64> &importText,
941+
const MissingImportFixItInfo &fixItInfo,
942+
ASTContext &ctx) {
946943
// Add flags that must be used consistently on every import in every file.
947944
if (fixItInfo.flags.contains(ImportFlags::ImplementationOnly))
948945
importText += "@_implementationOnly ";
@@ -969,6 +966,12 @@ static void emitMissingImportFixIt(SourceLoc loc,
969966
importText += "import ";
970967
importText += fixItInfo.moduleToImport->getName().str();
971968
importText += "\n";
969+
}
970+
971+
static void emitMissingImportNoteAndFixIt(
972+
SourceLoc loc, const MissingImportFixItInfo &fixItInfo, ASTContext &ctx) {
973+
llvm::SmallString<64> importText;
974+
appendMissingImportFixIt(importText, fixItInfo, ctx);
972975
ctx.Diags
973976
.diagnose(loc, diag::candidate_add_import, fixItInfo.moduleToImport)
974977
.fixItInsert(loc, importText);
@@ -995,7 +998,7 @@ diagnoseAndFixMissingImportForMember(const ValueDecl *decl, SourceFile *sf,
995998
return;
996999

9971000
for (auto &fixItInfo : fixItInfos) {
998-
emitMissingImportFixIt(bestLoc, fixItInfo, ctx);
1001+
emitMissingImportNoteAndFixIt(bestLoc, fixItInfo, ctx);
9991002
}
10001003
}
10011004

@@ -1024,6 +1027,11 @@ bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
10241027
// In lazy typechecking mode just emit the diagnostic immediately without a
10251028
// fix-it since there won't be an opportunity to emit delayed diagnostics.
10261029
if (ctx.TypeCheckerOpts.EnableLazyTypecheck) {
1030+
// Lazy type-checking and migration for MemberImportVisibility are
1031+
// completely incompatible, so just skip the diagnostic entirely.
1032+
if (ctx.LangOpts.isMigratingToFeature(Feature::MemberImportVisibility))
1033+
return false;
1034+
10271035
auto modulesToImport = missingImportsForDefiningModule(definingModule, *sf);
10281036
if (modulesToImport.empty())
10291037
return false;
@@ -1036,7 +1044,76 @@ bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
10361044
return false;
10371045
}
10381046

1047+
void migrateToMemberImportVisibility(SourceFile &sf) {
1048+
auto delayedDiags = sf.takeDelayedMissingImportForMemberDiagnostics();
1049+
if (delayedDiags.empty())
1050+
return;
1051+
1052+
auto &ctx = sf.getASTContext();
1053+
auto bestLoc = ctx.Diags.getBestAddImportFixItLoc(&sf);
1054+
if (bestLoc.isInvalid())
1055+
return;
1056+
1057+
// Collect the distinct modules that need to be imported and map them
1058+
// to the collection of declarations which are used in the file and belong
1059+
// to the module.
1060+
llvm::SmallVector<ModuleDecl *, 8> modulesToImport;
1061+
llvm::SmallDenseMap<ModuleDecl *, std::vector<const ValueDecl *>>
1062+
declsByModuleToImport;
1063+
for (auto declAndLocs : delayedDiags) {
1064+
auto decl = declAndLocs.first;
1065+
auto definingModules = missingImportsForDefiningModule(
1066+
decl->getModuleContextForNameLookup(), sf);
1067+
1068+
for (auto definingModule : definingModules) {
1069+
auto existing = declsByModuleToImport.find(definingModule);
1070+
if (existing != declsByModuleToImport.end()) {
1071+
existing->second.push_back(decl);
1072+
} else {
1073+
declsByModuleToImport[definingModule] = {decl};
1074+
modulesToImport.push_back(definingModule);
1075+
}
1076+
}
1077+
}
1078+
1079+
// Emit one warning for each module that needcs to be imported and emit notes
1080+
// for each reference to a declaration from that module in the file.
1081+
llvm::sort(modulesToImport, [](ModuleDecl *lhs, ModuleDecl *rhs) -> int {
1082+
return lhs->getName().compare(rhs->getName());
1083+
});
1084+
1085+
auto fixItCache = MissingImportFixItCache(sf);
1086+
for (auto mod : modulesToImport) {
1087+
auto fixItInfo = fixItCache.getFixItInfo(mod);
1088+
llvm::SmallString<64> importText;
1089+
appendMissingImportFixIt(importText, fixItInfo, ctx);
1090+
ctx.Diags.diagnose(bestLoc, diag::add_required_import_for_member, mod)
1091+
.fixItInsert(bestLoc, importText);
1092+
1093+
auto decls = declsByModuleToImport.find(mod);
1094+
if (decls == declsByModuleToImport.end())
1095+
continue;
1096+
1097+
for (auto decl : decls->second) {
1098+
auto locs = delayedDiags.find(decl);
1099+
if (locs == delayedDiags.end())
1100+
continue;
1101+
1102+
for (auto loc : locs->second) {
1103+
ctx.Diags.diagnose(loc, diag::decl_from_module_used_here, decl, mod);
1104+
}
1105+
}
1106+
}
1107+
}
1108+
10391109
void swift::diagnoseMissingImports(SourceFile &sf) {
1110+
// Missing import diagnostics should be emitted differently in "migrate" mode.
1111+
if (sf.getASTContext().LangOpts.isMigratingToFeature(
1112+
Feature::MemberImportVisibility)) {
1113+
migrateToMemberImportVisibility(sf);
1114+
return;
1115+
}
1116+
10401117
auto delayedDiags = sf.takeDelayedMissingImportForMemberDiagnostics();
10411118
auto fixItCache = MissingImportFixItCache(sf);
10421119

lib/Sema/TypeCheckType.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,8 @@ resolveUnqualifiedIdentTypeRepr(const TypeResolution &resolution,
17721772
// If the look up did not yield any results, try again but allow members from
17731773
// modules that are not directly imported to be accessible.
17741774
bool didIgnoreMissingImports = false;
1775-
if (!globals && ctx.LangOpts.hasFeature(Feature::MemberImportVisibility)) {
1775+
if (!globals && ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
1776+
/*allowMigration=*/true)) {
17761777
lookupOptions |= NameLookupFlags::IgnoreMissingImports;
17771778
globals = TypeChecker::lookupUnqualifiedType(DC, id, repr->getLoc(),
17781779
lookupOptions);
@@ -2035,8 +2036,8 @@ static Type resolveQualifiedIdentTypeRepr(const TypeResolution &resolution,
20352036
DC, parentTy, repr->getNameRef(), repr->getLoc(), lookupOptions);
20362037

20372038
// If no members were found, try ignoring missing imports.
2038-
if (!memberTypes &&
2039-
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility)) {
2039+
if (!memberTypes && ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
2040+
/*allowMigration=*/true)) {
20402041
lookupOptions |= NameLookupFlags::IgnoreMissingImports;
20412042
memberTypes = TypeChecker::lookupMemberType(
20422043
DC, parentTy, repr->getNameRef(), repr->getLoc(), lookupOptions);

lib/Sema/TypeOfReference.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2698,7 +2698,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
26982698
increaseScore(SK_Unavailable, locator);
26992699

27002700
// If the declaration is from a module that hasn't been imported, note that.
2701-
if (getASTContext().LangOpts.hasFeature(Feature::MemberImportVisibility)) {
2701+
if (getASTContext().LangOpts.hasFeature(Feature::MemberImportVisibility,
2702+
/*allowMigration=*/true)) {
27022703
if (!useDC->isDeclImported(decl))
27032704
increaseScore(SK_MissingImport, locator);
27042705
}

0 commit comments

Comments
 (0)