Skip to content

[Sema] Support negation/parens with __builtin_available #111439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions clang/lib/Sema/SemaAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,25 +1005,54 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
return true;
}

struct ExtractedAvailabilityExpr {
const ObjCAvailabilityCheckExpr *E = nullptr;
bool isNegated = false;
};

ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) {
const auto *E = IfCond;
bool IsNegated = false;
while (true) {
E = E->IgnoreParens();
if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) {
return ExtractedAvailabilityExpr{AE, IsNegated};
}

const auto *UO = dyn_cast<UnaryOperator>(E);
if (!UO || UO->getOpcode() != UO_LNot) {
return ExtractedAvailabilityExpr{};
}
E = UO->getSubExpr();
IsNegated = !IsNegated;
}
}

bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
VersionTuple CondVersion;
if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
CondVersion = E->getVersion();

// If we're using the '*' case here or if this check is redundant, then we
// use the enclosing version to check both branches.
if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
} else {
ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond());
if (!IfCond.E) {
// This isn't an availability checking 'if', we can just continue.
return Base::TraverseIfStmt(If);
}

VersionTuple CondVersion = IfCond.E->getVersion();
// If we're using the '*' case here or if this check is redundant, then we
// use the enclosing version to check both branches.
if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) {
return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
}

auto *Guarded = If->getThen();
auto *Unguarded = If->getElse();
if (IfCond.isNegated) {
std::swap(Guarded, Unguarded);
}

AvailabilityStack.push_back(CondVersion);
bool ShouldContinue = TraverseStmt(If->getThen());
bool ShouldContinue = TraverseStmt(Guarded);
AvailabilityStack.pop_back();

return ShouldContinue && TraverseStmt(If->getElse());
return ShouldContinue && TraverseStmt(Unguarded);
}

} // end anonymous namespace
Expand Down
25 changes: 24 additions & 1 deletion clang/test/Sema/attr-availability.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void test_10095131(void) {
#ifdef WARN_PARTIAL
// FIXME: This note should point to the declaration with the availability
// attribute.
// expected-note@+2 {{'PartiallyAvailable' has been marked as being introduced in macOS 10.8 here, but the deployment target is macOS 10.5}}
// expected-note@+2 5 {{'PartiallyAvailable' has been marked as being introduced in macOS 10.8 here, but the deployment target is macOS 10.5}}
#endif
extern void PartiallyAvailable(void) ;
void with_redeclaration(void) {
Expand All @@ -53,6 +53,29 @@ void with_redeclaration(void) {
enum PartialEnum p = kPartialEnumConstant;
}

#ifdef WARN_PARTIAL
void conditional_warnings() {
if (__builtin_available(macos 10.8, *)) {
PartiallyAvailable();
} else {
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
}
if (!__builtin_available(macos 10.8, *)) {
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
} else {
PartiallyAvailable();
}
if (!!!(!__builtin_available(macos 10.8, *))) {
PartiallyAvailable();
} else {
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
}
if (~__builtin_available(macos 10.8, *)) { // expected-warning {{does not guard availability here}}
PartiallyAvailable(); // expected-warning {{only available on macOS 10.8 or newer}} expected-note {{enclose 'PartiallyAvailable'}}
}
}
#endif

__attribute__((availability(macos, unavailable))) // expected-warning {{attribute 'availability' is ignored}}
enum {
NSDataWritingFileProtectionWriteOnly = 0x30000000,
Expand Down
Loading