[clang] 57c9780 - [clang][ExtractAPI] Record availability information on all platforms

Daniel Grumberg via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 19 14:55:06 PDT 2022


Author: Daniel Grumberg
Date: 2022-08-19T14:54:52-07:00
New Revision: 57c9780d60b15baf0eba4393857affce47f60aa7

URL: https://github.com/llvm/llvm-project/commit/57c9780d60b15baf0eba4393857affce47f60aa7
DIFF: https://github.com/llvm/llvm-project/commit/57c9780d60b15baf0eba4393857affce47f60aa7.diff

LOG: [clang][ExtractAPI] Record availability information on all platforms

Currently ExtractAPI only emits availability information for the
current platform. This makes it easy for clients to get all availability
information for a given symbol in one invocation as opposed to having to invoke
clang once per-platform and then merge the symbol-graphs.

Differential Revision: https://reviews.llvm.org/D130918

Added: 
    clang/lib/ExtractAPI/AvailabilityInfo.cpp
    clang/test/ExtractAPI/availability.c

Modified: 
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/AvailabilityInfo.h
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/CMakeLists.txt
    clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 53db46ca44c13..b77d76d500df6 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -58,7 +58,7 @@ struct APIRecord {
   StringRef USR;
   StringRef Name;
   PresumedLoc Location;
-  AvailabilityInfo Availability;
+  AvailabilitySet Availabilities;
   LinkageInfo Linkage;
 
   /// Documentation comment lines attached to this symbol declaration.
@@ -102,12 +102,13 @@ struct APIRecord {
   APIRecord() = delete;
 
   APIRecord(RecordKind Kind, StringRef USR, StringRef Name,
-            PresumedLoc Location, const AvailabilityInfo &Availability,
+            PresumedLoc Location, AvailabilitySet Availabilities,
             LinkageInfo Linkage, const DocComment &Comment,
             DeclarationFragments Declaration, DeclarationFragments SubHeading)
-      : USR(USR), Name(Name), Location(Location), Availability(Availability),
-        Linkage(Linkage), Comment(Comment), Declaration(Declaration),
-        SubHeading(SubHeading), Kind(Kind) {}
+      : USR(USR), Name(Name), Location(Location),
+        Availabilities(std::move(Availabilities)), Linkage(Linkage),
+        Comment(Comment), Declaration(Declaration), SubHeading(SubHeading),
+        Kind(Kind) {}
 
   // Pure virtual destructor to make APIRecord abstract
   virtual ~APIRecord() = 0;
@@ -118,13 +119,13 @@ struct GlobalFunctionRecord : APIRecord {
   FunctionSignature Signature;
 
   GlobalFunctionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                       const AvailabilityInfo &Availability,
-                       LinkageInfo Linkage, const DocComment &Comment,
+                       AvailabilitySet Availabilities, LinkageInfo Linkage,
+                       const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
                        FunctionSignature Signature)
-      : APIRecord(RK_GlobalFunction, USR, Name, Loc, Availability, Linkage,
-                  Comment, Declaration, SubHeading),
+      : APIRecord(RK_GlobalFunction, USR, Name, Loc, std::move(Availabilities),
+                  Linkage, Comment, Declaration, SubHeading),
         Signature(Signature) {}
 
   static bool classof(const APIRecord *Record) {
@@ -138,12 +139,12 @@ struct GlobalFunctionRecord : APIRecord {
 /// This holds information associated with global functions.
 struct GlobalVariableRecord : APIRecord {
   GlobalVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                       const AvailabilityInfo &Availability,
-                       LinkageInfo Linkage, const DocComment &Comment,
+                       AvailabilitySet Availabilities, LinkageInfo Linkage,
+                       const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading)
-      : APIRecord(RK_GlobalVariable, USR, Name, Loc, Availability, Linkage,
-                  Comment, Declaration, SubHeading) {}
+      : APIRecord(RK_GlobalVariable, USR, Name, Loc, std::move(Availabilities),
+                  Linkage, Comment, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_GlobalVariable;
@@ -156,11 +157,10 @@ struct GlobalVariableRecord : APIRecord {
 /// This holds information associated with enum constants.
 struct EnumConstantRecord : APIRecord {
   EnumConstantRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                     const AvailabilityInfo &Availability,
-                     const DocComment &Comment,
+                     AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading)
-      : APIRecord(RK_EnumConstant, USR, Name, Loc, Availability,
+      : APIRecord(RK_EnumConstant, USR, Name, Loc, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
@@ -176,10 +176,10 @@ struct EnumRecord : APIRecord {
   SmallVector<std::unique_ptr<EnumConstantRecord>> Constants;
 
   EnumRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-             const AvailabilityInfo &Availability, const DocComment &Comment,
+             AvailabilitySet Availabilities, const DocComment &Comment,
              DeclarationFragments Declaration, DeclarationFragments SubHeading)
-      : APIRecord(RK_Enum, USR, Name, Loc, Availability, LinkageInfo::none(),
-                  Comment, Declaration, SubHeading) {}
+      : APIRecord(RK_Enum, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_Enum;
@@ -192,10 +192,10 @@ struct EnumRecord : APIRecord {
 /// This holds information associated with struct fields.
 struct StructFieldRecord : APIRecord {
   StructFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                    const AvailabilityInfo &Availability,
-                    const DocComment &Comment, DeclarationFragments Declaration,
+                    AvailabilitySet Availabilities, const DocComment &Comment,
+                    DeclarationFragments Declaration,
                     DeclarationFragments SubHeading)
-      : APIRecord(RK_StructField, USR, Name, Loc, Availability,
+      : APIRecord(RK_StructField, USR, Name, Loc, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
@@ -211,11 +211,11 @@ struct StructRecord : APIRecord {
   SmallVector<std::unique_ptr<StructFieldRecord>> Fields;
 
   StructRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-               const AvailabilityInfo &Availability, const DocComment &Comment,
+               AvailabilitySet Availabilities, const DocComment &Comment,
                DeclarationFragments Declaration,
                DeclarationFragments SubHeading)
-      : APIRecord(RK_Struct, USR, Name, Loc, Availability, LinkageInfo::none(),
-                  Comment, Declaration, SubHeading) {}
+      : APIRecord(RK_Struct, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_Struct;
@@ -241,13 +241,12 @@ struct ObjCPropertyRecord : APIRecord {
   bool IsOptional;
 
   ObjCPropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                     const AvailabilityInfo &Availability,
-                     const DocComment &Comment,
+                     AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, AttributeKind Attributes,
                      StringRef GetterName, StringRef SetterName,
                      bool IsOptional)
-      : APIRecord(RK_ObjCProperty, USR, Name, Loc, Availability,
+      : APIRecord(RK_ObjCProperty, USR, Name, Loc, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading),
         Attributes(Attributes), GetterName(GetterName), SetterName(SetterName),
         IsOptional(IsOptional) {}
@@ -270,12 +269,12 @@ struct ObjCInstanceVariableRecord : APIRecord {
   AccessControl Access;
 
   ObjCInstanceVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                             const AvailabilityInfo &Availability,
+                             AvailabilitySet Availabilities,
                              const DocComment &Comment,
                              DeclarationFragments Declaration,
                              DeclarationFragments SubHeading,
                              AccessControl Access)
-      : APIRecord(RK_ObjCIvar, USR, Name, Loc, Availability,
+      : APIRecord(RK_ObjCIvar, USR, Name, Loc, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading),
         Access(Access) {}
 
@@ -293,11 +292,11 @@ struct ObjCMethodRecord : APIRecord {
   bool IsInstanceMethod;
 
   ObjCMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                   const AvailabilityInfo &Availability,
-                   const DocComment &Comment, DeclarationFragments Declaration,
+                   AvailabilitySet Availabilities, const DocComment &Comment,
+                   DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, FunctionSignature Signature,
                    bool IsInstanceMethod)
-      : APIRecord(RK_ObjCMethod, USR, Name, Loc, Availability,
+      : APIRecord(RK_ObjCMethod, USR, Name, Loc, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading),
         Signature(Signature), IsInstanceMethod(IsInstanceMethod) {}
 
@@ -341,12 +340,12 @@ struct ObjCContainerRecord : APIRecord {
   ObjCContainerRecord() = delete;
 
   ObjCContainerRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                      PresumedLoc Loc, const AvailabilityInfo &Availability,
+                      PresumedLoc Loc, AvailabilitySet Availabilities,
                       LinkageInfo Linkage, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading)
-      : APIRecord(Kind, USR, Name, Loc, Availability, Linkage, Comment,
-                  Declaration, SubHeading) {}
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), Linkage,
+                  Comment, Declaration, SubHeading) {}
 
   virtual ~ObjCContainerRecord() = 0;
 };
@@ -356,13 +355,12 @@ struct ObjCCategoryRecord : ObjCContainerRecord {
   SymbolReference Interface;
 
   ObjCCategoryRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                     const AvailabilityInfo &Availability,
-                     const DocComment &Comment,
+                     AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, SymbolReference Interface)
-      : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Loc, Availability,
-                            LinkageInfo::none(), Comment, Declaration,
-                            SubHeading),
+      : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Loc,
+                            std::move(Availabilities), LinkageInfo::none(),
+                            Comment, Declaration, SubHeading),
         Interface(Interface) {}
 
   static bool classof(const APIRecord *Record) {
@@ -380,13 +378,14 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
   SmallVector<ObjCCategoryRecord *> Categories;
 
   ObjCInterfaceRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                      const AvailabilityInfo &Availability, LinkageInfo Linkage,
+                      AvailabilitySet Availabilities, LinkageInfo Linkage,
                       const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading,
                       SymbolReference SuperClass)
-      : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Loc, Availability,
-                            Linkage, Comment, Declaration, SubHeading),
+      : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Loc,
+                            std::move(Availabilities), Linkage, Comment,
+                            Declaration, SubHeading),
         SuperClass(SuperClass) {}
 
   static bool classof(const APIRecord *Record) {
@@ -400,13 +399,12 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
 /// This holds information associated with Objective-C protocols.
 struct ObjCProtocolRecord : ObjCContainerRecord {
   ObjCProtocolRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                     const AvailabilityInfo &Availability,
-                     const DocComment &Comment,
+                     AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading)
-      : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Loc, Availability,
-                            LinkageInfo::none(), Comment, Declaration,
-                            SubHeading) {}
+      : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Loc,
+                            std::move(Availabilities), LinkageInfo::none(),
+                            Comment, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_ObjCProtocol;
@@ -421,7 +419,7 @@ struct MacroDefinitionRecord : APIRecord {
   MacroDefinitionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                         DeclarationFragments Declaration,
                         DeclarationFragments SubHeading)
-      : APIRecord(RK_MacroDefinition, USR, Name, Loc, AvailabilityInfo(),
+      : APIRecord(RK_MacroDefinition, USR, Name, Loc, AvailabilitySet(),
                   LinkageInfo(), {}, Declaration, SubHeading) {}
 
   static bool classof(const APIRecord *Record) {
@@ -441,11 +439,11 @@ struct TypedefRecord : APIRecord {
   SymbolReference UnderlyingType;
 
   TypedefRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                const AvailabilityInfo &Availability, const DocComment &Comment,
+                AvailabilitySet Availabilities, const DocComment &Comment,
                 DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, SymbolReference UnderlyingType)
-      : APIRecord(RK_Typedef, USR, Name, Loc, Availability, LinkageInfo(),
-                  Comment, Declaration, SubHeading),
+      : APIRecord(RK_Typedef, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo(), Comment, Declaration, SubHeading),
         UnderlyingType(UnderlyingType) {}
 
   static bool classof(const APIRecord *Record) {
@@ -478,7 +476,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   GlobalVariableRecord *
   addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
-               const AvailabilityInfo &Availability, LinkageInfo Linkage,
+               AvailabilitySet Availability, LinkageInfo Linkage,
                const DocComment &Comment, DeclarationFragments Declaration,
                DeclarationFragments SubHeading);
 
@@ -490,7 +488,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   GlobalFunctionRecord *
   addGlobalFunction(StringRef Name, StringRef USR, PresumedLoc Loc,
-                    const AvailabilityInfo &Availability, LinkageInfo Linkage,
+                    AvailabilitySet Availability, LinkageInfo Linkage,
                     const DocComment &Comment, DeclarationFragments Declaration,
                     DeclarationFragments SubHeading,
                     FunctionSignature Signature);
@@ -503,7 +501,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   EnumConstantRecord *addEnumConstant(EnumRecord *Enum, StringRef Name,
                                       StringRef USR, PresumedLoc Loc,
-                                      const AvailabilityInfo &Availability,
+                                      AvailabilitySet Availability,
                                       const DocComment &Comment,
                                       DeclarationFragments Declaration,
                                       DeclarationFragments SubHeading);
@@ -515,8 +513,7 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   EnumRecord *addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
-                      const AvailabilityInfo &Availability,
-                      const DocComment &Comment,
+                      AvailabilitySet Availability, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading);
 
@@ -528,7 +525,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   StructFieldRecord *addStructField(StructRecord *Struct, StringRef Name,
                                     StringRef USR, PresumedLoc Loc,
-                                    const AvailabilityInfo &Availability,
+                                    AvailabilitySet Availability,
                                     const DocComment &Comment,
                                     DeclarationFragments Declaration,
                                     DeclarationFragments SubHeading);
@@ -540,7 +537,7 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   StructRecord *addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
-                          const AvailabilityInfo &Availability,
+                          AvailabilitySet Availability,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading);
@@ -553,8 +550,8 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCCategoryRecord *
   addObjCCategory(StringRef Name, StringRef USR, PresumedLoc Loc,
-                  const AvailabilityInfo &Availability,
-                  const DocComment &Comment, DeclarationFragments Declaration,
+                  AvailabilitySet Availability, const DocComment &Comment,
+                  DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, SymbolReference Interface);
 
   /// Create and add an Objective-C interface record into the API set.
@@ -565,7 +562,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCInterfaceRecord *
   addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
-                   const AvailabilityInfo &Availability, LinkageInfo Linkage,
+                   AvailabilitySet Availability, LinkageInfo Linkage,
                    const DocComment &Comment, DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, SymbolReference SuperClass);
 
@@ -577,7 +574,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCMethodRecord *
   addObjCMethod(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-                PresumedLoc Loc, const AvailabilityInfo &Availability,
+                PresumedLoc Loc, AvailabilitySet Availability,
                 const DocComment &Comment, DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, FunctionSignature Signature,
                 bool IsInstanceMethod);
@@ -590,7 +587,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCPropertyRecord *
   addObjCProperty(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-                  PresumedLoc Loc, const AvailabilityInfo &Availability,
+                  PresumedLoc Loc, AvailabilitySet Availability,
                   const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading,
                   ObjCPropertyRecord::AttributeKind Attributes,
@@ -604,9 +601,8 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCInstanceVariableRecord *addObjCInstanceVariable(
       ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-      PresumedLoc Loc, const AvailabilityInfo &Availability,
-      const DocComment &Comment, DeclarationFragments Declaration,
-      DeclarationFragments SubHeading,
+      PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
       ObjCInstanceVariableRecord::AccessControl Access);
 
   /// Create and add an Objective-C protocol record into the API set.
@@ -617,7 +613,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCProtocolRecord *addObjCProtocol(StringRef Name, StringRef USR,
                                       PresumedLoc Loc,
-                                      const AvailabilityInfo &Availability,
+                                      AvailabilitySet Availability,
                                       const DocComment &Comment,
                                       DeclarationFragments Declaration,
                                       DeclarationFragments SubHeading);
@@ -641,7 +637,7 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   TypedefRecord *addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
-                            const AvailabilityInfo &Availability,
+                            AvailabilitySet Availability,
                             const DocComment &Comment,
                             DeclarationFragments Declaration,
                             DeclarationFragments SubHeading,

diff  --git a/clang/include/clang/ExtractAPI/AvailabilityInfo.h b/clang/include/clang/ExtractAPI/AvailabilityInfo.h
index f5f85bd576120..a258bc52c125d 100644
--- a/clang/include/clang/ExtractAPI/AvailabilityInfo.h
+++ b/clang/include/clang/ExtractAPI/AvailabilityInfo.h
@@ -15,6 +15,8 @@
 #ifndef LLVM_CLANG_EXTRACTAPI_AVAILABILITY_INFO_H
 #define LLVM_CLANG_EXTRACTAPI_AVAILABILITY_INFO_H
 
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/VersionTuple.h"
 #include "llvm/Support/raw_ostream.h"
@@ -24,20 +26,38 @@ using llvm::VersionTuple;
 namespace clang {
 namespace extractapi {
 
-/// Stores availability attributes of a symbol.
+/// Stores availability attributes of a symbol in a given domain.
 struct AvailabilityInfo {
+  /// The domain for which this availability info item applies
+  std::string Domain;
   VersionTuple Introduced;
   VersionTuple Deprecated;
   VersionTuple Obsoleted;
-  bool Unavailable{false};
-  bool UnconditionallyDeprecated{false};
-  bool UnconditionallyUnavailable{false};
 
-  /// Determine if this AvailabilityInfo represents the default availability.
-  bool isDefault() const { return *this == AvailabilityInfo(); }
+  AvailabilityInfo() = default;
+
+  AvailabilityInfo(StringRef Domain, VersionTuple I, VersionTuple D,
+                   VersionTuple O)
+      : Domain(Domain), Introduced(I), Deprecated(D), Obsoleted(O) {}
+};
+
+class AvailabilitySet {
+private:
+  using AvailabilityList = llvm::SmallVector<AvailabilityInfo, 4>;
+  AvailabilityList Availabilities;
+
+  bool UnconditionallyDeprecated = false;
+  bool UnconditionallyUnavailable = false;
+
+public:
+  AvailabilitySet(const Decl *Decl);
+  AvailabilitySet() = default;
 
-  /// Check if the symbol is unavailable.
-  bool isUnavailable() const { return Unavailable; }
+  AvailabilityList::const_iterator begin() const {
+    return Availabilities.begin();
+  }
+
+  AvailabilityList::const_iterator end() const { return Availabilities.end(); }
 
   /// Check if the symbol is unconditionally deprecated.
   ///
@@ -51,27 +71,10 @@ struct AvailabilityInfo {
     return UnconditionallyUnavailable;
   }
 
-  AvailabilityInfo() = default;
-
-  AvailabilityInfo(VersionTuple I, VersionTuple D, VersionTuple O, bool U,
-                   bool UD, bool UU)
-      : Introduced(I), Deprecated(D), Obsoleted(O), Unavailable(U),
-        UnconditionallyDeprecated(UD), UnconditionallyUnavailable(UU) {}
-
-  friend bool operator==(const AvailabilityInfo &Lhs,
-                         const AvailabilityInfo &Rhs);
+  /// Determine if this AvailabilitySet represents default availability.
+  bool isDefault() const { return Availabilities.empty(); }
 };
 
-inline bool operator==(const AvailabilityInfo &Lhs,
-                       const AvailabilityInfo &Rhs) {
-  return std::tie(Lhs.Introduced, Lhs.Deprecated, Lhs.Obsoleted,
-                  Lhs.Unavailable, Lhs.UnconditionallyDeprecated,
-                  Lhs.UnconditionallyUnavailable) ==
-         std::tie(Rhs.Introduced, Rhs.Deprecated, Rhs.Obsoleted,
-                  Rhs.Unavailable, Rhs.UnconditionallyDeprecated,
-                  Rhs.UnconditionallyUnavailable);
-}
-
 } // namespace extractapi
 } // namespace clang
 

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 073e36f320d37..8ab03a833e3c2 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -43,68 +43,77 @@ RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
 
 GlobalVariableRecord *
 APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
-                     const AvailabilityInfo &Availability, LinkageInfo Linkage,
+                     AvailabilitySet Availabilities, LinkageInfo Linkage,
                      const DocComment &Comment, DeclarationFragments Fragments,
                      DeclarationFragments SubHeading) {
-  return addTopLevelRecord(GlobalVariables, USR, Name, Loc, Availability,
-                           Linkage, Comment, Fragments, SubHeading);
+  return addTopLevelRecord(GlobalVariables, USR, Name, Loc,
+                           std::move(Availabilities), Linkage, Comment,
+                           Fragments, SubHeading);
 }
 
 GlobalFunctionRecord *APISet::addGlobalFunction(
     StringRef Name, StringRef USR, PresumedLoc Loc,
-    const AvailabilityInfo &Availability, LinkageInfo Linkage,
+    AvailabilitySet Availabilities, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Fragments,
     DeclarationFragments SubHeading, FunctionSignature Signature) {
-  return addTopLevelRecord(GlobalFunctions, USR, Name, Loc, Availability,
-                           Linkage, Comment, Fragments, SubHeading, Signature);
+  return addTopLevelRecord(GlobalFunctions, USR, Name, Loc,
+                           std::move(Availabilities), Linkage, Comment,
+                           Fragments, SubHeading, Signature);
 }
 
-EnumConstantRecord *APISet::addEnumConstant(
-    EnumRecord *Enum, StringRef Name, StringRef USR, PresumedLoc Loc,
-    const AvailabilityInfo &Availability, const DocComment &Comment,
-    DeclarationFragments Declaration, DeclarationFragments SubHeading) {
+EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
+                                            StringRef USR, PresumedLoc Loc,
+                                            AvailabilitySet Availabilities,
+                                            const DocComment &Comment,
+                                            DeclarationFragments Declaration,
+                                            DeclarationFragments SubHeading) {
   auto Record = std::make_unique<EnumConstantRecord>(
-      USR, Name, Loc, Availability, Comment, Declaration, SubHeading);
+      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+      SubHeading);
   return Enum->Constants.emplace_back(std::move(Record)).get();
 }
 
 EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
-                            const AvailabilityInfo &Availability,
+                            AvailabilitySet Availabilities,
                             const DocComment &Comment,
                             DeclarationFragments Declaration,
                             DeclarationFragments SubHeading) {
-  return addTopLevelRecord(Enums, USR, Name, Loc, Availability, Comment,
-                           Declaration, SubHeading);
+  return addTopLevelRecord(Enums, USR, Name, Loc, std::move(Availabilities),
+                           Comment, Declaration, SubHeading);
 }
 
 StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
                                           StringRef USR, PresumedLoc Loc,
-                                          const AvailabilityInfo &Availability,
+                                          AvailabilitySet Availabilities,
                                           const DocComment &Comment,
                                           DeclarationFragments Declaration,
                                           DeclarationFragments SubHeading) {
   auto Record = std::make_unique<StructFieldRecord>(
-      USR, Name, Loc, Availability, Comment, Declaration, SubHeading);
+      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+      SubHeading);
   return Struct->Fields.emplace_back(std::move(Record)).get();
 }
 
 StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
-                                const AvailabilityInfo &Availability,
+                                AvailabilitySet Availabilities,
                                 const DocComment &Comment,
                                 DeclarationFragments Declaration,
                                 DeclarationFragments SubHeading) {
-  return addTopLevelRecord(Structs, USR, Name, Loc, Availability, Comment,
-                           Declaration, SubHeading);
+  return addTopLevelRecord(Structs, USR, Name, Loc, std::move(Availabilities),
+                           Comment, Declaration, SubHeading);
 }
 
-ObjCCategoryRecord *APISet::addObjCCategory(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
-    const AvailabilityInfo &Availability, const DocComment &Comment,
-    DeclarationFragments Declaration, DeclarationFragments SubHeading,
-    SymbolReference Interface) {
+ObjCCategoryRecord *APISet::addObjCCategory(StringRef Name, StringRef USR,
+                                            PresumedLoc Loc,
+                                            AvailabilitySet Availabilities,
+                                            const DocComment &Comment,
+                                            DeclarationFragments Declaration,
+                                            DeclarationFragments SubHeading,
+                                            SymbolReference Interface) {
   // Create the category record.
-  auto *Record = addTopLevelRecord(ObjCCategories, USR, Name, Loc, Availability,
-                                   Comment, Declaration, SubHeading, Interface);
+  auto *Record = addTopLevelRecord(ObjCCategories, USR, Name, Loc,
+                                   std::move(Availabilities), Comment,
+                                   Declaration, SubHeading, Interface);
 
   // If this category is extending a known interface, associate it with the
   // ObjCInterfaceRecord.
@@ -117,56 +126,57 @@ ObjCCategoryRecord *APISet::addObjCCategory(
 
 ObjCInterfaceRecord *APISet::addObjCInterface(
     StringRef Name, StringRef USR, PresumedLoc Loc,
-    const AvailabilityInfo &Availability, LinkageInfo Linkage,
+    AvailabilitySet Availabilities, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Declaration,
     DeclarationFragments SubHeading, SymbolReference SuperClass) {
-  return addTopLevelRecord(ObjCInterfaces, USR, Name, Loc, Availability,
-                           Linkage, Comment, Declaration, SubHeading,
-                           SuperClass);
+  return addTopLevelRecord(ObjCInterfaces, USR, Name, Loc,
+                           std::move(Availabilities), Linkage, Comment,
+                           Declaration, SubHeading, SuperClass);
 }
 
 ObjCMethodRecord *APISet::addObjCMethod(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-    PresumedLoc Loc, const AvailabilityInfo &Availability,
-    const DocComment &Comment, DeclarationFragments Declaration,
-    DeclarationFragments SubHeading, FunctionSignature Signature,
-    bool IsInstanceMethod) {
+    PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, bool IsInstanceMethod) {
   auto Record = std::make_unique<ObjCMethodRecord>(
-      USR, Name, Loc, Availability, Comment, Declaration, SubHeading, Signature,
-      IsInstanceMethod);
+      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+      SubHeading, Signature, IsInstanceMethod);
   return Container->Methods.emplace_back(std::move(Record)).get();
 }
 
 ObjCPropertyRecord *APISet::addObjCProperty(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-    PresumedLoc Loc, const AvailabilityInfo &Availability,
-    const DocComment &Comment, DeclarationFragments Declaration,
-    DeclarationFragments SubHeading,
+    PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
     ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
     StringRef SetterName, bool IsOptional) {
   auto Record = std::make_unique<ObjCPropertyRecord>(
-      USR, Name, Loc, Availability, Comment, Declaration, SubHeading,
-      Attributes, GetterName, SetterName, IsOptional);
+      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+      SubHeading, Attributes, GetterName, SetterName, IsOptional);
   return Container->Properties.emplace_back(std::move(Record)).get();
 }
 
 ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-    PresumedLoc Loc, const AvailabilityInfo &Availability,
-    const DocComment &Comment, DeclarationFragments Declaration,
-    DeclarationFragments SubHeading,
+    PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
     ObjCInstanceVariableRecord::AccessControl Access) {
   auto Record = std::make_unique<ObjCInstanceVariableRecord>(
-      USR, Name, Loc, Availability, Comment, Declaration, SubHeading, Access);
+      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+      SubHeading, Access);
   return Container->Ivars.emplace_back(std::move(Record)).get();
 }
 
-ObjCProtocolRecord *APISet::addObjCProtocol(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
-    const AvailabilityInfo &Availability, const DocComment &Comment,
-    DeclarationFragments Declaration, DeclarationFragments SubHeading) {
-  return addTopLevelRecord(ObjCProtocols, USR, Name, Loc, Availability, Comment,
-                           Declaration, SubHeading);
+ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
+                                            PresumedLoc Loc,
+                                            AvailabilitySet Availabilities,
+                                            const DocComment &Comment,
+                                            DeclarationFragments Declaration,
+                                            DeclarationFragments SubHeading) {
+  return addTopLevelRecord(ObjCProtocols, USR, Name, Loc,
+                           std::move(Availabilities), Comment, Declaration,
+                           SubHeading);
 }
 
 MacroDefinitionRecord *
@@ -178,13 +188,13 @@ APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
 
 TypedefRecord *APISet::addTypedef(StringRef Name, StringRef USR,
                                   PresumedLoc Loc,
-                                  const AvailabilityInfo &Availability,
+                                  AvailabilitySet Availabilities,
                                   const DocComment &Comment,
                                   DeclarationFragments Declaration,
                                   DeclarationFragments SubHeading,
                                   SymbolReference UnderlyingType) {
-  return addTopLevelRecord(Typedefs, USR, Name, Loc, Availability, Comment,
-                           Declaration, SubHeading, UnderlyingType);
+  return addTopLevelRecord(Typedefs, USR, Name, Loc, std::move(Availabilities),
+                           Comment, Declaration, SubHeading, UnderlyingType);
 }
 
 StringRef APISet::recordUSR(const Decl *D) {

diff  --git a/clang/lib/ExtractAPI/AvailabilityInfo.cpp b/clang/lib/ExtractAPI/AvailabilityInfo.cpp
new file mode 100644
index 0000000000000..ada64cfb92e64
--- /dev/null
+++ b/clang/lib/ExtractAPI/AvailabilityInfo.cpp
@@ -0,0 +1,50 @@
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/AST/Attr.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+using namespace extractapi;
+
+AvailabilitySet::AvailabilitySet(const Decl *Decl) {
+  // Collect availability attributes from all redeclrations.
+  for (const auto *RD : Decl->redecls()) {
+    if (const auto *A = RD->getAttr<UnavailableAttr>()) {
+      if (!A->isImplicit()) {
+        this->Availabilities.clear();
+        UnconditionallyUnavailable = true;
+      }
+    }
+
+    if (const auto *A = RD->getAttr<DeprecatedAttr>()) {
+      if (!A->isImplicit()) {
+        this->Availabilities.clear();
+        UnconditionallyDeprecated = true;
+      }
+    }
+
+    for (const auto *Attr : RD->specific_attrs<AvailabilityAttr>()) {
+      StringRef Domain = Attr->getPlatform()->getName();
+      auto *Availability =
+          llvm::find_if(Availabilities, [Domain](const AvailabilityInfo &Info) {
+            return Domain.equals(Info.Domain);
+          });
+      if (Availability != Availabilities.end()) {
+        // Get the highest introduced version for all redeclarations.
+        if (Availability->Introduced < Attr->getIntroduced())
+          Availability->Introduced = Attr->getIntroduced();
+
+        // Get the lowest deprecated version for all redeclarations.
+        if (Availability->Deprecated > Attr->getDeprecated())
+          Availability->Deprecated = Attr->getDeprecated();
+
+        // Get the lowest obsoleted version for all redeclarations.
+        if (Availability->Obsoleted > Attr->getObsoleted())
+          Availability->Obsoleted = Attr->getObsoleted();
+      } else {
+        Availabilities.emplace_back(Domain, Attr->getIntroduced(),
+                                    Attr->getDeprecated(),
+                                    Attr->getObsoleted());
+      }
+    }
+  }
+}

diff  --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt
index ed79282c14906..904321ba33a58 100644
--- a/clang/lib/ExtractAPI/CMakeLists.txt
+++ b/clang/lib/ExtractAPI/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangExtractAPI
   API.cpp
+  AvailabilityInfo.cpp
   ExtractAPIConsumer.cpp
   DeclarationFragments.cpp
   Serialization/SerializerBase.cpp

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index 1a785182e3634..969ee772fa063 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -264,7 +264,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
@@ -278,7 +277,7 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
         DeclarationFragmentsBuilder::getSubHeading(Decl);
 
     // Add the global variable record to the API set.
-    API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
+    API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
                      Declaration, SubHeading);
     return true;
   }
@@ -325,7 +324,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
@@ -341,8 +339,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
 
     // Add the function record to the API set.
-    API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
-                          Declaration, SubHeading, Signature);
+    API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
+                          Comment, Declaration, SubHeading, Signature);
     return true;
   }
 
