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+
801844struct 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
914998bool 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
0 commit comments