mirror of https://github.com/microsoft/clang.git
Implement support for [[nodiscard]] in C++1z that is based off existing support for warn_unused_result, and treat it as an extension pre-C++1z. This also means extending the existing warn_unused_result attribute so that it can be placed on an enum as well as a class.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262872 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
45b0585fa2
commit
3827f56644
|
@ -2025,12 +2025,16 @@ public:
|
|||
return getType()->getAs<FunctionType>()->getCallResultType(getASTContext());
|
||||
}
|
||||
|
||||
/// \brief Returns the WarnUnusedResultAttr that is either declared on this
|
||||
/// function, or its return type declaration.
|
||||
const Attr *getUnusedResultAttr() const;
|
||||
|
||||
/// \brief Returns true if this function or its return type has the
|
||||
/// warn_unused_result attribute. If the return type has the attribute and
|
||||
/// this function is a method of the return type's class, then false will be
|
||||
/// returned to avoid spurious warnings on member methods such as assignment
|
||||
/// operators.
|
||||
bool hasUnusedResultAttr() const;
|
||||
bool hasUnusedResultAttr() const { return getUnusedResultAttr() != nullptr; }
|
||||
|
||||
/// \brief Returns the storage class as written in the source. For the
|
||||
/// computed linkage of symbol, see getLinkage.
|
||||
|
|
|
@ -1537,11 +1537,12 @@ def WarnUnused : InheritableAttr {
|
|||
}
|
||||
|
||||
def WarnUnusedResult : InheritableAttr {
|
||||
let Spellings = [GCC<"warn_unused_result">,
|
||||
CXX11<"clang", "warn_unused_result">];
|
||||
let Subjects = SubjectList<[ObjCMethod, CXXRecord, FunctionLike], WarnDiag,
|
||||
"ExpectedFunctionMethodOrClass">;
|
||||
let Documentation = [Undocumented];
|
||||
let Spellings = [CXX11<"", "nodiscard", 201603>,
|
||||
CXX11<"clang", "warn_unused_result">,
|
||||
GCC<"warn_unused_result">];
|
||||
let Subjects = SubjectList<[ObjCMethod, Enum, CXXRecord, FunctionLike],
|
||||
WarnDiag, "ExpectedFunctionMethodEnumOrClass">;
|
||||
let Documentation = [WarnUnusedResultsDocs];
|
||||
}
|
||||
|
||||
def Weak : InheritableAttr {
|
||||
|
|
|
@ -729,6 +729,31 @@ When one method overrides another, the overriding method can be more widely avai
|
|||
}];
|
||||
}
|
||||
|
||||
def WarnUnusedResultsDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Heading = "nodiscard, warn_unused_result, clang::warn_unused_result, gnu::warn_unused_result";
|
||||
let Content = [{
|
||||
Clang supports the ability to diagnose when the results of a function call
|
||||
expression are discarded under suspicious circumstances. A diagnostic is
|
||||
generated when a function or its return type is marked with ``[[nodiscard]]``
|
||||
(or ``__attribute__((warn_unused_result))``) and the function call appears as a
|
||||
potentially-evaluated discarded-value expression that is not explicitly cast to
|
||||
`void`.
|
||||
|
||||
.. code-block: c++
|
||||
struct [[nodiscard]] error_info { /*...*/ };
|
||||
error_info enable_missile_safety_mode();
|
||||
|
||||
void launch_missiles();
|
||||
void test_missiles() {
|
||||
enable_missile_safety_mode(); // diagnoses
|
||||
launch_missiles();
|
||||
}
|
||||
error_info &foo();
|
||||
void f() { foo(); } // Does not diagnose, error_info is a reference.
|
||||
}];
|
||||
}
|
||||
|
||||
def FallthroughDocs : Documentation {
|
||||
let Category = DocCatStmt;
|
||||
let Content = [{
|
||||
|
|
|
@ -2475,7 +2475,7 @@ def warn_attribute_wrong_decl_type : Warning<
|
|||
"variables, functions and classes|Objective-C protocols|"
|
||||
"functions and global variables|structs, unions, and typedefs|structs and typedefs|"
|
||||
"interface or protocol declarations|kernel functions|non-K&R-style functions|"
|
||||
"variables, enums, fields and typedefs}1">,
|
||||
"variables, enums, fields and typedefs|functions, methods, enums, and classes}1">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>;
|
||||
def warn_type_attribute_wrong_type : Warning<
|
||||
|
@ -6587,11 +6587,13 @@ def warn_side_effects_typeid : Warning<
|
|||
"expression with side effects will be evaluated despite being used as an "
|
||||
"operand to 'typeid'">, InGroup<PotentiallyEvaluatedExpression>;
|
||||
def warn_unused_result : Warning<
|
||||
"ignoring return value of function declared with warn_unused_result "
|
||||
"attribute">, InGroup<DiagGroup<"unused-result">>;
|
||||
"ignoring return value of function declared with %0 attribute">,
|
||||
InGroup<DiagGroup<"unused-result">>;
|
||||
def warn_unused_volatile : Warning<
|
||||
"expression result unused; assign into a variable to force a volatile load">,
|
||||
InGroup<DiagGroup<"unused-volatile-lvalue">>;
|
||||
def ext_nodiscard_attr_is_a_cxx1z_extension : ExtWarn<
|
||||
"use of the 'nodiscard' attribute is a C++1z extension">, InGroup<CXX1z>;
|
||||
|
||||
def warn_unused_comparison : Warning<
|
||||
"%select{%select{|in}1equality|relational}0 comparison result unused">,
|
||||
|
|
|
@ -893,7 +893,8 @@ enum AttributeDeclKind {
|
|||
ExpectedObjectiveCInterfaceOrProtocol,
|
||||
ExpectedKernelFunction,
|
||||
ExpectedFunctionWithProtoType,
|
||||
ExpectedVariableEnumFieldOrTypedef
|
||||
ExpectedVariableEnumFieldOrTypedef,
|
||||
ExpectedFunctionMethodEnumOrClass
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
@ -2935,16 +2935,22 @@ SourceRange FunctionDecl::getReturnTypeSourceRange() const {
|
|||
return RTRange;
|
||||
}
|
||||
|
||||
bool FunctionDecl::hasUnusedResultAttr() const {
|
||||
const Attr *FunctionDecl::getUnusedResultAttr() const {
|
||||
QualType RetType = getReturnType();
|
||||
if (RetType->isRecordType()) {
|
||||
const CXXRecordDecl *Ret = RetType->getAsCXXRecordDecl();
|
||||
const auto *MD = dyn_cast<CXXMethodDecl>(this);
|
||||
if (Ret && Ret->hasAttr<WarnUnusedResultAttr>() &&
|
||||
!(MD && MD->getCorrespondingMethodInClass(Ret, true)))
|
||||
return true;
|
||||
if (Ret && !(MD && MD->getCorrespondingMethodInClass(Ret, true))) {
|
||||
if (const auto *R = Ret->getAttr<WarnUnusedResultAttr>())
|
||||
return R;
|
||||
}
|
||||
} else if (const auto *ET = RetType->getAs<EnumType>()) {
|
||||
if (const EnumDecl *ED = ET->getDecl()) {
|
||||
if (const auto *R = ED->getAttr<WarnUnusedResultAttr>())
|
||||
return R;
|
||||
}
|
||||
}
|
||||
return hasAttr<WarnUnusedResultAttr>();
|
||||
return getAttr<WarnUnusedResultAttr>();
|
||||
}
|
||||
|
||||
/// \brief For an inline function definition in C, or for a gnu_inline function
|
||||
|
|
|
@ -3635,7 +3635,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
|
|||
case AttributeList::AT_FallThrough:
|
||||
case AttributeList::AT_CXX11NoReturn:
|
||||
return true;
|
||||
|
||||
case AttributeList::AT_WarnUnusedResult:
|
||||
return !ScopeName && AttrName->getName().equals("nodiscard");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -2463,6 +2463,12 @@ static void handleWarnUnusedResult(Sema &S, Decl *D, const AttributeList &Attr)
|
|||
return;
|
||||
}
|
||||
|
||||
// If this is spelled as the standard C++1z attribute, but not in C++1z, warn
|
||||
// about using it as an extension.
|
||||
if (!S.getLangOpts().CPlusPlus1z && Attr.isCXX11Attribute() &&
|
||||
!Attr.getScopeName())
|
||||
S.Diag(Attr.getLoc(), diag::ext_nodiscard_attr_is_a_cxx1z_extension);
|
||||
|
||||
D->addAttr(::new (S.Context)
|
||||
WarnUnusedResultAttr(Attr.getRange(), S.Context,
|
||||
Attr.getAttributeSpellingListIndex()));
|
||||
|
|
|
@ -249,10 +249,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
|
|||
// is written in a macro body, only warn if it has the warn_unused_result
|
||||
// attribute.
|
||||
if (const Decl *FD = CE->getCalleeDecl()) {
|
||||
const FunctionDecl *Func = dyn_cast<FunctionDecl>(FD);
|
||||
if (Func ? Func->hasUnusedResultAttr()
|
||||
: FD->hasAttr<WarnUnusedResultAttr>()) {
|
||||
Diag(Loc, diag::warn_unused_result) << R1 << R2;
|
||||
if (const Attr *A = isa<FunctionDecl>(FD)
|
||||
? cast<FunctionDecl>(FD)->getUnusedResultAttr()
|
||||
: FD->getAttr<WarnUnusedResultAttr>()) {
|
||||
Diag(Loc, diag::warn_unused_result) << A << R1 << R2;
|
||||
return;
|
||||
}
|
||||
if (ShouldSuppress)
|
||||
|
@ -276,8 +276,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
|
|||
}
|
||||
const ObjCMethodDecl *MD = ME->getMethodDecl();
|
||||
if (MD) {
|
||||
if (MD->hasAttr<WarnUnusedResultAttr>()) {
|
||||
Diag(Loc, diag::warn_unused_result) << R1 << R2;
|
||||
if (const auto *A = MD->getAttr<WarnUnusedResultAttr>()) {
|
||||
Diag(Loc, diag::warn_unused_result) << A << R1 << R2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ void t4(int a) {
|
|||
// rdar://7186119
|
||||
int t5f(void) __attribute__((warn_unused_result));
|
||||
void t5() {
|
||||
t5f(); // expected-warning {{ignoring return value of function declared with warn_unused_result}}
|
||||
t5f(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}}
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,11 +88,11 @@ int t6() {
|
|||
if (fn1() < 0 || fn2(2,1) < 0 || fn3(2) < 0) // no warnings
|
||||
return -1;
|
||||
|
||||
fn1(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}}
|
||||
fn1(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}}
|
||||
fn2(92, 21); // expected-warning {{ignoring return value of function declared with pure attribute}}
|
||||
fn3(42); // expected-warning {{ignoring return value of function declared with const attribute}}
|
||||
__builtin_abs(0); // expected-warning {{ignoring return value of function declared with const attribute}}
|
||||
(void)0, fn1(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}}
|
||||
(void)0, fn1(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ int t7 __attribute__ ((warn_unused_result)); // expected-warning {{'warn_unused_
|
|||
// PR4010
|
||||
int (*fn4)(void) __attribute__ ((warn_unused_result));
|
||||
void t8() {
|
||||
fn4(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}}
|
||||
fn4(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}}
|
||||
}
|
||||
|
||||
void t9() __attribute__((warn_unused_result)); // expected-warning {{attribute 'warn_unused_result' cannot be applied to functions without return value}}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -std=c++1z -verify %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -DEXT %s
|
||||
|
||||
#if !defined(EXT)
|
||||
static_assert(__has_cpp_attribute(nodiscard) == 201603);
|
||||
|
||||
struct [[nodiscard]] S {};
|
||||
S get_s();
|
||||
S& get_s_ref();
|
||||
|
||||
enum [[nodiscard]] E {};
|
||||
E get_e();
|
||||
|
||||
[[nodiscard]] int get_i();
|
||||
|
||||
void f() {
|
||||
get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
|
||||
// Okay, warnings are not encouraged
|
||||
get_s_ref();
|
||||
(void)get_s();
|
||||
(void)get_i();
|
||||
(void)get_e();
|
||||
}
|
||||
|
||||
[[nodiscard nodiscard]] int wrong1(); // expected-error {{attribute 'nodiscard' cannot appear multiple times in an attribute specifier}}
|
||||
|
||||
namespace [[nodiscard]] N {} // expected-warning {{'nodiscard' attribute only applies to functions, methods, enums, and classes}}
|
||||
#else
|
||||
struct [[nodiscard]] S {}; // expected-warning {{use of the 'nodiscard' attribute is a C++1z extension}}
|
||||
#endif // EXT
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
void foo(INTF *a) {
|
||||
[a garf];
|
||||
[a fee]; // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}}
|
||||
[INTF c]; // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}}
|
||||
[a fee]; // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}}
|
||||
[INTF c]; // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -634,7 +634,7 @@ as the draft C++1z standard evolves.</p>
|
|||
<tr>
|
||||
<td><tt>[[nodiscard]]</tt> attribute</td>
|
||||
<td><a href="http://wg21.link/p0189r1">P0189R1</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="full" align="center">SVN</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><tt>[[maybe_unused]]</tt> attribute</td>
|
||||
|
|
Loading…
Reference in New Issue