@@ -366,7 +364,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -379,8 +376,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
         DeclarationFragmentsBuilder::getSubHeading(Decl);
 
     EnumRecord *EnumRecord =
-        API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
-                    Declaration, SubHeading);
+        API.addEnum(API.copyString(Name), USR, Loc, AvailabilitySet(Decl),
+                    Comment, Declaration, SubHeading);
 
     // Now collect information about the enumerators in this enum.
     recordEnumConstants(EnumRecord, Decl->enumerators());
@@ -407,7 +404,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -419,8 +415,9 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     DeclarationFragments SubHeading =
         DeclarationFragmentsBuilder::getSubHeading(Decl);
 
-    StructRecord *StructRecord = API.addStruct(
-        Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
+    StructRecord *StructRecord =
+        API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                      Declaration, SubHeading);
 
     // Now collect information about the fields in this struct.
     recordStructFields(StructRecord, Decl->fields());
@@ -441,7 +438,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
@@ -462,8 +458,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     }
 
     ObjCInterfaceRecord *ObjCInterfaceRecord =
-        API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
-                             Declaration, SubHeading, SuperClass);
+        API.addObjCInterface(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
+                             Comment, Declaration, SubHeading, SuperClass);
 
     // Record all methods (selectors). This doesn't include automatically
     // synthesized property methods.
