Skip to content

Commit 62f257d

Browse files
authored
Merge pull request #31100 from xymus/require-avail-less-spam
[Sema] Don't require explicit availability on extensions without public members
2 parents ec9afec + e1d13b8 commit 62f257d

File tree

2 files changed

+56
-15
lines changed

2 files changed

+56
-15
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2828,6 +2828,23 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *Decl,
28282828
return AW.diagAvailability(const_cast<ValueDecl *>(Decl), R, nullptr, Flags);
28292829
}
28302830

2831+
/// Should we warn that \p valueDecl needs an explicit availability annotation
2832+
/// in -require-explicit-availaiblity mode?
2833+
static bool declNeedsExplicitAvailability(const ValueDecl *valueDecl) {
2834+
AccessScope scope =
2835+
valueDecl->getFormalAccessScope(/*useDC*/nullptr,
2836+
/*treatUsableFromInlineAsPublic*/true);
2837+
if (!scope.isPublic() ||
2838+
valueDecl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
2839+
return false;
2840+
2841+
// Warn on decls without an introduction version.
2842+
auto &ctx = valueDecl->getASTContext();
2843+
auto safeRangeUnderApprox = AvailabilityInference::availableRange(valueDecl, ctx);
2844+
return !safeRangeUnderApprox.getOSVersion().hasLowerEndpoint() &&
2845+
!valueDecl->getAttrs().isUnavailable(ctx);
2846+
}
2847+
28312848
void swift::checkExplicitAvailability(Decl *decl) {
28322849
// Check only if the command line option was set.
28332850
if (!decl->getASTContext().LangOpts.RequireExplicitAvailability)
@@ -2840,27 +2857,35 @@ void swift::checkExplicitAvailability(Decl *decl) {
28402857

28412858
ValueDecl *valueDecl = dyn_cast<ValueDecl>(decl);
28422859
if (valueDecl == nullptr) {
2843-
// decl should be either a ValueDecl or an ExtensionDecl
2860+
// decl should be either a ValueDecl or an ExtensionDecl.
28442861
auto extension = cast<ExtensionDecl>(decl);
28452862
valueDecl = extension->getExtendedNominal();
28462863
if (!valueDecl)
28472864
return;
2848-
}
28492865

2850-
// Skip decls that are not public and not usable from inline.
2851-
AccessScope scope =
2852-
valueDecl->getFormalAccessScope(/*useDC*/nullptr,
2853-
/*treatUsableFromInlineAsPublic*/true);
2854-
if (!scope.isPublic() ||
2855-
decl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
2856-
return;
2866+
// Skip extensions without public members or conformances.
2867+
auto members = extension->getMembers();
2868+
auto hasMembers = std::any_of(members.begin(), members.end(),
2869+
[](const Decl *D) -> bool {
2870+
if (auto VD = dyn_cast<ValueDecl>(D))
2871+
if (declNeedsExplicitAvailability(VD))
2872+
return true;
2873+
return false;
2874+
});
28572875

2858-
// Warn on decls without an introduction version.
2859-
auto &ctx = decl->getASTContext();
2860-
auto safeRangeUnderApprox = AvailabilityInference::availableRange(decl, ctx);
2861-
if (!safeRangeUnderApprox.getOSVersion().hasLowerEndpoint() &&
2862-
!decl->getAttrs().isUnavailable(ctx)) {
2876+
auto protocols = extension->getLocalProtocols(ConformanceLookupKind::OnlyExplicit);
2877+
auto hasProtocols = std::any_of(protocols.begin(), protocols.end(),
2878+
[](const ProtocolDecl *PD) -> bool {
2879+
AccessScope scope =
2880+
PD->getFormalAccessScope(/*useDC*/nullptr,
2881+
/*treatUsableFromInlineAsPublic*/true);
2882+
return scope.isPublic();
2883+
});
2884+
2885+
if (!hasMembers && !hasProtocols) return;
2886+
}
28632887

2888+
if (declNeedsExplicitAvailability(valueDecl)) {
28642889
auto diag = decl->diagnose(diag::public_decl_needs_availability);
28652890

28662891
auto suggestPlatform = decl->getASTContext().LangOpts.RequireExplicitAvailabilityTarget;
@@ -2876,6 +2901,7 @@ void swift::checkExplicitAvailability(Decl *decl) {
28762901
{
28772902
llvm::raw_string_ostream Out(AttrText);
28782903

2904+
auto &ctx = valueDecl->getASTContext();
28792905
StringRef OriginalIndent = Lexer::getIndentationForLine(
28802906
ctx.SourceMgr, InsertLoc);
28812907
Out << "@available(" << suggestPlatform << ", *)\n"

test/attr/require_explicit_availability.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Test the -require-explicit-availability flag
2+
13
// RUN: %swiftc_driver -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -Xfrontend -verify -require-explicit-availability -require-explicit-availability-target "macOS 10.10" %s
24
// RUN: %swiftc_driver -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -warnings-as-errors %s
35

@@ -43,8 +45,21 @@ public class C { } // expected-warning {{public declarations should have an avai
4345

4446
public protocol P { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
4547

48+
private protocol PrivateProto { }
49+
4650
extension S { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
47-
func ok() { }
51+
public func warnForPublicMembers() { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{3-3=@available(macOS 10.10, *)\n }}
52+
}
53+
54+
extension S {
55+
internal func dontWarnWithoutPublicMembers() { }
56+
private func dontWarnWithoutPublicMembers1() { }
57+
}
58+
59+
extension S : P { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
60+
}
61+
62+
extension S : PrivateProto {
4863
}
4964

5065
open class OpenClass { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}

0 commit comments

Comments
 (0)