Split mergeAvailabilityAttr out of handleAvailabilityAttr. This is important
for having a uniform logic for adding attributes to a decl. This in turn
is needed to fix the FIXME:

  // FIXME: This needs to happen before we merge declarations. Then,
  // let attribute merging cope with attribute conflicts.
  ProcessDeclAttributes(S, NewFD, D,
                        /*NonInheritable=*/false, /*Inheritable=*/true);

The idea is that mergeAvailabilityAttr will become a method. Once attributes
are processed before merging, it will be called from handleAvailabilityAttr to
handle multiple attributes in one decl:

void f(int) __attribute__((availability(ios,deprecated=3.0),
                           availability(ios,introduced=2.0)));

and from SemaDecl.cpp to handle multiple decls:

void f(int) __attribute__((availability(ios,deprecated=3.0)));
void f(int) __attribute__((availability(ios,introduced=2.0)));

As a bonus, use the new structure to diagnose incompatible availability
attributes added to different decls (see included testcases).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156269 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 7680ae1..e7960e9 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1672,6 +1672,8 @@
   "feature cannot be %select{introduced|deprecated|obsoleted}0 in %1 version "
   "%2 before it was %select{introduced|deprecated|obsoleted}3 in version %4; "
   "attribute ignored">, InGroup<Availability>;
+def warn_mismatched_availability: Warning<
+  "availability does not match previous declaration">, InGroup<Availability>;
 
 // Thread Safety Attributes
 def warn_thread_attribute_ignored : Warning<
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index d1cc73c..500de92 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1633,6 +1633,13 @@
 /// attribute.
 static bool
 DeclHasAttr(const Decl *D, const Attr *A) {
+  // There can be multiple AvailabilityAttr in a Decl. Make sure we copy
+  // all of them. It is mergeAvailabilityAttr in SemaDeclAttr.cpp that is
+  // responsible for making sure they are consistent.
+  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(A);
+  if (AA)
+    return false;
+
   const OwnershipAttr *OA = dyn_cast<OwnershipAttr>(A);
   const AnnotateAttr *Ann = dyn_cast<AnnotateAttr>(A);
   for (Decl::attr_iterator i = D->attr_begin(), e = D->attr_end(); i != e; ++i)
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index af3fabb..c0bc369 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -1690,64 +1690,143 @@
                                  Attr.getRange(), S.Context));
 }
 