@@ -488,7 +484,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -500,8 +495,9 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     DeclarationFragments SubHeading =
         DeclarationFragmentsBuilder::getSubHeading(Decl);
 
-    ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
-        Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
+    ObjCProtocolRecord *ObjCProtocolRecord =
+        API.addObjCProtocol(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                            Declaration, SubHeading);
 
     recordObjCMethods(ObjCProtocolRecord, Decl->methods());
     recordObjCProperties(ObjCProtocolRecord, Decl->properties());
@@ -524,7 +520,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
     StringRef Name = Decl->getName();
-    AvailabilityInfo Availability = getAvailability(Decl);
     StringRef USR = API.recordUSR(Decl);
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
@@ -536,7 +531,7 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
         TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
                                                                          API);
 
-    API.addTypedef(Name, USR, Loc, Availability, Comment,
+    API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
                    DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
                    DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
 
@@ -549,7 +544,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     StringRef USR = API.recordUSR(Decl);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    AvailabilityInfo Availability = getAvailability(Decl);
     DocComment Comment;
     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -565,8 +559,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
                               API.recordUSR(InterfaceDecl));
 
     ObjCCategoryRecord *ObjCCategoryRecord =
-        API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
-                            SubHeading, Interface);
+        API.addObjCCategory(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                            Declaration, SubHeading, Interface);
 
     recordObjCMethods(ObjCCategoryRecord, Decl->methods());
     recordObjCProperties(ObjCCategoryRecord, Decl->properties());
