]> granicus.if.org Git - clang/commitdiff
Properly implement warn_unused_result checking for classes/structs.
authorKaelyn Takata <rikka@google.com>
Thu, 9 Apr 2015 19:43:04 +0000 (19:43 +0000)
committerKaelyn Takata <rikka@google.com>
Thu, 9 Apr 2015 19:43:04 +0000 (19:43 +0000)
The previous implementation would copy the attribute from the class to
functions that have the class as their return type when the functions
are first declared. This proved to have two flaws:
  1) if the class is forward-declared without the attribute and a
     function or method with the class as a its return type is declared,
     and afterward the class is defined with warn_unused_result, the
     function or method would never inherit the attribute, and
  2) the check simply failed for functions and methods that are part of
     a template instantiation, regardless of whether the class with
     warn_unused_result is part of a specific instantiation or part of
     the template itself (presumably because those function/method
     declaration does not hit the same code path as a non-template one
     and so never inherits the attribute).

The new approach is to instead modify the two places where a function or
method call is checked for the warn_unused_result attribute on the decl
by extending the checks to also look for the attribute on the decl's
return type.

Additionally, the check for return types that have the warn_unused_result
now excludes pointers and references to such types, as such return types do
not necessarily imply a transfer of ownership for the underlying object
being referred to by the return value. This does not change the behavior
of functions that are directly given the warn_unused_result attribute.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@234526 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
lib/AST/Decl.cpp
lib/AST/Expr.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaStmt.cpp
test/SemaCXX/warn-unused-result.cpp

index 3e3d79ff29ef7d983ed3b98fa6cc5bf5cc43de17..f8e17268e6cb842a1e8ede75a088007c8edcafaf 100644 (file)
@@ -1965,6 +1965,13 @@ public:
     return getType()->getAs<FunctionType>()->getCallResultType(getASTContext());
   }
 
+  /// \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;
+
   /// \brief Returns the storage class as written in the source. For the
   /// computed linkage of symbol, see getLinkage.
   StorageClass getStorageClass() const { return StorageClass(SClass); }
index 69e44573dc6299b6f87e75f8b3d467c3f1f55999..2ae73de5074643e01a565a94e29a78cffcba2d8a 100644 (file)
@@ -2822,6 +2822,18 @@ SourceRange FunctionDecl::getReturnTypeSourceRange() const {
   return RTRange;
 }
 
+bool FunctionDecl::hasUnusedResultAttr() const {
+  QualType RetType = getReturnType();
+  if (RetType->isRecordType()) {
+    const CXXRecordDecl *Ret = RetType->getAsCXXRecordDecl();
+    const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(this);
+    if (Ret && Ret->hasAttr<WarnUnusedResultAttr>() &&
+        !(MD && MD->getCorrespondingMethodInClass(Ret, true)))
+      return true;
+  }
+  return hasAttr<WarnUnusedResultAttr>();
+}
+
 /// \brief For an inline function definition in C, or for a gnu_inline function
 /// in C++, determine whether the definition will be externally visible.
 ///
index d4ec2714d59cbcc3e94a97cb786bce510e248a82..76a4da2371b54fa5b5e2d4926abb6866b5a6dfc3 100644 (file)
@@ -2161,12 +2161,16 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
     // If this is a direct call, get the callee.
     const CallExpr *CE = cast<CallExpr>(this);
     if (const Decl *FD = CE->getCalleeDecl()) {
+      const FunctionDecl *Func = dyn_cast<FunctionDecl>(FD);
+      bool HasWarnUnusedResultAttr = Func ? Func->hasUnusedResultAttr()
+                                          : FD->hasAttr<WarnUnusedResultAttr>();
+
       // If the callee has attribute pure, const, or warn_unused_result, warn
       // about it. void foo() { strlen("bar"); } should warn.
       //
       // Note: If new cases are added here, DiagnoseUnusedExprResult should be
       // updated to match for QoI.
-      if (FD->hasAttr<WarnUnusedResultAttr>() ||
+      if (HasWarnUnusedResultAttr ||
           FD->hasAttr<PureAttr>() || FD->hasAttr<ConstAttr>()) {
         WarnE = this;
         Loc = CE->getCallee()->getLocStart();
index ce183d0adf43a4b4f6413aa05d8f20fdc26aa9d1..1bad38f53b816034ea1347ceb791bec46a38d768 100644 (file)
@@ -7489,23 +7489,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   // Handle attributes.
   ProcessDeclAttributes(S, NewFD, D);
 
-  QualType RetType = NewFD->getReturnType();
-  const CXXRecordDecl *Ret = RetType->isRecordType() ?
-      RetType->getAsCXXRecordDecl() : RetType->getPointeeCXXRecordDecl();
-  if (!NewFD->isInvalidDecl() && !NewFD->hasAttr<WarnUnusedResultAttr>() &&
-      Ret && Ret->hasAttr<WarnUnusedResultAttr>()) {
-    const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
-    // Attach WarnUnusedResult to functions returning types with that attribute.
-    // Don't apply the attribute to that type's own non-static member functions
-    // (to avoid warning on things like assignment operators)
-    if (!MD || MD->getParent() != Ret)
-      NewFD->addAttr(WarnUnusedResultAttr::CreateImplicit(Context));
-  }
-
   if (getLangOpts().OpenCL) {
     // OpenCL v1.1 s6.5: Using an address space qualifier in a function return
     // type declaration will generate a compilation error.
-    unsigned AddressSpace = RetType.getAddressSpace();
+    unsigned AddressSpace = NewFD->getReturnType().getAddressSpace();
     if (AddressSpace == LangAS::opencl_local ||
         AddressSpace == LangAS::opencl_global ||
         AddressSpace == LangAS::opencl_constant) {
index 5774f53c060fa72cf2a6d4a783e9d3457a7049c4..8b174f8d922b695f9e2b0feab0ee700a42e97c13 100644 (file)
@@ -239,7 +239,9 @@ 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()) {
-      if (FD->hasAttr<WarnUnusedResultAttr>()) {
+      const FunctionDecl *Func = dyn_cast<FunctionDecl>(FD);
+      if (Func ? Func->hasUnusedResultAttr()
+               : FD->hasAttr<WarnUnusedResultAttr>()) {
         Diag(Loc, diag::warn_unused_result) << R1 << R2;
         return;
       }
index 7bdb4245a95a74a7213fd2a77a6ea1b47996bdcd..01bc457ec2069c27def31b975c2b7776ee141637 100644 (file)
@@ -44,6 +44,12 @@ void bah() {
 }
 
 namespace warn_unused_CXX11 {
+class Status;
+class Foo {
+ public:
+  Status doStuff();
+};
+
 struct [[clang::warn_unused_result]] Status {
   bool ok() const;
   Status& operator=(const Status& x);
@@ -73,10 +79,23 @@ void lazy() {
   (void)DoYetAnotherThing();
 
   DoSomething(); // expected-warning {{ignoring return value}}
-  DoSomethingElse(); // expected-warning {{ignoring return value}}
-  DoAnotherThing(); // expected-warning {{ignoring return value}}
+  DoSomethingElse();
+  DoAnotherThing();
   DoYetAnotherThing();
 }
+
+template <typename T>
+class [[clang::warn_unused_result]] StatusOr {
+};
+StatusOr<int> doit();
+void test() {
+  Foo f;
+  f.doStuff(); // expected-warning {{ignoring return value}}
+  doit(); // expected-warning {{ignoring return value}}
+
+  auto func = []() { return Status(); };
+  func(); // expected-warning {{ignoring return value}}
+}
 }
 
 namespace PR17587 {