+bool checkAvailabilityAttr(Sema &S, SourceRange Range,
+                           IdentifierInfo *Platform,
+                           VersionTuple Introduced,
+                           VersionTuple Deprecated,
+                           VersionTuple Obsoleted) {
+  StringRef PlatformName
+    = AvailabilityAttr::getPrettyPlatformName(Platform->getName());
+  if (PlatformName.empty())
+    PlatformName = Platform->getName();
+
+  // Ensure that Introduced <= Deprecated <= Obsoleted (although not all
+  // of these steps are needed).
+  if (!Introduced.empty() && !Deprecated.empty() &&
+      !(Introduced <= Deprecated)) {
+    S.Diag(Range.getBegin(), diag::warn_availability_version_ordering)
+      << 1 << PlatformName << Deprecated.getAsString()
+      << 0 << Introduced.getAsString();
+    return true;
+  }
+
+  if (!Introduced.empty() && !Obsoleted.empty() &&
+      !(Introduced <= Obsoleted)) {
+    S.Diag(Range.getBegin(), diag::warn_availability_version_ordering)
+      << 2 << PlatformName << Obsoleted.getAsString()
+      << 0 << Introduced.getAsString();
+    return true;
+  }
+
+  if (!Deprecated.empty() && !Obsoleted.empty() &&
+      !(Deprecated <= Obsoleted)) {
+    S.Diag(Range.getBegin(), diag::warn_availability_version_ordering)
+      << 2 << PlatformName << Obsoleted.getAsString()
+      << 1 << Deprecated.getAsString();
+    return true;
+  }
+
+  return false;
+}
+
+static void mergeAvailabilityAttr(Sema &S, Decl *D, SourceRange Range,
+                                  IdentifierInfo *Platform,
+                                  VersionTuple Introduced,
+                                  VersionTuple Deprecated,
+                                  VersionTuple Obsoleted,
+                                  bool IsUnavailable,
+                                  StringRef Message) {
+  VersionTuple MergedIntroduced;
+  VersionTuple MergedDeprecated;
+  VersionTuple MergedObsoleted;
+  bool FoundAny = false;
+
+  for (specific_attr_iterator<AvailabilityAttr>
+         i = D->specific_attr_begin<AvailabilityAttr>(),
+         e = D->specific_attr_end<AvailabilityAttr>();
+       i != e ; ++i) {
+    const AvailabilityAttr *OldAA = *i;
+    IdentifierInfo *OldPlatform = OldAA->getPlatform();
+    if (OldPlatform != Platform)
+      continue;
+    FoundAny = true;
+    VersionTuple OldIntroduced = OldAA->getIntroduced();
+    VersionTuple OldDeprecated = OldAA->getDeprecated();
+    VersionTuple OldObsoleted = OldAA->getObsoleted();
+    bool OldIsUnavailable = OldAA->getUnavailable();
+    StringRef OldMessage = OldAA->getMessage();
+
+    if ((!OldIntroduced.empty() && !Introduced.empty() &&
+         OldIntroduced != Introduced) ||
+        (!OldDeprecated.empty() && !Deprecated.empty() &&
+         OldDeprecated != Deprecated) ||
+        (!OldObsoleted.empty() && !Obsoleted.empty() &&
+         OldObsoleted != Obsoleted) ||
+        (OldIsUnavailable != IsUnavailable) ||
+        (OldMessage != Message)) {
+      S.Diag(Range.getBegin(), diag::warn_mismatched_availability);
+      S.Diag(OldAA->getLocation(), diag::note_previous_attribute);
+      return;
+    }
+    if (MergedIntroduced.empty())
+      MergedIntroduced = OldIntroduced;
+    if (MergedDeprecated.empty())
+      MergedDeprecated = OldDeprecated;
+    if (MergedObsoleted.empty())
+      MergedObsoleted = OldObsoleted;
+  }
+
+  if (FoundAny &&
+      MergedIntroduced == Introduced &&
+      MergedDeprecated == Deprecated &&
+      MergedObsoleted == Obsoleted)
+    return;
+
+  if (MergedIntroduced.empty())
+    MergedIntroduced = Introduced;
+  if (MergedDeprecated.empty())
+    MergedDeprecated = Deprecated;
+  if (MergedObsoleted.empty())
+    MergedObsoleted = Obsoleted;
+
+  if (!checkAvailabilityAttr(S, Range, Platform, MergedIntroduced,
+                             MergedDeprecated, MergedObsoleted)) {
+    D->addAttr(::new (S.Context) AvailabilityAttr(Range, S.Context,
+                                                  Platform,
+                                                  Introduced,
+                                                  Deprecated,
+                                                  Obsoleted,
+                                                  IsUnavailable,
+                                                  Message));
+  }
+}
+
 static void handleAvailabilityAttr(Sema &S, Decl *D,
                                    const AttributeList &Attr) {
   IdentifierInfo *Platform = Attr.getParameterName();
   SourceLocation PlatformLoc = Attr.getParameterLoc();
 
-  StringRef PlatformName
-    = AvailabilityAttr::getPrettyPlatformName(Platform->getName());
-  if (PlatformName.empty()) {
+  if (AvailabilityAttr::getPrettyPlatformName(Platform->getName()).empty())
     S.Diag(PlatformLoc, diag::warn_availability_unknown_platform)
       << Platform;
 
-    PlatformName = Platform->getName();
-  }
-
   AvailabilityChange Introduced = Attr.getAvailabilityIntroduced();
   AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated();
   AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted();
   bool IsUnavailable = Attr.getUnavailableLoc().isValid();
-
-  // Ensure that Introduced <= Deprecated <= Obsoleted (although not all
-  // of these steps are needed).
-  if (Introduced.isValid() && Deprecated.isValid() &&
-      !(Introduced.Version <= Deprecated.Version)) {
-    S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
-      << 1 << PlatformName << Deprecated.Version.getAsString()
-      << 0 << Introduced.Version.getAsString();
-    return;
-  }
-
-  if (Introduced.isValid() && Obsoleted.isValid() &&
-      !(Introduced.Version <= Obsoleted.Version)) {
-    S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
-      << 2 << PlatformName << Obsoleted.Version.getAsString()
-      << 0 << Introduced.Version.getAsString();
-    return;
-  }
-
-  if (Deprecated.isValid() && Obsoleted.isValid() &&
-      !(Deprecated.Version <= Obsoleted.Version)) {
-    S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering)
-      << 2 << PlatformName << Obsoleted.Version.getAsString()
-      << 1 << Deprecated.Version.getAsString();
-    return;
-  }
-
   StringRef Str;
   const StringLiteral *SE = 
     dyn_cast_or_null<const StringLiteral>(Attr.getMessageExpr());
   if (SE)
     Str = SE->getString();
-  
-  D->addAttr(::new (S.Context) AvailabilityAttr(Attr.getRange(), S.Context,
-                                                Platform,
-                                                Introduced.Version,
-                                                Deprecated.Version,
-                                                Obsoleted.Version,
-                                                IsUnavailable, 
-                                                Str));
+
+  mergeAvailabilityAttr(S, D, Attr.getRange(),
+                        Platform,
+                        Introduced.Version,
+                        Deprecated.Version,
+                        Obsoleted.Version,
+                        IsUnavailable,
+                        Str);
 }
 
 static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
diff --git a/test/Sema/attr-availability.c b/test/Sema/attr-availability.c
index 5fb63cd..89252a6 100644
--- a/test/Sema/attr-availability.c
+++ b/test/Sema/attr-availability.c
@@ -24,3 +24,16 @@
     NSDataWritingFileProtectionWriteOnly = 0x30000000,
     NSDataWritingFileProtectionCompleteUntilUserAuthentication = 0x40000000,
 };
+
+void f4(int) __attribute__((availability(ios,deprecated=3.0)));
+void f4(int) __attribute__((availability(ios,introduced=4.0))); // expected-warning {{feature cannot be deprecated in iOS version 3.0 before it was introduced in version 4.0; attribute ignored}}
+
+void f5(int) __attribute__((availability(ios,deprecated=3.0),  // expected-warning {{feature cannot be deprecated in iOS version 3.0 before it was introduced in version 4.0; attribute ignored}}
+                            availability(ios,introduced=4.0)));
+
+void f6(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{previous attribute is here}}
+void f6(int) __attribute__((availability(ios,deprecated=4.0))); // expected-warning {{availability does not match previous declaration}}
+
+void f7(int) __attribute__((availability(ios,introduced=2.0)));
+void f7(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{previous attribute is here}}
+void f7(int) __attribute__((availability(ios,deprecated=4.0))); // expected-warning {{availability does not match previous declaration}}