@@ -577,37 +571,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
   }
 
 private:
-  /// Get availability information of the declaration \p D.
-  AvailabilityInfo getAvailability(const Decl *D) const {
-    StringRef PlatformName = Context.getTargetInfo().getPlatformName();
-
-    AvailabilityInfo Availability;
-    // Collect availability attributes from all redeclarations.
-    for (const auto *RD : D->redecls()) {
-      for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
-        if (A->getPlatform()->getName() != PlatformName)
-          continue;
-        Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
-                                        A->getObsoleted(), A->getUnavailable(),
-                                        /* UnconditionallyDeprecated */ false,
-                                        /* UnconditionallyUnavailable */ false);
-        break;
-      }
-
-      if (const auto *A = RD->getAttr<UnavailableAttr>())
-        if (!A->isImplicit()) {
-          Availability.Unavailable = true;
-          Availability.UnconditionallyUnavailable = true;
-        }
-
-      if (const auto *A = RD->getAttr<DeprecatedAttr>())
-        if (!A->isImplicit())
-          Availability.UnconditionallyDeprecated = true;
-    }
-
-    return Availability;
-  }
-
   /// Collect API information for the enum constants and associate with the
   /// parent enum.
   void recordEnumConstants(EnumRecord *EnumRecord,
@@ -618,7 +581,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       StringRef USR = API.recordUSR(Constant);
       PresumedLoc Loc =
           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
-      AvailabilityInfo Availability = getAvailability(Constant);
       DocComment Comment;
       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -630,8 +592,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       DeclarationFragments SubHeading =
           DeclarationFragmentsBuilder::getSubHeading(Constant);
 
-      API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
-                          Declaration, SubHeading);
+      API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
+                          Comment, Declaration, SubHeading);
     }
   }
 
