Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -722,8 +722,11 @@ 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 { Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -2239,3 +2239,24 @@ }]; } +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. + }]; +} Index: cfe/trunk/lib/Lex/PPMacroExpansion.cpp =================================================================== --- cfe/trunk/lib/Lex/PPMacroExpansion.cpp +++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp @@ -1079,6 +1079,7 @@ .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) Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -5136,12 +5136,27 @@ } } + // 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(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 @@ break; } + CharSourceRange UseRange; + StringRef Replacement; + if (K == Sema::AD_Deprecation) { + if (auto attr = D->getAttr()) + 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); } Index: cfe/trunk/test/SemaCXX/attr-deprecated-replacement-error.cpp =================================================================== --- cfe/trunk/test/SemaCXX/attr-deprecated-replacement-error.cpp +++ cfe/trunk/test/SemaCXX/attr-deprecated-replacement-error.cpp @@ -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}} Index: cfe/trunk/test/SemaCXX/attr-deprecated-replacement-fixit.cpp =================================================================== --- cfe/trunk/test/SemaCXX/attr-deprecated-replacement-fixit.cpp +++ cfe/trunk/test/SemaCXX/attr-deprecated-replacement-fixit.cpp @@ -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" +} Index: cfe/trunk/test/SemaCXX/cxx11-attr-print.cpp =================================================================== --- cfe/trunk/test/SemaCXX/cxx11-attr-print.cpp +++ cfe/trunk/test/SemaCXX/cxx11-attr-print.cpp @@ -16,6 +16,15 @@ // 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; Index: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp +++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp @@ -1111,6 +1111,15 @@ << " 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 Spellings = GetFlattenedSpellings(R); @@ -1224,6 +1233,8 @@ 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) {