Skip to content

Commit 36e5b5a

Browse files
authored
Merge pull request swiftlang#25152 from xymus/explicit-intro-version
Add CLI option to warn when a public decl has no introduction version
2 parents e4e009f + c44cff7 commit 36e5b5a

File tree

8 files changed

+147
-0
lines changed

8 files changed

+147
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4152,6 +4152,9 @@ ERROR(availability_protocol_requires_version,
41524152
NOTE(availability_protocol_requirement_here, none,
41534153
"protocol requirement here", ())
41544154

4155+
WARNING(public_decl_needs_availability, none,
4156+
"public declarations should have an availability attribute with -require-explicit-availability", ())
4157+
41554158
// This doesn't display as an availability diagnostic, but it's
41564159
// implemented there and fires when these subscripts are marked
41574160
// unavailable, so it seems appropriate to put it here.

include/swift/Basic/LangOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ namespace swift {
9696
/// Enable 'availability' restrictions for App Extensions.
9797
bool EnableAppExtensionRestrictions = false;
9898

99+
/// Require public declarations to declare an introduction OS version.
100+
bool RequireExplicitAvailability = false;
101+
102+
/// Introduction platform and version to suggest as fix-it
103+
/// when using RequireExplicitAvailability.
104+
std::string RequireExplicitAvailabilityTarget;
105+
99106
///
100107
/// Support for alternate usage modes
101108
///

include/swift/Option/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,14 @@ def enable_library_evolution : Flag<["-"], "enable-library-evolution">,
312312
Flags<[FrontendOption, ModuleInterfaceOption]>,
313313
HelpText<"Build the module to allow binary-compatible library evolution">;
314314

315+
def require_explicit_availability : Flag<["-"], "require-explicit-availability">,
316+
Flags<[FrontendOption, NoInteractiveOption]>,
317+
HelpText<"Require explicit availability on public declarations">;
318+
def require_explicit_availability_target : Separate<["-"], "require-explicit-availability-target">,
319+
Flags<[FrontendOption, NoInteractiveOption]>,
320+
HelpText<"Suggest fix-its adding @available(<target>, *) to public declarations without availability">,
321+
MetaVarName<"<target>">;
322+
315323
def module_name : Separate<["-"], "module-name">,
316324
Flags<[FrontendOption, ModuleInterfaceOption]>,
317325
HelpText<"Name of the module to build">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,13 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
395395
Opts.RequestEvaluatorGraphVizPath = A->getValue();
396396
}
397397

398+
if (Args.getLastArg(OPT_require_explicit_availability, OPT_require_explicit_availability_target)) {
399+
Opts.RequireExplicitAvailability = true;
400+
if (const Arg *A = Args.getLastArg(OPT_require_explicit_availability_target)) {
401+
Opts.RequireExplicitAvailabilityTarget = A->getValue();
402+
}
403+
}
404+
398405
if (const Arg *A = Args.getLastArg(OPT_solver_memory_threshold)) {
399406
unsigned threshold;
400407
if (StringRef(A->getValue()).getAsInteger(10, threshold)) {

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,3 +2788,60 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *Decl,
27882788
AvailabilityWalker AW(TC, DC);
27892789
return AW.diagAvailability(const_cast<ValueDecl *>(Decl), R, nullptr, Flags);
27902790
}
2791+
2792+
void swift::checkExplicitAvailability(Decl *decl) {
2793+
// Check only if the command line option was set.
2794+
if (!decl->getASTContext().LangOpts.RequireExplicitAvailability)
2795+
return;
2796+
2797+
// Skip nominal type members as the type should be annotated.
2798+
auto declContext = decl->getDeclContext();
2799+
if (isa<NominalTypeDecl>(declContext))
2800+
return;
2801+
2802+
ValueDecl *valueDecl = dyn_cast<ValueDecl>(decl);
2803+
if (valueDecl == nullptr) {
2804+
// decl should be either a ValueDecl or an ExtensionDecl
2805+
auto extension = cast<ExtensionDecl>(decl);
2806+
valueDecl = extension->getExtendedNominal();
2807+
if (!valueDecl)
2808+
return;
2809+
}
2810+
2811+
// Skip decls that are not public and not usable from inline.
2812+
AccessScope scope =
2813+
valueDecl->getFormalAccessScope(/*useDC*/nullptr,
2814+
/*treatUsableFromInlineAsPublic*/true);
2815+
if (!scope.isPublic() ||
2816+
decl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
2817+
return;
2818+
2819+
// Warn on decls without an introduction version.
2820+
auto &ctx = decl->getASTContext();
2821+
auto safeRangeUnderApprox = AvailabilityInference::availableRange(decl, ctx);
2822+
if (!safeRangeUnderApprox.getOSVersion().hasLowerEndpoint()) {
2823+
auto diag = decl->diagnose(diag::public_decl_needs_availability);
2824+
2825+
auto suggestPlatform = decl->getASTContext().LangOpts.RequireExplicitAvailabilityTarget;
2826+
if (!suggestPlatform.empty()) {
2827+
auto InsertLoc = decl->getAttrs().getStartLoc(/*forModifiers=*/false);
2828+
if (InsertLoc.isInvalid())
2829+
InsertLoc = decl->getStartLoc();
2830+
2831+
if (InsertLoc.isInvalid())
2832+
return;
2833+
2834+
std::string AttrText;
2835+
{
2836+
llvm::raw_string_ostream Out(AttrText);
2837+
2838+
StringRef OriginalIndent = Lexer::getIndentationForLine(
2839+
ctx.SourceMgr, InsertLoc);
2840+
Out << "@available(" << suggestPlatform << ", *)\n"
2841+
<< OriginalIndent;
2842+
}
2843+
2844+
diag.fixItInsert(InsertLoc, AttrText);
2845+
}
2846+
}
2847+
}

lib/Sema/TypeCheckAvailability.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace swift {
2828
class Expr;
2929
class InFlightDiagnostic;
3030
class TypeChecker;
31+
class Decl;
3132
class ValueDecl;
3233

3334
/// Diagnose uses of unavailable declarations.
@@ -82,6 +83,9 @@ bool diagnoseExplicitUnavailability(
8283
const DeclContext *DC,
8384
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts);
8485

86+
/// Check if \p decl has a introduction version required by -require-explicit-availability
87+
void checkExplicitAvailability(Decl *decl);
88+
8589
} // namespace swift
8690

8791
#endif // SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H

lib/Sema/TypeCheckDecl.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "DerivedConformances.h"
2020
#include "TypeChecker.h"
2121
#include "TypeCheckAccess.h"
22+
#include "TypeCheckAvailability.h"
2223
#include "TypeCheckType.h"
2324
#include "MiscDiagnostics.h"
2425
#include "swift/AST/AccessScope.h"
@@ -2692,6 +2693,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26922693
checkEnumRawValues(TC, ED);
26932694
}
26942695

2696+
checkExplicitAvailability(ED);
2697+
26952698
TC.checkDeclCircularity(ED);
26962699
TC.ConformanceContexts.push_back(ED);
26972700
}
@@ -2715,6 +2718,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
27152718

27162719
checkAccessControl(TC, SD);
27172720

2721+
checkExplicitAvailability(SD);
2722+
27182723
TC.checkDeclCircularity(SD);
27192724
TC.ConformanceContexts.push_back(SD);
27202725
}
@@ -2937,6 +2942,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
29372942