@@ -645,7 +607,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       StringRef USR = API.recordUSR(Field);
       PresumedLoc Loc =
           Context.getSourceManager().getPresumedLoc(Field->getLocation());
-      AvailabilityInfo Availability = getAvailability(Field);
       DocComment Comment;
       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -657,8 +618,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       DeclarationFragments SubHeading =
           DeclarationFragmentsBuilder::getSubHeading(Field);
 
-      API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
-                         Declaration, SubHeading);
+      API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
+                         Comment, Declaration, SubHeading);
     }
   }
 
@@ -675,7 +636,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       StringRef USR = API.recordUSR(Method);
       PresumedLoc Loc =
           Context.getSourceManager().getPresumedLoc(Method->getLocation());
-      AvailabilityInfo Availability = getAvailability(Method);
       DocComment Comment;
       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -689,8 +649,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       FunctionSignature Signature =
           DeclarationFragmentsBuilder::getFunctionSignature(Method);
 
-      API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
-                        Declaration, SubHeading, Signature,
+      API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
+                        Comment, Declaration, SubHeading, Signature,
                         Method->isInstanceMethod());
     }
   }
@@ -702,7 +662,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       StringRef USR = API.recordUSR(Property);
       PresumedLoc Loc =
           Context.getSourceManager().getPresumedLoc(Property->getLocation());
