2828#include " swift/AST/Import.h"
2929#include " swift/AST/ParameterList.h"
3030#include " swift/AST/Pattern.h"
31+ #include " swift/AST/PrettyStackTrace.h"
3132#include " swift/AST/TypeCheckRequests.h"
3233#include " swift/Basic/Assertions.h"
3334#include " clang/AST/DeclCXX.h"
@@ -70,10 +71,22 @@ static void forAllRequirementTypes(
7071using CheckTypeAccessCallback =
7172 void (AccessScope, const TypeRepr *, DowngradeToWarning, ImportAccessLevel);
7273
74+ using CheckDeclAccessCallback = void (AccessScope, SourceLoc, ImportAccessLevel);
75+
7376class AccessControlCheckerBase {
7477protected:
7578 bool checkUsableFromInline;
7679
80+ bool shouldSkipChecking (const ValueDecl *decl);
81+
82+ // / Returns true if access checking ought to be skipped for the given
83+ // / `AccessScope`.
84+ bool shouldSkipAccessCheckingInContext (AccessScope contextAccessScope,
85+ const ASTContext &ctx);
86+
87+ ImportAccessLevel getImportAccessForDecl (const ValueDecl *decl,
88+ const DeclContext *useDC);
89+
7790 void checkTypeAccessImpl (
7891 Type type, TypeRepr *typeRepr, AccessScope contextAccessScope,
7992 const DeclContext *useDC, bool mayBeInferred,
@@ -102,6 +115,12 @@ class AccessControlCheckerBase {
102115 });
103116 }
104117
118+ void checkAvailabilityDomains (const Decl *D);
119+
120+ void checkDeclAccess (SourceLoc loc, const ValueDecl *decl,
121+ AccessScope contextAccessScope, const DeclContext *useDC,
122+ llvm::function_ref<CheckDeclAccessCallback> diagnose);
123+
105124 AccessControlCheckerBase (bool checkUsableFromInline)
106125 : checkUsableFromInline(checkUsableFromInline) {}
107126
@@ -117,10 +136,50 @@ class AccessControlCheckerBase {
117136 const ValueDecl *ownerDecl);
118137
119138 void checkGlobalActorAccess (const Decl *D);
139+
140+ void checkAvailabilityDomains (const Decl *D, AccessScope accessScope,
141+ AccessLevel contextAccess);
120142};
121143
122144} // end anonymous namespace
123145
146+ bool AccessControlCheckerBase::shouldSkipChecking (const ValueDecl *decl) {
147+ if (!checkUsableFromInline)
148+ return false ;
149+
150+ if (decl->getFormalAccess () != AccessLevel::Internal &&
151+ decl->getFormalAccess () != AccessLevel::Package)
152+ return true ;
153+ return !decl->isUsableFromInline ();
154+ }
155+
156+ bool AccessControlCheckerBase::shouldSkipAccessCheckingInContext (
157+ AccessScope contextAccessScope, const ASTContext &ctx) {
158+ if (ctx.isAccessControlDisabled ())
159+ return true ;
160+
161+ // Don't spend time checking local declarations; this is always valid by the
162+ // time we get to this point.
163+ if (contextAccessScope.isInContext () &&
164+ contextAccessScope.getDeclContext ()->isLocalContext ())
165+ return true ;
166+
167+ return false ;
168+ }
169+
170+ ImportAccessLevel
171+ AccessControlCheckerBase::getImportAccessForDecl (const ValueDecl *decl,
172+ const DeclContext *useDC) {
173+ auto complainImport = decl->getImportAccessFrom (useDC);
174+
175+ // Don't complain about an import that doesn't restrict the access
176+ // level of the decl. This can happen with imported `package` decls.
177+ if (complainImport && complainImport->accessLevel >= decl->getFormalAccess ())
178+ return std::nullopt ;
179+
180+ return complainImport;
181+ }
182+
124183// / Searches the given type representation for a `DeclRefTypeRepr` that is
125184// / bound to a type declaration with the given access scope. The type
126185// / representation is searched in source order. For example, nodes in
@@ -201,12 +260,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
201260 llvm::function_ref<CheckTypeAccessCallback> diagnose) {
202261
203262 auto &Context = useDC->getASTContext ();
204- if (Context.isAccessControlDisabled ())
205- return ;
206- // Don't spend time checking local declarations; this is always valid by the
207- // time we get to this point.
208- if (contextAccessScope.isInContext () &&
209- contextAccessScope.getDeclContext ()->isLocalContext ())
263+ if (shouldSkipAccessCheckingInContext (contextAccessScope, Context))
210264 return ;
211265
212266 // Report where it was imported from.
@@ -229,7 +283,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
229283 return TypeWalker::Action::Continue;
230284 }));
231285 }
232- };
286+ }
233287
234288 AccessScope problematicAccessScope = AccessScope::getPublic ();
235289
@@ -307,19 +361,36 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
307361 const ValueDecl *VD = complainRepr->getBoundDecl ();
308362 assert (VD &&
309363 " findTypeDeclWithAccessScope should return bound TypeReprs only" );
310- complainImport = VD->getImportAccessFrom (useDC);
311-
312- // Don't complain about an import that doesn't restrict the access
313- // level of the decl. This can happen with imported `package` decls.
314- if (complainImport.has_value () &&
315- complainImport->accessLevel >= VD->getFormalAccess ())
316- complainImport = std::nullopt ;
364+ complainImport = getImportAccessForDecl (VD, useDC);
317365 }
318366
319367 diagnose (problematicAccessScope, complainRepr, downgradeToWarning,
320368 complainImport);
321369}
322370
371+ void AccessControlCheckerBase::checkDeclAccess (
372+ SourceLoc loc, const ValueDecl *decl, AccessScope contextAccessScope,
373+ const DeclContext *useDC,
374+ llvm::function_ref<CheckDeclAccessCallback> diagnose) {
375+
376+ auto &ctx = useDC->getASTContext ();
377+ if (shouldSkipAccessCheckingInContext (contextAccessScope, ctx))
378+ return ;
379+
380+ recordRequiredImportAccessLevelForDecl (
381+ decl, useDC, contextAccessScope.accessLevelForDiagnostics (), loc);
382+
383+ AccessScope declAccessScope =
384+ decl->getFormalAccessScope (useDC, checkUsableFromInline);
385+ if (contextAccessScope.hasEqualDeclContextWith (declAccessScope) ||
386+ contextAccessScope.isChildOf (declAccessScope))
387+ return ;
388+
389+ // The reference to the decl violates the rules of access control.
390+ ImportAccessLevel complainImport = getImportAccessForDecl (decl, useDC);
391+ diagnose (declAccessScope, loc, complainImport);
392+ }
393+
323394// / Checks if the access scope of the type described by \p TL is valid for the
324395// / type to be the type of \p context. If it isn't, calls \p diagnose with a
325396// / TypeRepr representing the offending part of \p TL.
@@ -556,6 +627,50 @@ void AccessControlCheckerBase::checkGlobalActorAccess(const Decl *D) {
556627 });
557628}
558629
630+ void AccessControlCheckerBase::checkAvailabilityDomains (
631+ const Decl *D, AccessScope accessScope, AccessLevel contextAccess) {
632+ auto &ctx = D->getASTContext ();
633+ for (auto attr : D->getSemanticAvailableAttrs ()) {
634+ if (auto *domainDecl = attr.getDomain ().getDecl ()) {
635+ checkDeclAccess (
636+ attr.getParsedAttr ()->getDomainLoc (), domainDecl, accessScope,
637+ D->getDeclContext (),
638+ [&](AccessScope domainAccessScope, SourceLoc useLoc,
639+ ImportAccessLevel importLimit) {
640+ // FIXME: [availability] Improve diagnostics by indicating the decl
641+ // that the formal access is implied by. Enum cases, associated
642+ // types, protocol requirements, etc. inherit their access level
643+ // from their context.
644+
645+ if (checkUsableFromInline) {
646+ ctx.Diags .diagnose (
647+ useLoc, diag::attr_availability_domain_not_usable_from_inline,
648+ attr.getDomain (), attr.getParsedAttr (), D);
649+ noteLimitingImport (nullptr , ctx, importLimit, domainDecl);
650+ return ;
651+ }
652+
653+ ctx.Diags .diagnose (useLoc, diag::attr_availability_domain_access,
654+ attr.getDomain (),
655+ domainAccessScope.accessLevelForDiagnostics (),
656+ attr.getParsedAttr (), contextAccess, D);
657+ noteLimitingImport (nullptr , ctx, importLimit, domainDecl);
658+ });
659+ }
660+ }
661+ }
662+
663+ void AccessControlCheckerBase::checkAvailabilityDomains (const Decl *D) {
664+ auto VD = dyn_cast<ValueDecl>(D->getAbstractSyntaxDeclForAttributes ());
665+ if (!VD || shouldSkipChecking (VD))
666+ return ;
667+
668+ AccessScope contextAccessScope =
669+ VD->getFormalAccessScope (VD->getDeclContext (), checkUsableFromInline);
670+
671+ checkAvailabilityDomains (VD, contextAccessScope, VD->getFormalAccess ());
672+ }
673+
559674namespace {
560675class AccessControlChecker : public AccessControlCheckerBase ,
561676 public DeclVisitor<AccessControlChecker> {
@@ -573,6 +688,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
573688
574689 DeclVisitor<AccessControlChecker>::visit (D);
575690 checkGlobalActorAccess (D);
691+ checkAvailabilityDomains (D);
576692 }
577693
578694 // Force all kinds to be handled at a lower level.
@@ -1321,13 +1437,6 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
13211437 UsableFromInlineChecker ()
13221438 : AccessControlCheckerBase(/* checkUsableFromInline=*/ true ) {}
13231439
1324- static bool shouldSkipChecking (const ValueDecl *VD) {
1325- if (VD->getFormalAccess () != AccessLevel::Internal &&
1326- VD->getFormalAccess () != AccessLevel::Package)
1327- return true ;
1328- return !VD->isUsableFromInline ();
1329- };
1330-
13311440 void visit (Decl *D) {
13321441 if (!D->getASTContext ().isSwiftVersionAtLeast (4 , 2 ))
13331442 return ;
@@ -1341,6 +1450,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
13411450
13421451 DeclVisitor<UsableFromInlineChecker>::visit (D);
13431452 checkGlobalActorAccess (D);
1453+ checkAvailabilityDomains (D);
13441454 }
13451455
13461456 // Force all kinds to be handled at a lower level.
@@ -2139,10 +2249,22 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
21392249 }
21402250 }
21412251
2252+ void checkAvailabilityDomains (const Decl *D) {
2253+ D = D->getAbstractSyntaxDeclForAttributes ();
2254+ for (auto attr : D->getSemanticAvailableAttrs ()) {
2255+ if (auto *domainDecl = attr.getDomain ().getDecl ()) {
2256+ diagnoseDeclAvailability (domainDecl,
2257+ attr.getParsedAttr ()->getDomainLoc (), nullptr ,
2258+ Where, std::nullopt );
2259+ }
2260+ }
2261+ }
2262+
21422263 void visit (Decl *D) {
21432264 DeclVisitor<DeclAvailabilityChecker>::visit (D);
21442265 checkGlobalActor (D);
21452266 checkAttachedMacros (D);
2267+ checkAvailabilityDomains (D);
21462268 }
21472269
21482270 // Force all kinds to be handled at a lower level.
@@ -2372,14 +2494,10 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
23722494 }
23732495
23742496 void checkConstrainedExtensionRequirements (ExtensionDecl *ED,
2375- bool hasExportedMembers ) {
2497+ ExportabilityReason reason ) {
23762498 if (!ED->getTrailingWhereClause ())
23772499 return ;
23782500
2379- ExportabilityReason reason =
2380- hasExportedMembers ? ExportabilityReason::ExtensionWithPublicMembers
2381- : ExportabilityReason::ExtensionWithConditionalConformances;
2382-
23832501 forAllRequirementTypes (ED, [&](Type type, TypeRepr *typeRepr) {
23842502 checkType (type, typeRepr, ED, reason,
23852503 DeclAvailabilityFlag::DisableUnsafeChecking);
@@ -2423,7 +2541,23 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
24232541 // the 'where' clause must only name exported types.
24242542 Where = wasWhere.withExported (hasExportedMembers ||
24252543 !ED->getInherited ().empty ());
2426- checkConstrainedExtensionRequirements (ED, hasExportedMembers);
2544+ ExportabilityReason reason =
2545+ hasExportedMembers
2546+ ? ExportabilityReason::ExtensionWithPublicMembers
2547+ : ExportabilityReason::ExtensionWithConditionalConformances;
2548+ checkConstrainedExtensionRequirements (ED, reason);
2549+
2550+ // Diagnose the exportability of the availability domains referenced by the
2551+ // @available attributes attached to the extension.
2552+ if (Where.isExported ()) {
2553+ for (auto availableAttr : ED->getSemanticAvailableAttrs ()) {
2554+ if (auto *domainDecl = availableAttr.getDomain ().getDecl ()) {
2555+ TypeChecker::diagnoseDeclRefExportability (
2556+ availableAttr.getParsedAttr ()->getDomainLoc (), domainDecl,
2557+ Where.withReason (reason));
2558+ }
2559+ }
2560+ }
24272561
24282562 // If we haven't already visited the extended nominal visit it here.
24292563 // This logic is too wide but prevents false reports of an unused public
@@ -2491,7 +2625,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
24912625
24922626} // end anonymous namespace
24932627
2494- static void checkExtensionGenericParamAccess (const ExtensionDecl *ED) {
2628+ static void checkExtensionAccess (const ExtensionDecl *ED) {
24952629 auto *AA = ED->getAttrs ().getAttribute <AccessControlAttr>();
24962630 if (!AA)
24972631 return ;
@@ -2522,8 +2656,11 @@ static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) {
25222656 break ;
25232657 }
25242658
2525- AccessControlChecker ().checkGenericParamAccess (
2526- ED, ED, desiredAccessScope, userSpecifiedAccess);
2659+ auto accessChecker = AccessControlChecker ();
2660+ accessChecker.checkGenericParamAccess (ED, ED, desiredAccessScope,
2661+ userSpecifiedAccess);
2662+ accessChecker.checkAvailabilityDomains (ED, desiredAccessScope,
2663+ userSpecifiedAccess);
25272664}
25282665
25292666DisallowedOriginKind swift::getDisallowedOriginKind (const Decl *decl,
@@ -2659,13 +2796,10 @@ void swift::checkAccessControl(Decl *D) {
26592796 AccessControlChecker (allowInlineable).visit (D);
26602797 UsableFromInlineChecker ().visit (D);
26612798 } else if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
2662- checkExtensionGenericParamAccess (ED);
2799+ checkExtensionAccess (ED);
26632800 registerPackageAccessForPackageExtendedType (ED);
26642801 }
26652802
2666- if (isa<AccessorDecl>(D))
2667- return ;
2668-
26692803 auto where = ExportContext::forDeclSignature (D);
26702804 if (where.isImplicit ())
26712805 return ;
0 commit comments