| 1 | //===- Availability.cpp --------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file implements the Availability information for Decls. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "clang/AST/Availability.h" |
| 14 | #include "clang/AST/ASTContext.h" |
| 15 | #include "clang/AST/Attr.h" |
| 16 | #include "clang/AST/Decl.h" |
| 17 | #include "clang/Basic/TargetInfo.h" |
| 18 | |
| 19 | namespace { |
| 20 | |
| 21 | /// Represents the availability of a symbol across platforms. |
| 22 | struct AvailabilitySet { |
| 23 | bool UnconditionallyDeprecated = false; |
| 24 | bool UnconditionallyUnavailable = false; |
| 25 | |
| 26 | void insert(clang::AvailabilityInfo &&Availability) { |
| 27 | auto *Found = getForPlatform(Domain: Availability.Domain); |
| 28 | if (Found) |
| 29 | Found->mergeWith(Other: std::move(Availability)); |
| 30 | else |
| 31 | Availabilities.emplace_back(Args: std::move(Availability)); |
| 32 | } |
| 33 | |
| 34 | clang::AvailabilityInfo *getForPlatform(llvm::StringRef Domain) { |
| 35 | auto *It = llvm::find_if(Range&: Availabilities, |
| 36 | P: [Domain](const clang::AvailabilityInfo &Info) { |
| 37 | return Domain.compare(RHS: Info.Domain) == 0; |
| 38 | }); |
| 39 | return It == Availabilities.end() ? nullptr : It; |
| 40 | } |
| 41 | |
| 42 | private: |
| 43 | llvm::SmallVector<clang::AvailabilityInfo> Availabilities; |
| 44 | }; |
| 45 | |
| 46 | static void createInfoForDecl(const clang::Decl *Decl, |
| 47 | AvailabilitySet &Availabilities) { |
| 48 | // Collect availability attributes from all redeclarations. |
| 49 | for (const auto *RD : Decl->redecls()) { |
| 50 | for (const auto *A : RD->specific_attrs<clang::AvailabilityAttr>()) { |
| 51 | Availabilities.insert(Availability: clang::AvailabilityInfo( |
| 52 | A->getPlatform()->getName(), A->getIntroduced(), A->getDeprecated(), |
| 53 | A->getObsoleted(), A->getUnavailable(), false, false)); |
| 54 | } |
| 55 | |
| 56 | if (const auto *A = RD->getAttr<clang::UnavailableAttr>()) |
| 57 | if (!A->isImplicit()) |
| 58 | Availabilities.UnconditionallyUnavailable = true; |
| 59 | |
| 60 | if (const auto *A = RD->getAttr<clang::DeprecatedAttr>()) |
| 61 | if (!A->isImplicit()) |
| 62 | Availabilities.UnconditionallyDeprecated = true; |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | } // namespace |
| 67 | |
| 68 | namespace clang { |
| 69 | |
| 70 | void AvailabilityInfo::mergeWith(AvailabilityInfo Other) { |
| 71 | if (isDefault() && Other.isDefault()) |
| 72 | return; |
| 73 | |
| 74 | if (Domain.empty()) |
| 75 | Domain = Other.Domain; |
| 76 | |
| 77 | UnconditionallyUnavailable |= Other.UnconditionallyUnavailable; |
| 78 | UnconditionallyDeprecated |= Other.UnconditionallyDeprecated; |
| 79 | Unavailable |= Other.Unavailable; |
| 80 | |
| 81 | Introduced = std::max(a: Introduced, b: Other.Introduced); |
| 82 | |
| 83 | // Default VersionTuple is 0.0.0 so if both are non default let's pick the |
| 84 | // smallest version number, otherwise select the one that is non-zero if there |
| 85 | // is one. |
| 86 | if (!Deprecated.empty() && !Other.Deprecated.empty()) |
| 87 | Deprecated = std::min(a: Deprecated, b: Other.Deprecated); |
| 88 | else |
| 89 | Deprecated = std::max(a: Deprecated, b: Other.Deprecated); |
| 90 | |
| 91 | if (!Obsoleted.empty() && !Other.Obsoleted.empty()) |
| 92 | Obsoleted = std::min(a: Obsoleted, b: Other.Obsoleted); |
| 93 | else |
| 94 | Obsoleted = std::max(a: Obsoleted, b: Other.Obsoleted); |
| 95 | } |
| 96 | |
| 97 | AvailabilityInfo AvailabilityInfo::createFromDecl(const Decl *D) { |
| 98 | AvailabilitySet Availabilities; |
| 99 | // Walk DeclContexts upwards starting from D to find the combined availability |
| 100 | // of the symbol. |
| 101 | for (const auto *Ctx = D; Ctx; |
| 102 | Ctx = llvm::cast_or_null<Decl>(Val: Ctx->getDeclContext())) |
| 103 | createInfoForDecl(Decl: Ctx, Availabilities); |
| 104 | |
| 105 | if (auto *Avail = Availabilities.getForPlatform( |
| 106 | Domain: D->getASTContext().getTargetInfo().getPlatformName())) { |
| 107 | Avail->UnconditionallyDeprecated = Availabilities.UnconditionallyDeprecated; |
| 108 | Avail->UnconditionallyUnavailable = |
| 109 | Availabilities.UnconditionallyUnavailable; |
| 110 | return std::move(*Avail); |
| 111 | } |
| 112 | |
| 113 | AvailabilityInfo Avail; |
| 114 | Avail.UnconditionallyDeprecated = Availabilities.UnconditionallyDeprecated; |
| 115 | Avail.UnconditionallyUnavailable = Availabilities.UnconditionallyUnavailable; |
| 116 | return Avail; |
| 117 | } |
| 118 | |
| 119 | } // namespace clang |
| 120 | |