-      AvailabilityInfo Availability = getAvailability(Property);
       DocComment Comment;
       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -728,8 +687,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
         Attributes |= ObjCPropertyRecord::Class;
 
       API.addObjCProperty(
-          Container, Name, USR, Loc, Availability, Comment, Declaration,
-          SubHeading,
+          Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
+          Declaration, SubHeading,
           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
           GetterName, SetterName, Property->isOptional());
     }
@@ -745,7 +704,6 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       StringRef USR = API.recordUSR(Ivar);
       PresumedLoc Loc =
           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
-      AvailabilityInfo Availability = getAvailability(Ivar);
       DocComment Comment;
       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
@@ -760,8 +718,9 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
       ObjCInstanceVariableRecord::AccessControl Access =
           Ivar->getCanonicalAccessControl();
 
-      API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
-                                  Comment, Declaration, SubHeading, Access);
+      API.addObjCInstanceVariable(Container, Name, USR, Loc,
+                                  AvailabilitySet(Ivar), Comment, Declaration,
+                                  SubHeading, Access);
     }
   }
 

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index d1bf83c8674f2..0810dec91b49b 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -135,30 +135,42 @@ Object serializeSourceRange(const PresumedLoc &BeginLoc,
 /// Serialize the availability attributes of a symbol.
 ///
 /// Availability information contains the introduced, deprecated, and obsoleted
-/// versions of the symbol as semantic versions, if not default.
-/// Availability information also contains flags to indicate if the symbol is
-/// unconditionally unavailable or deprecated,
-/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)).
+/// versions of the symbol for a given domain (roughly corresponds to a
+/// platform) as semantic versions, if not default.  Availability information
+/// also contains flags to indicate if the symbol is unconditionally unavailable
+/// or deprecated, i.e. \c __attribute__((unavailable)) and \c
+/// __attribute__((deprecated)).
 ///
 /// \returns \c None if the symbol has default availability attributes, or
