28
28
#include " swift/AST/Import.h"
29
29
#include " swift/AST/ParameterList.h"
30
30
#include " swift/AST/Pattern.h"
31
+ #include " swift/AST/PrettyStackTrace.h"
31
32
#include " swift/AST/TypeCheckRequests.h"
32
33
#include " swift/Basic/Assertions.h"
33
34
#include " clang/AST/DeclCXX.h"
@@ -70,10 +71,22 @@ static void forAllRequirementTypes(
70
71
using CheckTypeAccessCallback =
71
72
void (AccessScope, const TypeRepr *, DowngradeToWarning, ImportAccessLevel);
72
73
74
+ using CheckDeclAccessCallback = void (AccessScope, SourceLoc, ImportAccessLevel);
75
+
73
76
class AccessControlCheckerBase {
74
77
protected:
75
78
bool checkUsableFromInline;
76
79
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
+
77
90
void checkTypeAccessImpl (
78
91
Type type, TypeRepr *typeRepr, AccessScope contextAccessScope,
79
92
const DeclContext *useDC, bool mayBeInferred,
@@ -102,6 +115,12 @@ class AccessControlCheckerBase {
102
115
});
103
116
}
104
117
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
+
105
124
AccessControlCheckerBase (bool checkUsableFromInline)
106
125
: checkUsableFromInline(checkUsableFromInline) {}
107
126
@@ -117,10 +136,50 @@ class AccessControlCheckerBase {
117
136
const ValueDecl *ownerDecl);
118
137
119
138
void checkGlobalActorAccess (const Decl *D);
139
+
140
+ void checkAvailabilityDomains (const Decl *D, AccessScope accessScope,
141
+ AccessLevel contextAccess);
120
142
};
121
143
122
144
} // end anonymous namespace
123
145
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
+
124
183
// / Searches the given type representation for a `DeclRefTypeRepr` that is
125
184
// / bound to a type declaration with the given access scope. The type
126
185
// / representation is searched in source order. For example, nodes in
@@ -201,12 +260,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
201
260
llvm::function_ref<CheckTypeAccessCallback> diagnose) {
202
261
203
262
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))
210
264
return ;
211
265
212
266
// Report where it was imported from.
@@ -229,7 +283,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
229
283
return TypeWalker::Action::Continue;
230
284
}));
231
285
}
232
- };
286
+ }
233
287
234
288
AccessScope problematicAccessScope = AccessScope::getPublic ();
235
289
@@ -307,19 +361,36 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
307
361
const ValueDecl *VD = complainRepr->getBoundDecl ();
308
362
assert (VD &&
309
363
" 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);
317
365
}
318
366
319
367
diagnose (problematicAccessScope, complainRepr, downgradeToWarning,
320
368
complainImport);
321
369
}
322
370
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
+
323
394
// / Checks if the access scope of the type described by \p TL is valid for the
324
395
// / type to be the type of \p context. If it isn't, calls \p diagnose with a
325
396
// / TypeRepr representing the offending part of \p TL.
@@ -556,6 +627,50 @@ void AccessControlCheckerBase::checkGlobalActorAccess(const Decl *D) {
556
627
});
557
628
}
558
629
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
+
559
674
namespace {
560
675
class AccessControlChecker : public AccessControlCheckerBase ,
561
676
public DeclVisitor<AccessControlChecker> {
@@ -573,6 +688,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
573
688
574
689
DeclVisitor<AccessControlChecker>::visit (D);
575
690
checkGlobalActorAccess (D);
691
+ checkAvailabilityDomains (D);
576
692
}
577
693
578
694
// Force all kinds to be handled at a lower level.
@@ -1321,13 +1437,6 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
1321
1437
UsableFromInlineChecker ()
1322
1438
: AccessControlCheckerBase(/* checkUsableFromInline=*/ true ) {}
1323
1439
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
-
1331
1440
void visit (Decl *D) {
1332
1441
if (!D->getASTContext ().isSwiftVersionAtLeast (4 , 2 ))
1333
1442
return ;
@@ -1341,6 +1450,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
1341
1450
1342
1451
DeclVisitor<UsableFromInlineChecker>::visit (D);
1343
1452
checkGlobalActorAccess (D);
1453
+ checkAvailabilityDomains (D);
1344
1454
}
1345
1455
1346
1456
// Force all kinds to be handled at a lower level.
@@ -2139,10 +2249,22 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
2139
2249
}
2140
2250
}
2141
2251
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
+
2142
2263
void visit (Decl *D) {
2143
2264
DeclVisitor<DeclAvailabilityChecker>::visit (D);
2144
2265
checkGlobalActor (D);
2145
2266
checkAttachedMacros (D);
2267
+ checkAvailabilityDomains (D);
2146
2268
}
2147
2269
2148
2270
// Force all kinds to be handled at a lower level.
@@ -2372,14 +2494,10 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
2372
2494
}
2373
2495
2374
2496
void checkConstrainedExtensionRequirements (ExtensionDecl *ED,
2375
- bool hasExportedMembers ) {
2497
+ ExportabilityReason reason ) {
2376
2498
if (!ED->getTrailingWhereClause ())
2377
2499
return ;
2378
2500
2379
- ExportabilityReason reason =
2380
- hasExportedMembers ? ExportabilityReason::ExtensionWithPublicMembers
2381
- : ExportabilityReason::ExtensionWithConditionalConformances;
2382
-
2383
2501
forAllRequirementTypes (ED, [&](Type type, TypeRepr *typeRepr) {
2384
2502
checkType (type, typeRepr, ED, reason,
2385
2503
DeclAvailabilityFlag::DisableUnsafeChecking);
@@ -2423,7 +2541,23 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
2423
2541
// the 'where' clause must only name exported types.
2424
2542
Where = wasWhere.withExported (hasExportedMembers ||
2425
2543
!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
+ }
2427
2561
2428
2562
// If we haven't already visited the extended nominal visit it here.
2429
2563
// This logic is too wide but prevents false reports of an unused public
@@ -2491,7 +2625,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
2491
2625
2492
2626
} // end anonymous namespace
2493
2627
2494
- static void checkExtensionGenericParamAccess (const ExtensionDecl *ED) {
2628
+ static void checkExtensionAccess (const ExtensionDecl *ED) {
2495
2629
auto *AA = ED->getAttrs ().getAttribute <AccessControlAttr>();
2496
2630
if (!AA)
2497
2631
return ;
@@ -2522,8 +2656,11 @@ static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) {
2522
2656
break ;
2523
2657
}
2524
2658
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);
2527
2664
}
2528
2665
2529
2666
DisallowedOriginKind swift::getDisallowedOriginKind (const Decl *decl,
@@ -2659,13 +2796,10 @@ void swift::checkAccessControl(Decl *D) {
2659
2796
AccessControlChecker (allowInlineable).visit (D);
2660
2797
UsableFromInlineChecker ().visit (D);
2661
2798
} else if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
2662
- checkExtensionGenericParamAccess (ED);
2799
+ checkExtensionAccess (ED);
2663
2800
registerPackageAccessForPackageExtendedType (ED);
2664
2801
}
2665
2802
2666
- if (isa<AccessorDecl>(D))
2667
- return ;
2668
-
2669
2803
auto where = ExportContext::forDeclSignature (D);
2670
2804
if (where.isImplicit ())
2671
2805
return ;
0 commit comments