Skip to content

Commit aeb5a46

Browse files
committed
AST: Emit warnings for redundant availability checks in custom domains.
1 parent a3fbe3d commit aeb5a46

File tree

7 files changed

+58
-40
lines changed

7 files changed

+58
-40
lines changed

include/swift/AST/AvailabilityConstraint.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,21 @@ enum class AvailabilityConstraintFlag : uint8_t {
188188
};
189189
using AvailabilityConstraintFlags = OptionSet<AvailabilityConstraintFlag>;
190190

191-
/// Returns the set of availability constraints that restrict use of \p decl
191+
/// Returns the set of availability constraints that restricts use of \p decl
192192
/// when it is referenced from the given context. In other words, it is the
193-
/// collection of of `@available` attributes with unsatisfied conditions.
193+
/// collection of `@available` attributes with unsatisfied conditions.
194194
DeclAvailabilityConstraints getAvailabilityConstraintsForDecl(
195195
const Decl *decl, const AvailabilityContext &context,
196196
AvailabilityConstraintFlags flags = std::nullopt);
197+
198+
/// Returns the availability constraints that restricts use of \p decl
199+
/// in \p domain when it is referenced from the given context. In other words,
200+
/// it is the unsatisfied `@available` attribute that applies to \p domain in
201+
/// the given context.
202+
std::optional<AvailabilityConstraint> getAvailabilityConstraintForDeclInDomain(
203+
const Decl *decl, const AvailabilityContext &context,
204+
AvailabilityDomain domain,
205+
AvailabilityConstraintFlags flags = std::nullopt);
197206
} // end namespace swift
198207

