Add an optional string argument to DeprecatedAttr for Fix-It.

We only add this to __attribute__((deprecated)).

Differential Revision: http://reviews.llvm.org/D17865

llvm-svn: 263652
This commit is contained in:
Manman Ren 2016-03-16 18:50:49 +00:00
parent 8a46c067ed
commit c7890fed01
8 changed files with 116 additions and 6 deletions

View File

@ -722,8 +722,11 @@ def OpenCLGenericAddressSpace : TypeAttr {
def Deprecated : InheritableAttr {
let Spellings = [GCC<"deprecated">, Declspec<"deprecated">,
CXX11<"","deprecated", 201309>];
let Args = [StringArgument<"Message", 1>];
let Documentation = [Undocumented];
let Args = [StringArgument<"Message", 1>,
// An optional string argument that enables us to provide a
// Fix-It.
StringArgument<"Replacement", 1>];
let Documentation = [DeprecatedDocs];
}
def Destructor : InheritableAttr {

View File

@ -2239,3 +2239,24 @@ experimental at this time.
}];
}
def DeprecatedDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
The ``deprecated`` attribute can be applied to a function, a variable, or a
type. This is useful when identifying functions, variables, or types that are
expected to be removed in a future version of a program.
Consider the function declaration for a hypothetical function ``f``:
.. code-block:: c++
void f(void) __attribute__((deprecated("message", "replacement")));
When spelled as `__attribute__((deprecated))`, the deprecated attribute can have
two optional string arguments. The first one is the message to display when
emitting the warning; the second one enables the compiler to provide a Fix-It
to replace the deprecated name with a new name. Otherwise, when spelled as
`[[gnu::deprecated]] or [[deprecated]]`, the attribute can have one optional
string argument which is the message to display when emitting the warning.
}];
}

View File

@ -1079,6 +1079,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("attribute_cf_returns_retained", true)
.Case("attribute_cf_returns_on_parameters", true)
.Case("attribute_deprecated_with_message", true)
.Case("attribute_deprecated_with_replacement", true)
.Case("attribute_ext_vector_type", true)
.Case("attribute_ns_returns_not_retained", true)
.Case("attribute_ns_returns_retained", true)

View File

@ -5136,12 +5136,27 @@ static void handleDeprecatedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}
}
// Handle the cases where the attribute has a text message.
StringRef Str, Replacement;
if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0) &&
!S.checkStringLiteralArgumentAttr(Attr, 0, Str))
return;
// Only support a single optional message for Declspec and CXX11.
if (Attr.isDeclspecAttribute() || Attr.isCXX11Attribute())
checkAttributeAtMostNumArgs(S, Attr, 1);
else if (Attr.isArgExpr(1) && Attr.getArgAsExpr(1) &&
!S.checkStringLiteralArgumentAttr(Attr, 1, Replacement))
return;
if (!S.getLangOpts().CPlusPlus14)
if (Attr.isCXX11Attribute() &&
!(Attr.hasScope() && Attr.getScopeName()->isStr("gnu")))
S.Diag(Attr.getLoc(), diag::ext_cxx14_attr) << Attr.getName();
handleAttrWithMessage<DeprecatedAttr>(S, D, Attr);
D->addAttr(::new (S.Context) DeprecatedAttr(Attr.getRange(), S.Context, Str,
Replacement,
Attr.getAttributeSpellingListIndex()));
}
static void handleNoSanitizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
@ -6209,18 +6224,35 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K,
break;
}
CharSourceRange UseRange;
StringRef Replacement;
if (K == Sema::AD_Deprecation) {
if (auto attr = D->getAttr<DeprecatedAttr>())
Replacement = attr->getReplacement();
if (!Replacement.empty())
UseRange =
CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
}
if (!Message.empty()) {
S.Diag(Loc, diag_message) << D << Message;
S.Diag(Loc, diag_message) << D << Message
<< (UseRange.isValid() ?
FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint());
if (ObjCProperty)
S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
<< ObjCProperty->getDeclName() << property_note_select;
} else if (!UnknownObjCClass) {
S.Diag(Loc, diag) << D;
S.Diag(Loc, diag) << D
<< (UseRange.isValid() ?
FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint());
if (ObjCProperty)
S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
<< ObjCProperty->getDeclName() << property_note_select;
} else {
S.Diag(Loc, diag_fwdclass_message) << D;
S.Diag(Loc, diag_fwdclass_message) << D
<< (UseRange.isValid() ?
FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint());
S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
}

View File

@ -0,0 +1,17 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -verify -fsyntax-only -std=c++11 -fms-extensions %s
#if !__has_feature(attribute_deprecated_with_replacement)
#error "Missing __has_feature"
#endif
int a1 [[deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}}
int a2 [[deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}}
int b1 [[gnu::deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}}
int b2 [[gnu::deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}}
__declspec(deprecated("warning", "fixit")) int c1; // expected-error{{'deprecated' attribute takes no more than 1 argument}}
__declspec(deprecated("warning", 1)) int c2; // expected-error{{'deprecated' attribute takes no more than 1 argument}}
int d1 __attribute__((deprecated("warning", "fixit")));
int d2 __attribute__((deprecated("warning", 1))); // expected-error{{'deprecated' attribute requires a string}}

View File

@ -0,0 +1,16 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -verify -fsyntax-only %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: cp %s %t
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fixit %t
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -Werror %t
#if !__has_feature(attribute_deprecated_with_replacement)
#error "Missing __has_feature"
#endif
void f_8(int) __attribute__((deprecated("message", "new8"))); // expected-note {{'f_8' has been explicitly marked deprecated here}}
void new8(int);
void test() {
f_8(0); // expected-warning{{'f_8' is deprecated}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:6}:"new8"
}

View File

@ -16,6 +16,15 @@ int a __attribute__((deprecated("warning")));
// CHECK: int b {{\[}}[gnu::deprecated("warning")]];
int b [[gnu::deprecated("warning")]];
// CHECK: __declspec(deprecated("warning"))
__declspec(deprecated("warning")) int c;
// CHECK: int d {{\[}}[deprecated("warning")]];
int d [[deprecated("warning")]];
// CHECK: __attribute__((deprecated("warning", "fixit")));
int e __attribute__((deprecated("warning", "fixit")));
// CHECK: int cxx11_alignas alignas(4);
alignas(4) int cxx11_alignas;

View File

@ -1111,6 +1111,15 @@ static void writeAvailabilityValue(raw_ostream &OS) {
<< " OS << \"";
}
static void writeDeprecatedAttrValue(raw_ostream &OS, std::string &Variety) {
OS << "\\\"\" << getMessage() << \"\\\"\";\n";
// Only GNU deprecated has an optional fixit argument at the second position.
if (Variety == "GNU")
OS << " if (!getReplacement().empty()) OS << \", \\\"\""
" << getReplacement() << \"\\\"\";\n";
OS << " OS << \"";
}
static void writeGetSpellingFunction(Record &R, raw_ostream &OS) {
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
@ -1224,6 +1233,8 @@ writePrettyPrintFunction(Record &R,
OS << "(";
if (Spelling == "availability") {
writeAvailabilityValue(OS);
} else if (Spelling == "deprecated" || Spelling == "gnu::deprecated") {
writeDeprecatedAttrValue(OS, Variety);
} else {
unsigned index = 0;
for (const auto &arg : Args) {