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).

llvm-svn: 156269
This commit is contained in:
Rafael Espindola 2012-05-06 19:56:25 +00:00
parent 00a1e6d48b
commit 2d243bfe2f
4 changed files with 142 additions and 41 deletions

View File

@ -1672,6 +1672,8 @@ def warn_availability_version_ordering : Warning<
"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<

View File

@ -1633,6 +1633,13 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) {
/// 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)

View File

@ -1690,64 +1690,143 @@ static void handleObjCRequiresPropertyDefsAttr(Sema &S, Decl *D,
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) {

View File

@ -24,3 +24,16 @@ enum {
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}}