-/// an \c Object containing the formatted availability information.
-Optional<Object> serializeAvailability(const AvailabilityInfo &Avail) {
-  if (Avail.isDefault())
+/// an \c Array containing the formatted availability information.
+Optional<Array> serializeAvailability(const AvailabilitySet &Availabilities) {
+  if (Availabilities.isDefault())
     return None;
 
-  Object Availability;
-  serializeObject(Availability, "introducedVersion",
-                  serializeSemanticVersion(Avail.Introduced));
-  serializeObject(Availability, "deprecatedVersion",
-                  serializeSemanticVersion(Avail.Deprecated));
-  serializeObject(Availability, "obsoletedVersion",
-                  serializeSemanticVersion(Avail.Obsoleted));
-  if (Avail.isUnavailable())
-    Availability["isUnconditionallyUnavailable"] = true;
-  if (Avail.isUnconditionallyDeprecated())
-    Availability["isUnconditionallyDeprecated"] = true;
+  Array AvailabilityArray;
 
-  return Availability;
+  if (Availabilities.isUnconditionallyDeprecated()) {
+    Object UnconditionallyDeprecated;
+    UnconditionallyDeprecated["domain"] = "*";
+    UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
+    AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
+  }
+
+  // Note unconditionally unavailable records are skipped.
+
+  for (const auto &AvailInfo : Availabilities) {
+    Object Availability;
+    Availability["domain"] = AvailInfo.Domain;
+    serializeObject(Availability, "introducedVersion",
+                    serializeSemanticVersion(AvailInfo.Introduced));
+    serializeObject(Availability, "deprecatedVersion",
+                    serializeSemanticVersion(AvailInfo.Deprecated));
+    serializeObject(Availability, "obsoletedVersion",
+                    serializeSemanticVersion(AvailInfo.Obsoleted));
+    AvailabilityArray.emplace_back(std::move(Availability));
+  }
+
+  return AvailabilityArray;
 }
 
 /// Get the language name string for interface language references.
@@ -469,7 +481,7 @@ Object SymbolGraphSerializer::serializeModule() const {
 
 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
   // Skip unconditionally unavailable symbols
-  if (Record.Availability.isUnconditionallyUnavailable())
+  if (Record.Availabilities.isUnconditionallyUnavailable())
     return true;
 
   // Filter out symbols prefixed with an underscored as they are understood to
@@ -494,8 +506,8 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
   serializeObject(
       Obj, "location",
       serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
-  serializeObject(Obj, "availability",
-                  serializeAvailability(Record.Availability));
+  serializeArray(Obj, "availability",
+                 serializeAvailability(Record.Availabilities));
   serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
   serializeArray(Obj, "declarationFragments",
                  serializeDeclarationFragments(Record.Declaration));

diff  --git a/clang/test/ExtractAPI/availability.c b/clang/test/ExtractAPI/availability.c
new file mode 100644
index 0000000000000..54dbf5a6cac95
--- /dev/null
+++ b/clang/test/ExtractAPI/availability.c
@@ -0,0 +1,459 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api --product-name=Availability -triple arm64-apple-macosx -x c-header %t/input.h -o %t/output.json -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+void a(void);
+
+void b(void) __attribute__((availability(macos, introduced=12.0)));
+
+void c(void) __attribute__((availability(macos, introduced=11.0, deprecated=12.0, obsoleted=20.0)));
+
+void d(void) __attribute__((availability(macos, introduced=11.0, deprecated=12.0, obsoleted=20.0))) __attribute__((availability(ios, introduced=13.0)));
+
+void e(void) __attribute__((deprecated)) __attribute__((availability(macos, introduced=11.0)));
+
+void f(void) __attribute__((unavailable)) __attribute__((availability(macos, introduced=11.0)));
+
+void d(void) __attribute__((availability(tvos, introduced=15.0)));
+///expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "Availability",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "a"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@F at a"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "a"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "a"
+          }
+        ],
+        "title": "a"
+      },
+      "pathComponents": [
+        "a"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "availability": [
+        {
+          "domain": "macos",
+          "introducedVersion": {
+            "major": 12,
+            "minor": 0,
+            "patch": 0
+          }
+        }
+      ],
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "b"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@F at b"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "b"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "b"
+          }
+        ],
+        "title": "b"
+      },
+      "pathComponents": [
+        "b"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "availability": [
+        {
+          "deprecatedVersion": {
+            "major": 12,
+            "minor": 0,
+            "patch": 0
+          },
+          "domain": "macos",
+          "introducedVersion": {
+            "major": 11,
+            "minor": 0,
+            "patch": 0
+          },
+          "obsoletedVersion": {
+            "major": 20,
+            "minor": 0,
+            "patch": 0
+          }
+        }
+      ],
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "c"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@F at c"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "c"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "c"
+          }
+        ],
+        "title": "c"
+      },
+      "pathComponents": [
+        "c"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "availability": [
+        {
+          "deprecatedVersion": {
+            "major": 12,
+            "minor": 0,
+            "patch": 0
+          },
+          "domain": "macos",
+          "introducedVersion": {
+            "major": 11,
+            "minor": 0,
+            "patch": 0
+          },
+          "obsoletedVersion": {
+            "major": 20,
+            "minor": 0,
+            "patch": 0
+          }
+        },
+        {
+          "domain": "ios",
+          "introducedVersion": {
+            "major": 13,
+            "minor": 0,
+            "patch": 0
+          }
+        },
+        {
+          "domain": "tvos",
+          "introducedVersion": {
+            "major": 15,
+            "minor": 0,
+            "patch": 0
+          }
+        }
+      ],
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "d"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@F at d"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "d"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "d"
+          }
+        ],
+        "title": "d"
+      },
+      "pathComponents": [
+        "d"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "availability": [
+        {
+          "domain": "*",
+          "isUnconditionallyDeprecated": true
+        },
+        {
+          "domain": "macos",
+          "introducedVersion": {
+            "major": 11,
+            "minor": 0,
+            "patch": 0
+          }
+        }
+      ],
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "e"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@F at e"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "e"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "e"
+          }
+        ],
+        "title": "e"
+      },
+      "pathComponents": [
+        "e"
+      ]
+    }
+  ]
+}


        


More information about the cfe-commits mailing list