29382943
checkAccessControl(TC, CD);
29392944

2945+
checkExplicitAvailability(CD);
2946+
29402947
TC.checkDeclCircularity(CD);
29412948
TC.ConformanceContexts.push_back(CD);
29422949
}
@@ -3011,6 +3018,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
30113018

30123019
// Explicity compute the requirement signature to detect errors.
30133020
(void) PD->getRequirementSignature();
3021+
3022+
checkExplicitAvailability(PD);
30143023
}
30153024

30163025
void visitVarDecl(VarDecl *VD) {
@@ -3094,6 +3103,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
30943103
}
30953104

30963105
TC.checkParameterAttributes(FD->getParameters());
3106+
3107+
checkExplicitAvailability(FD);
30973108
}
30983109

30993110
void visitModuleDecl(ModuleDecl *) { }
@@ -3160,6 +3171,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
31603171
TC.checkDeclAttributes(ED);
31613172

31623173
checkAccessControl(TC, ED);
3174+
3175+
checkExplicitAvailability(ED);
31633176
}
31643177

31653178
void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -verify -require-explicit-availability -require-explicit-availability-target "macOS 10.10" %s
2+
// RUN: %target-swift-frontend -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -warnings-as-errors %s
3+
4+
public struct S { // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}}
5+
public func method() { }
6+
}
7+
8+
public func foo() { bar() } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
9+
10+
@usableFromInline
11+
func bar() { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
12+
13+
@available(macOS 10.1, *)
14+
public func ok() { }
15+
16+
@available(macOS, deprecated: 10.10)
17+
public func missingIntro() { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
18+
19+
func privateFunc() { }
20+
21+
@_alwaysEmitIntoClient
22+
public func alwaysEmitted() { }
23+
24+
@available(macOS 10.1, *)
25+
struct SOk {
26+
public func okMethod() { }
27+
}
28+
29+
precedencegroup MediumPrecedence {}
30+
infix operator + : MediumPrecedence
31+
32+
public func +(lhs: S, rhs: S) -> S { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
33+
34+
public enum E { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
35+
36+
public class C { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
37+
38+
public protocol P { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
39+
40+
extension S { // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
41+
func ok() { }
42+
}
43+
44+
open class OpenClass { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}}
45+
46+
private class PrivateClass { }
47+
48+
extension PrivateClass { }

0 commit comments

Comments
 (0)