@@ -219,6 +219,42 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
219
219
return true ;
220
220
}
221
221
222
+ // / Returns true if access to \p D should be diagnosed during exportability
223
+ // / checking. These diagnostics would typically be handled by the access
224
+ // / checker, and therefore should be suppressed to avoid duplicate diagnostics.
225
+ // / However, extensions are special because they do not have an intrinsic access
226
+ // / level and therefore the access checker does not currently handle them.
227
+ // / Instead, diagnostics for decls referenced in extension signatures are
228
+ // / deferred to exportability checking. An exportable extension is effectively a
229
+ // / public extension.
230
+ static bool shouldDiagnoseDeclAccess (const ValueDecl *D,
231
+ const ExportContext &where) {
232
+ auto reason = where.getExportabilityReason ();
233
+ auto DC = where.getDeclContext ();
234
+ if (!reason)
235
+ return false ;
236
+
237
+ switch (*reason) {
238
+ case ExportabilityReason::ExtensionWithPublicMembers:
239
+ case ExportabilityReason::ExtensionWithConditionalConformances:
240
+ return true ;
241
+ case ExportabilityReason::Inheritance:
242
+ return isa<ProtocolDecl>(D);
243
+ case ExportabilityReason::AvailableAttribute:
244
+ // If the context is an extension and that extension has an explicit
245
+ // access level then availability domains access has already been
246
+ // diagnosed.
247
+ if (auto *ED = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl ()))
248
+ return !ED->getAttrs ().getAttribute <AccessControlAttr>();
249
+ return false ;
250
+
251
+ case ExportabilityReason::General:
252
+ case ExportabilityReason::ResultBuilder:
253
+ case ExportabilityReason::PropertyWrapper:
254
+ return false ;
255
+ }
256
+ }
257
+
222
258
static bool diagnoseValueDeclRefExportability (SourceLoc loc, const ValueDecl *D,
223
259
const ExportContext &where) {
224
260
assert (where.mustOnlyReferenceExportedDecls ());
@@ -247,41 +283,44 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
247
283
}
248
284
});
249
285
250
- // Access levels from imports are reported with the others access levels.
251
- // Except for extensions and protocol conformances, we report them here.
252
- if (originKind == DisallowedOriginKind::NonPublicImport) {
253
- bool reportHere = [&] {
254
- switch (*reason) {
255
- case ExportabilityReason::ExtensionWithPublicMembers:
256
- case ExportabilityReason::ExtensionWithConditionalConformances:
257
- return true ;
258
- case ExportabilityReason::Inheritance:
259
- return isa<ProtocolDecl>(D);
260
- case ExportabilityReason::AvailableAttribute:
261
- // If the context is an extension and that extension has an explicit
262
- // access level, then access has already been diagnosed for the
263
- // @available attribute.
264
- if (auto *ED = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl ()))
265
- return !ED->getAttrs ().getAttribute <AccessControlAttr>();
266
- return false ;
267
- default :
268
- return false ;
269
- }
270
- }();
271
- if (!reportHere)
272
- return false ;
273
- }
274
-
275
- if (originKind == DisallowedOriginKind::None)
286
+ switch (originKind) {
287
+ case DisallowedOriginKind::None:
288
+ // The decl does not come from a source that needs to be checked for
289
+ // exportability.
276
290
return false ;
277
291
278
- // Some diagnostics emitted with the `MemberImportVisibility` feature enabled
279
- // subsume these diagnostics.
280
- if (originKind == DisallowedOriginKind::MissingImport &&
281
- ctx.LangOpts .hasFeature (Feature::MemberImportVisibility,
282
- /* allowMigration=*/ true ) &&
283
- SF)
284
- return false ;
292
+ case DisallowedOriginKind::NonPublicImport:
293
+ // With a few exceptions, access levels from imports are diagnosed during
294
+ // access checking and should be skipped here.
295
+ if (!shouldDiagnoseDeclAccess (D, where))
296
+ return false ;
297
+ break ;
298
+
299
+ case DisallowedOriginKind::MissingImport:
300
+ // Some diagnostics emitted with the `MemberImportVisibility` feature
301
+ // enabled subsume these diagnostics.
302
+ if (ctx.LangOpts .hasFeature (Feature::MemberImportVisibility,
303
+ /* allowMigration=*/ true ) &&
304
+ SF)
305
+ return false ;
306
+ break ;
307
+
308
+ case DisallowedOriginKind::SPIOnly:
309
+ // Availability attributes referring to availability domains from modules
310
+ // that are imported @_spiOnly in a -library-level=api will not be printed
311
+ // in the public swiftinterface of the module and should therefore not be
312
+ // diagnosed for exportability.
313
+ if (reason && reason == ExportabilityReason::AvailableAttribute &&
314
+ ctx.LangOpts .LibraryLevel == LibraryLevel::API)
315
+ return false ;
316
+ break ;
317
+
318
+ case DisallowedOriginKind::ImplementationOnly:
319
+ case DisallowedOriginKind::SPIImported:
320
+ case DisallowedOriginKind::SPILocal:
321
+ case DisallowedOriginKind::FragileCxxAPI:
322
+ break ;
323
+ }
285
324
286
325
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
287
326
// Only diagnose accessors if their disallowed origin kind differs from
0 commit comments