Skip to content

Commit 1553cb5

Browse files
authored
[Sema] Support negation/parens with __builtin_available (#111439)
At present, `__builtin_available` is really restrictive with its use. Overall, this seems like a good thing, since the analyses behind it are not very expensive. That said, it's very straightforward to support these two cases: ``` if ((__builtin_available(foo, *))) { // ... } ``` and ``` if (!__builtin_available(foo, *)) { // ... } else { // ... } ``` Seems nice to do so.
1 parent cf5bbeb commit 1553cb5

File tree

2 files changed

+64
-12
lines changed

2 files changed

+64
-12
lines changed

clang/lib/Sema/SemaAvailability.cpp

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,25 +1005,54 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
10051005
return true;
10061006
}
10071007

1008+
struct ExtractedAvailabilityExpr {
1009+
const ObjCAvailabilityCheckExpr *E = nullptr;
1010+
bool isNegated = false;
1011+
};
1012+
1013+
ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) {
1014+
const auto *E = IfCond;
1015+
bool IsNegated = false;
1016+
while (true) {
1017+
E = E->IgnoreParens();
1018+
if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) {
1019+
return ExtractedAvailabilityExpr{AE, IsNegated};
1020+
}
1021+
1022+
const auto *UO = dyn_cast<UnaryOperator>(E);
1023+
if (!UO || UO->getOpcode() != UO_LNot) {
1024+
return ExtractedAvailabilityExpr{};
1025+
}
1026+
E = UO->getSubExpr();
1027+
IsNegated = !IsNegated;
1028+
}
1029+
}
1030+
10081031
bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
1009-
VersionTuple CondVersion;
1010-
if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
1011-
CondVersion = E->getVersion();
1012-
1013-
// If we're using the '*' case here or if this check is redundant, then we
1014-
// use the enclosing version to check both branches.
1015-
if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
1016-
return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
1017-
} else {
1032+
ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond());
1033+
if (!IfCond.E) {
10181034
// This isn't an availability checking 'if', we can just continue.
10191035
return Base::TraverseIfStmt(If);
10201036
}
10211037

1038+
VersionTuple CondVersion = IfCond.E->getVersion();
1039+
// If we're using the '*' case here or if this check is redundant, then we
1040+
// use the enclosing version to check both branches.
1041+
if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) {
1042+
return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
1043+
}
1044+
1045+
auto *Guarded = If->getThen();
1046+
auto *Unguarded = If->getElse();
1047+
if (IfCond.isNegated) {
1048+
std::swap(Guarded, Unguarded);
1049+
}
1050+
10221051
AvailabilityStack.push_back(CondVersion);
1023-
bool ShouldContinue = TraverseStmt(If->getThen());
1052+
bool ShouldContinue = TraverseStmt(Guarded);
10241053
AvailabilityStack.pop_back();
10251054

1026-
return ShouldContinue && TraverseStmt(If->getElse());
1055+
return ShouldContinue && TraverseStmt(Unguarded);
10271056
}
10281057

10291058
} // end anonymous namespace

clang/test/Sema/attr-availability.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void test_10095131(void) {
4040
#ifdef WARN_PARTIAL
4141
// FIXME: This note should point to the declaration with the availability
4242
// attribute.
43-
// expected-note@+2 {{'PartiallyAvailable' has been marked as being introduced in macOS 10.8 here, but the deployment target is macOS 10.5}}
43+
// expected-note@+2 5 {{'PartiallyAvailable' has been marked as being introduced in macOS 10.8 here, but the deployment target is macOS 10.5}}
4444
#endif
4545
extern void PartiallyAvailable(void) ;
4646
void with_redeclaration(void) {
@@ -53,6 +53,29 @@ void with_redeclaration(void) {
5353
enum PartialEnum p = kPartialEnumConstant;
5454
}
5555

56+
#ifdef WARN_PARTIAL
57+
void conditional_warnings() {
58+
if (__builtin_available(macos 10.8, *)) {
59+
PartiallyAvailable();
60+
} else {
61+
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
62+
}
63+
if (!__builtin_available(macos 10.8, *)) {
64+
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
65+
} else {
66+
PartiallyAvailable();
67+
}
68+
if (!!!(!__builtin_available(macos 10.8, *))) {
69+
PartiallyAvailable();
70+
} else {
71+
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
72+
}
73+
if (~__builtin_available(macos 10.8, *)) { // expected-warning {{does not guard availability here}}
74+
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
75+
}
76+
}
77+
#endif
78+
5679
__attribute__((availability(macos, unavailable))) // expected-warning {{attribute 'availability' is ignored}}
5780
enum {
5881
NSDataWritingFileProtectionWriteOnly = 0x30000000,

0 commit comments

Comments
 (0)