1515// declaration of members (such as constructors).
1616//
1717// ===----------------------------------------------------------------------===//
18+
19+ #include " TypeCheckAvailability.h"
1820#include " TypeChecker.h"
1921#include " TypoCorrection.h"
2022#include " swift/AST/ExistentialLayout.h"
@@ -755,43 +757,87 @@ TypoCorrectionResults::claimUniqueCorrection() {
755757 return SyntacticTypoCorrection (WrittenName, Loc, uniqueCorrectedName);
756758}
757759
758- bool swift::diagnoseMissingImportForMember (const ValueDecl *decl,
759- const DeclContext *dc,
760- SourceLoc loc) {
761- if (decl->findImport (dc))
762- return false ;
760+ struct MissingImportFixItInfo {
761+ OptionSet<ImportFlags> flags;
762+ std::optional<AccessLevel> accessLevel;
763+ };
764+
765+ class MissingImportFixItCache {
766+ SourceFile &sf;
767+ llvm::DenseMap<const ModuleDecl *, MissingImportFixItInfo> infos;
768+
769+ public:
770+ MissingImportFixItCache (SourceFile &sf) : sf(sf){};
771+
772+ MissingImportFixItInfo getInfo (const ModuleDecl *mod) {
773+ auto existing = infos.find (mod);
774+ if (existing != infos.end ())
775+ return existing->getSecond ();
776+
777+ MissingImportFixItInfo info;
778+
779+ // Find imports of the defining module in other source files and aggregate
780+ // the attributes and access level usage on those imports collectively. This
781+ // information can be used to emit a fix-it that is consistent with
782+ // how the module is imported in the rest of the module.
783+ auto parentModule = sf.getParentModule ();
784+ bool foundImport = false ;
785+ bool anyImportHasAccessLevel = false ;
786+ for (auto file : parentModule->getFiles ()) {
787+ if (auto otherSF = dyn_cast<SourceFile>(file)) {
788+ unsigned flagsInSourceFile = 0x0 ;
789+ otherSF->forEachImportOfModule (
790+ mod, [&](AttributedImport<ImportedModule> &import ) {
791+ foundImport = true ;
792+ anyImportHasAccessLevel |= import .accessLevelRange .isValid ();
793+ flagsInSourceFile |= import .options .toRaw ();
794+ });
795+ info.flags |= ImportFlags (flagsInSourceFile);
796+ }
797+ }
763798
764- auto &ctx = dc->getASTContext ();
799+ // Add an appropriate access level as long as it would not conflict with
800+ // existing imports that lack access levels.
801+ if (!foundImport || anyImportHasAccessLevel)
802+ info.accessLevel = sf.getMaxAccessLevelUsingImport (mod);
803+
804+ infos[mod] = info;
805+ return info;
806+ }
807+ };
808+
809+ static void diagnoseMissingImportForMember (const ValueDecl *decl,
810+ SourceFile *sf, SourceLoc loc) {
811+ auto &ctx = sf->getASTContext ();
765812 auto definingModule = decl->getModuleContext ();
766813 ctx.Diags .diagnose (loc, diag::candidate_from_missing_import,
767814 decl->getDescriptiveKind (), decl->getName (),
768815 definingModule);
816+ }
817+
818+ static void
819+ diagnoseAndFixMissingImportForMember (const ValueDecl *decl, SourceFile *sf,
820+ SourceLoc loc,
821+ MissingImportFixItCache &fixItCache) {
822+
823+ diagnoseMissingImportForMember (decl, sf, loc);
769824
770- SourceLoc bestLoc =
771- ctx.Diags .getBestAddImportFixItLoc (decl, dc->getParentSourceFile ());
825+ auto &ctx = sf->getASTContext ();
826+ auto definingModule = decl->getModuleContext ();
827+ SourceLoc bestLoc = ctx.Diags .getBestAddImportFixItLoc (decl, sf);
772828 if (!bestLoc.isValid ())
773- return false ;
829+ return ;
774830
775831 llvm::SmallString<64 > importText;
776832
777- // Check other source files for import flags that should be applied to the
778- // fix-it for consistency with the rest of the imports in the module.
779- auto parentModule = dc->getParentModule ();
780- OptionSet<ImportFlags> flags;
781- for (auto file : parentModule->getFiles ()) {
782- if (auto sf = dyn_cast<SourceFile>(file))
783- flags |= sf->getImportFlags (definingModule);
784- }
785-
786- if (flags.contains (ImportFlags::ImplementationOnly))
833+ auto fixItInfo = fixItCache.getInfo (definingModule);
834+ if (fixItInfo.flags .contains (ImportFlags::ImplementationOnly))
787835 importText += " @_implementationOnly " ;
788- if (flags.contains (ImportFlags::WeakLinked))
836+ if (fixItInfo. flags .contains (ImportFlags::WeakLinked))
789837 importText += " @_weakLinked " ;
790- if (flags.contains (ImportFlags::SPIOnly))
838+ if (fixItInfo. flags .contains (ImportFlags::SPIOnly))
791839 importText += " @_spiOnly " ;
792840
793- // FIXME: Access level should be considered, too.
794-
795841 // @_spi imports.
796842 if (decl->isSPI ()) {
797843 auto spiGroups = decl->getSPIGroups ();
@@ -802,11 +848,49 @@ bool swift::diagnoseMissingImportForMember(const ValueDecl *decl,
802848 }
803849 }
804850
851+ if (auto accessLevel = fixItInfo.accessLevel ) {
852+ importText += getAccessLevelSpelling (*accessLevel);
853+ importText += " " ;
854+ }
855+
805856 importText += " import " ;
806857 importText += definingModule->getName ().str ();
807858 importText += " \n " ;
808859 ctx.Diags .diagnose (bestLoc, diag::candidate_add_import, definingModule)
809860 .fixItInsert (bestLoc, importText);
861+ }
810862
811- return true ;
863+ bool swift::maybeDiagnoseMissingImportForMember (const ValueDecl *decl,
864+ const DeclContext *dc,
865+ SourceLoc loc) {
866+ if (decl->findImport (dc))
867+ return false ;
868+
869+ auto sf = dc->getParentSourceFile ();
870+ if (!sf)
871+ return false ;
872+
873+ auto &ctx = dc->getASTContext ();
874+
875+ // In lazy typechecking mode just emit the diagnostic immediately without a
876+ // fix-it since there won't be an opportunity to emit delayed diagnostics.
877+ if (ctx.TypeCheckerOpts .EnableLazyTypecheck ) {
878+ diagnoseMissingImportForMember (decl, sf, loc);
879+ return true ;
880+ }
881+
882+ sf->addDelayedMissingImportForMemberDiagnostic (decl, loc);
883+ return false ;
884+ }
885+
886+ void swift::diagnoseMissingImports (SourceFile &sf) {
887+ auto delayedDiags = sf.takeDelayedMissingImportForMemberDiagnostics ();
888+ auto fixItCache = MissingImportFixItCache (sf);
889+
890+ for (auto declAndLocs : delayedDiags) {
891+ for (auto loc : declAndLocs.second ) {
892+ diagnoseAndFixMissingImportForMember (declAndLocs.first , &sf, loc,
893+ fixItCache);
894+ }
895+ }
812896}
0 commit comments