199208
namespace llvm {

include/swift/AST/AvailabilityScope.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,11 @@ class AvailabilityScope : public ASTAllocated<AvailabilityScope> {
272272
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const;
273273

274274
/// Returns the availability version range that was explicitly written in
275-
/// source, if applicable. Otherwise, returns null.
276-
std::optional<const AvailabilityRange> getExplicitAvailabilityRange() const;
275+
/// source for the given domain, if applicable. Otherwise, returns
276+
/// `std::nullopt`.
277+
std::optional<const AvailabilityRange>
278+
getExplicitAvailabilityRange(AvailabilityDomain Domain,
279+
ASTContext &Ctx) const;
277280

278281
/// Returns the source range this scope represents.
279282
SourceRange getSourceRange() const { return SrcRange; }

lib/AST/AvailabilityConstraint.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,15 @@ swift::getAvailabilityConstraintsForDecl(const Decl *decl,
331331
return constraints;
332332
}
333333

334+
std::optional<AvailabilityConstraint>
335+
swift::getAvailabilityConstraintForDeclInDomain(
336+
const Decl *decl, const AvailabilityContext &context,
337+
AvailabilityDomain domain, AvailabilityConstraintFlags flags) {
338+
auto constraints = getAvailabilityConstraintsForDecl(decl, context, flags);
339+
for (auto const &constraint : constraints) {
340+
if (constraint.getDomain().isRelated(domain))
341+
return constraint;
342+
}
343+
344+
return std::nullopt;
345+
}

lib/AST/AvailabilityScope.cpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/AvailabilityScope.h"
1818

1919
#include "swift/AST/ASTContext.h"
20+
#include "swift/AST/AvailabilityConstraint.h"
2021
#include "swift/AST/AvailabilityInference.h"
2122
#include "swift/AST/AvailabilitySpec.h"
2223
#include "swift/AST/Decl.h"
@@ -356,15 +357,17 @@ SourceRange AvailabilityScope::getAvailabilityConditionVersionSourceRange(
356357
}
357358

358359
std::optional<const AvailabilityRange>
359-
AvailabilityScope::getExplicitAvailabilityRange() const {
360+
AvailabilityScope::getExplicitAvailabilityRange(AvailabilityDomain domain,
361+
ASTContext &ctx) const {
360362
switch (getReason()) {
361363
case Reason::Root:
362364
return std::nullopt;
363365

364366
case Reason::Decl: {
365367
auto decl = Node.getAsDecl();
366-
if (auto attr = decl->getAvailableAttrForPlatformIntroduction())
367-
return attr->getIntroducedRange(decl->getASTContext());
368+
if (auto constraint = swift::getAvailabilityConstraintForDeclInDomain(
369+
decl, AvailabilityContext::forAlwaysAvailable(ctx), domain))
370+
return constraint->getAttr().getIntroducedRange(ctx);
368371

369372
return std::nullopt;
370373
}
@@ -379,22 +382,12 @@ AvailabilityScope::getExplicitAvailabilityRange() const {
379382
case Reason::GuardStmtElseBranch:
380383
case Reason::WhileStmtBody:
381384
// Availability is inherently explicit for all of these nodes.
382-
return getPlatformAvailabilityRange();
385+
return getAvailabilityContext().getAvailabilityRange(domain, ctx);
383386
}
384387

385388
llvm_unreachable("Unhandled Reason in switch.");
386389
}
387390

388-
static std::string
389-
stringForAvailability(const AvailabilityRange &availability) {
390-
if (availability.isAlwaysAvailable())
391-
return "all";
392-
if (availability.isKnownUnreachable())
393-
return "none";
394-
395-
return availability.getVersionString();
396-
}
397-
398391
void AvailabilityScope::print(raw_ostream &OS, SourceManager &SrcMgr,
399392
unsigned Indent) const {
400393
OS.indent(Indent);
@@ -446,9 +439,6 @@ void AvailabilityScope::print(raw_ostream &OS, SourceManager &SrcMgr,
446439
}
447440
}
448441

449-
if (auto explicitAvailability = getExplicitAvailabilityRange())
450-
OS << " explicit_version=" << stringForAvailability(*explicitAvailability);
451-
452442
for (AvailabilityScope *Child : Children) {
453443
OS << '\n';
454444
Child->print(OS, SrcMgr, Indent + 2);

lib/AST/AvailabilityScopeBuilder.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,9 +1077,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
10771077
// current scope is completely contained in the range for the spec, then
10781078
// a version query can never be false, so the spec is useless.
10791079
// If so, report this.
1080-
// FIXME: [availability] Diagnose non-platform queries as useless too.
1081-
auto explicitRange = currentScope->getExplicitAvailabilityRange();
1082-
if (domain.isPlatform() && explicitRange && trueRange &&
1080+
auto explicitRange =
1081+
currentScope->getExplicitAvailabilityRange(domain, Context);
1082+
if (explicitRange && trueRange &&
10831083
explicitRange->isContainedIn(*trueRange)) {
10841084
// Platform unavailability queries never refine availability so don't
10851085
// diangose them.
@@ -1088,17 +1088,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
10881088

10891089
DiagnosticEngine &diags = Context.Diags;
10901090
if (currentScope->getReason() != AvailabilityScope::Reason::Root) {
1091-
PlatformKind bestPlatform = targetPlatform(Context.LangOpts);
1092-
1093-
// If possible, try to report the diagnostic in terms for the
1094-
// platform the user uttered in the '#available()'. For a platform
1095-
// that inherits availability from another platform it may be
1096-
// different from the platform specified in the target triple.
1097-
if (domain.getPlatformKind() != PlatformKind::none)
1098-
bestPlatform = domain.getPlatformKind();
10991091
diags.diagnose(query->getLoc(),
11001092
diag::availability_query_useless_enclosing_scope,
1101-
platformString(bestPlatform));
1093+
domain.getNameForAttributePrinting());
11021094
diags.diagnose(
11031095
currentScope->getIntroductionLoc(),
11041096
diag::availability_query_useless_enclosing_scope_here);

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@ static bool fixAvailabilityByNarrowingNearbyVersionCheck(
685685
return false;
686686

687687
// FIXME: [availability] Support fixing availability for versionless domains.
688-
auto ExplicitAvailability = scope->getExplicitAvailabilityRange();
688+
auto ExplicitAvailability =
689+
scope->getExplicitAvailabilityRange(Domain, Context);
689690
if (ExplicitAvailability && !RequiredAvailability.isAlwaysAvailable() &&
690691
scope->getReason() != AvailabilityScope::Reason::Root &&
691692
RequiredAvailability.isContainedIn(*ExplicitAvailability)) {

test/Availability/availability_custom_domains.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func testDeployment() {
4141
}
4242

4343
func testIfAvailable(_ truthy: Bool) {
44-
if #available(EnabledDomain) {
44+
if #available(EnabledDomain) { // expected-note {{enclosing scope here}}
4545
availableInEnabledDomain()
4646
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
4747
availableInDynamicDomain() // expected-error {{'availableInDynamicDomain()' is only available in DynamicDomain}}
@@ -59,7 +59,9 @@ func testIfAvailable(_ truthy: Bool) {
5959
unavailableInDynamicDomain()
6060
}
6161

62-
if #unavailable(EnabledDomain) {
62+
if #available(EnabledDomain) {} // expected-warning {{unnecessary check for 'EnabledDomain'; enclosing scope ensures guard will always be true}}
63+
64+
if #unavailable(EnabledDomain) { // FIXME: [availability] Diagnose as unreachable
6365
// Unreachable.
6466
availableInEnabledDomain()
6567
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
@@ -112,19 +114,25 @@ func testIfAvailable(_ truthy: Bool) {
112114
}
113115

114116
func testWhileAvailable() {
115-
while #available(EnabledDomain) {
117+
while #available(EnabledDomain) { // expected-note {{enclosing scope here}}
116118
availableInEnabledDomain()
117119
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
120+
121+
if #available(EnabledDomain) {} // expected-warning {{unnecessary check for 'EnabledDomain'; enclosing scope ensures guard will always be true}}
122+
if #unavailable(EnabledDomain) {} // FIXME: [availability] Diagnose as unreachable
118123
}
119124

120125
while #unavailable(EnabledDomain) {
121126
availableInEnabledDomain() // expected-error {{'availableInEnabledDomain()' is only available in EnabledDomain}}
122127
unavailableInEnabledDomain()
128+
129+
if #available(EnabledDomain) {} // FIXME: [availability] Diagnose as unreachable
130+
if #unavailable(EnabledDomain) {} // FIXME: [availability] Diagnose as redundant
123131
}
124132
}
125133

126134
func testGuardAvailable() {
127-
guard #available(EnabledDomain) else {
135+
guard #available(EnabledDomain) else { // expected-note {{enclosing scope here}}
128136
availableInEnabledDomain() // expected-error {{'availableInEnabledDomain()' is only available in EnabledDomain}}
129137
unavailableInEnabledDomain()
130138
availableInDynamicDomain() // expected-error {{'availableInDynamicDomain()' is only available in DynamicDomain}}
@@ -135,14 +143,17 @@ func testGuardAvailable() {
135143
availableInEnabledDomain()
136144
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
137145
availableInDynamicDomain() // expected-error {{'availableInDynamicDomain()' is only available in DynamicDomain}}
146+
147+
if #available(EnabledDomain) {} // expected-warning {{unnecessary check for 'EnabledDomain'; enclosing scope ensures guard will always be true}}
148+
if #unavailable(EnabledDomain) {} // FIXME: [availability] Diagnose as unreachable
138149
}
139150

140151
@available(EnabledDomain)
141-
func testEnabledDomainAvailable() {
152+
func testEnabledDomainAvailable() { // expected-note {{enclosing scope here}}
142153
availableInEnabledDomain()
143154
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
144155

145-
if #available(EnabledDomain) {} // FIXME: [availability] Diagnose as redundant
156+
if #available(EnabledDomain) {} // expected-warning {{unnecessary check for 'EnabledDomain'; enclosing scope ensures guard will always be true}}
146157
if #unavailable(EnabledDomain) {} // FIXME: [availability] Diagnose as unreachable
147158

148159
alwaysAvailable()

0 commit comments

Comments
 (0)