khuttun updated this revision to Diff 129090.
khuttun added a comment.

The checker is now disabled inside GNU statement expressions


https://reviews.llvm.org/D41655

Files:
  clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tidy/bugprone/CMakeLists.txt
  clang-tidy/bugprone/UnusedReturnValueCheck.cpp
  clang-tidy/bugprone/UnusedReturnValueCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/bugprone-unused-return-value.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/bugprone-unused-return-value-custom.cpp
  test/clang-tidy/bugprone-unused-return-value.cpp

Index: test/clang-tidy/bugprone-unused-return-value.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/bugprone-unused-return-value.cpp
@@ -0,0 +1,212 @@
+// RUN: %check_clang_tidy %s bugprone-unused-return-value %t
+
+namespace std {
+
+using size_t = decltype(sizeof(int));
+
+using max_align_t = long double;
+
+struct future {};
+
+enum class launch {
+  async,
+  deferred
+};
+
+template <typename Function, typename... Args>
+future async(Function &&, Args &&...);
+
+template <typename Function, typename... Args>
+future async(launch, Function &&, Args &&...);
+
+template <typename T>
+bool empty(const T &);
+
+// the check should be able to match std lib calls even if the functions are
+// declared inside inline namespaces
+inline namespace v1 {
+
+template <typename T>
+T *launder(T *);
+
+} // namespace v1
+
+template <typename T>
+struct allocator {
+  using value_type = T;
+  T *allocate(std::size_t);
+  T *allocate(std::size_t, const void *);
+};
+
+template <typename Alloc>
+struct allocator_traits {
+  using value_type = typename Alloc::value_type;
+  using pointer = value_type *;
+  using size_type = size_t;
+  using const_void_pointer = const void *;
+  static pointer allocate(Alloc &, size_type);
+  static pointer allocate(Alloc &, size_type, const_void_pointer);
+};
+
+template <typename OuterAlloc, typename... InnerAlloc>
+struct scoped_allocator_adaptor : public OuterAlloc {
+  using pointer = typename allocator_traits<OuterAlloc>::pointer;
+  using size_type = typename allocator_traits<OuterAlloc>::size_type;
+  using const_void_pointer = typename allocator_traits<OuterAlloc>::const_void_pointer;
+  pointer allocate(size_type);
+  pointer allocate(size_type, const_void_pointer);
+};
+
+template <typename T>
+struct default_delete {};
+
+template <typename T, typename Deleter = default_delete<T>>
+struct unique_ptr {
+  using pointer = T *;
+  pointer release() noexcept;
+};
+
+template <typename T, typename Allocator = allocator<T>>
+struct vector {
+  bool empty() const noexcept;
+};
+
+namespace filesystem {
+
+struct path {
+  bool empty() const noexcept;
+};
+
+} // namespace filesystem
+
+namespace pmr {
+
+struct memory_resource {
+  void *allocate(size_t, size_t = alignof(max_align_t));
+};
+
+template <typename T>
+struct polymorphic_allocator {
+  T *allocate(size_t);
+};
+
+} // namespace pmr
+
+} // namespace std
+
+struct Foo {
+  void f();
+};
+
+int increment(int i) {
+  return i + 1;
+}
+
+void useFuture(const std::future &fut);
+
+struct FooAlloc {
+  using value_type = Foo;
+};
+
+void warning() {
+  std::async(increment, 42);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::async(std::launch::async, increment, 42);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  Foo F;
+  std::launder(&F);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::unique_ptr<Foo> UP;
+  UP.release();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::allocator<Foo> FA;
+  FA.allocate(1);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+  FA.allocate(1, nullptr);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::allocator_traits<std::allocator<Foo>>::allocate(FA, 1);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+  std::allocator_traits<std::allocator<Foo>>::allocate(FA, 1, nullptr);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::scoped_allocator_adaptor<FooAlloc, FooAlloc> SAA;
+  SAA.allocate(1);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+  SAA.allocate(1, nullptr);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::pmr::memory_resource MR;
+  MR.allocate(1);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::pmr::polymorphic_allocator<Foo> PA;
+  PA.allocate(1);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::vector<Foo> FV;
+  FV.empty();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::filesystem::path P;
+  P.empty();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  std::empty(FV);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+}
+
+void noWarning() {
+  auto AsyncRetval1 = std::async(increment, 42);
+  auto AsyncRetval2 = std::async(std::launch::async, increment, 42);
+
+  Foo FNoWarning;
+  auto LaunderRetval = std::launder(&FNoWarning);
+
+  std::unique_ptr<Foo> UPNoWarning;
+  auto ReleaseRetval = UPNoWarning.release();
+
+  std::allocator<Foo> FANoWarning;
+  auto AllocRetval1 = FANoWarning.allocate(1, nullptr);
+  auto AllocRetval2 = FANoWarning.allocate(1);
+
+  auto AllocRetval3 = std::allocator_traits<std::allocator<Foo>>::allocate(FANoWarning, 1);
+  auto AllocRetval4 = std::allocator_traits<std::allocator<Foo>>::allocate(FANoWarning, 1, nullptr);
+
+  std::scoped_allocator_adaptor<FooAlloc, FooAlloc> SAANoWarning;
+  auto AllocRetval5 = SAANoWarning.allocate(1);
+  auto AllocRetval6 = SAANoWarning.allocate(1, nullptr);
+
+  std::pmr::memory_resource MRNoWarning;
+  auto AllocRetval7 = MRNoWarning.allocate(1);
+
+  std::pmr::polymorphic_allocator<Foo> PANoWarning;
+  auto AllocRetval8 = PANoWarning.allocate(1);
+
+  std::vector<Foo> FVNoWarning;
+  auto VectorEmptyRetval = FVNoWarning.empty();
+
+  std::filesystem::path PNoWarning;
+  auto PathEmptyRetval = PNoWarning.empty();
+
+  auto EmptyRetval = std::empty(FVNoWarning);
+
+  // test using the return value in different kinds of expressions
+  useFuture(std::async(increment, 42));
+  std::launder(&FNoWarning)->f();
+  delete std::launder(&FNoWarning);
+
+  // cast to void should allow ignoring the return value
+  (void)std::async(increment, 42);
+
+  // test discarding return value of functions that are not configured to be checked
+  increment(1);
+
+  // test that the check is disabled inside GNU statement expressions
+  ({ std::async(increment, 42); });
+  auto StmtExprRetval = ({ std::async(increment, 42); });
+}
Index: test/clang-tidy/bugprone-unused-return-value-custom.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/bugprone-unused-return-value-custom.cpp
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy %s bugprone-unused-return-value %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: bugprone-unused-return-value.FunctionsRegex, \
+// RUN:    value: "^::fun$|^::ns::Template<.*>::Inner::memFun$|^::ns::Type::staticFun$"}]}' \
+// RUN: --
+
+namespace std {
+
+template <typename T>
+T *launder(T *);
+
+} // namespace std
+
+namespace ns {
+
+template <typename T>
+struct Template {
+  struct Inner {
+    bool memFun();
+  };
+};
+
+using IntType = Template<int>;
+
+struct Derived : public Template<bool>::Inner {};
+
+struct Retval {
+  int *P;
+  Retval() { P = new int; }
+  ~Retval() { delete P; }
+};
+
+struct Type {
+  Retval memFun();
+  static Retval staticFun();
+};
+
+} // namespace ns
+
+int fun();
+
+void warning() {
+  fun();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  (fun());
+  // CHECK-MESSAGES: [[@LINE-1]]:4: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  ns::Template<bool>::Inner ObjA1;
+  ObjA1.memFun();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  ns::IntType::Inner ObjA2;
+  ObjA2.memFun();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  ns::Derived ObjA3;
+  ObjA3.memFun();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+
+  ns::Type::staticFun();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value]
+}
+
+void noWarning() {
+  auto R1 = fun();
+
+  ns::Template<bool>::Inner ObjB1;
+  auto R2 = ObjB1.memFun();
+
+  auto R3 = ns::Type::staticFun();
+
+  // test discarding return value of functions that are not configured to be checked
+  int I = 1;
+  std::launder(&I);
+
+  ns::Type ObjB2;
+  ObjB2.memFun();
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -32,6 +32,7 @@
    bugprone-string-constructor
    bugprone-suspicious-memset-usage
    bugprone-undefined-memory-manipulation
+   bugprone-unused-return-value
    bugprone-use-after-move
    bugprone-virtual-near-miss
    cert-dcl03-c (redirects to misc-static-assert) <cert-dcl03-c>
Index: docs/clang-tidy/checks/bugprone-unused-return-value.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/bugprone-unused-return-value.rst
@@ -0,0 +1,32 @@
+.. title:: clang-tidy - bugprone-unused-return-value
+
+bugprone-unused-return-value
+============================
+
+Warns on unused function return values. The checked funtions can be configured.
+
+Options
+-------
+
+.. option:: FunctionsRegex
+
+   A regular expression specifying the functions to check. Defaults to
+   ``^::std::async$|^::std::launder$|^::std::unique_ptr<.*>::release$|^::std::.*::allocate$|^::std::(.*::)*empty$``.
+   This means that the calls to following functions are checked by default:
+
+   - ``std::async()``. Not using the return value makes the call synchronous.
+   - ``std::launder()``. Not using the return value usually means that the
+     function interface was misunderstood by the programmer. Only the returned
+     pointer is "laundered", not the argument.
+   - ``std::unique_ptr::release()``. Not using the return value can lead to
+     resource leaks if the same pointer isn't stored anywhere else. Often,
+     ignoring the ``release()`` return value indicates that the programmer
+     confused the function with ``reset()``.
+   - All ``allocate()`` calls in ``std``. For example
+     ``std::allocator::allocate()`` and
+     ``std::pmr::memory_resource::allocate()``. Not using the return value is a
+     resource leak.
+   - All ``empty()`` calls in ``std``. For example ``std::vector::empty()``,
+     ``std::filesystem::path::empty()`` and ``std::empty()``. Not using the
+     return value often indicates that the programmer confused the function with
+     ``clear()``.
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -125,6 +125,11 @@
   a memory allocation function (``malloc()``, ``calloc()``, ``realloc()``,
   ``alloca()``) or the ``new[]`` operator in `C++`.
 
+- New `bugprone-unused-return-value
+  <http://clang.llvm.org/extra/clang-tidy/checks/bugprone-unused-return-value.html>`_ check
+
+  Warns on unused function return values.
+
 - New `cppcoreguidelines-owning-memory <http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-owning-memory.html>`_ check 
 
   This check implements the type-based semantic of ``gsl::owner<T*>``, but without
Index: clang-tidy/bugprone/UnusedReturnValueCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/bugprone/UnusedReturnValueCheck.h
@@ -0,0 +1,39 @@
+//===--- UnusedReturnValueCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRETURNVALUECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRETURNVALUECHECK_H
+
+#include "../ClangTidy.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Detects function calls where the return value is unused.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-unused-return-value.html
+class UnusedReturnValueCheck : public ClangTidyCheck {
+public:
+  UnusedReturnValueCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::string FuncRegex;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRETURNVALUECHECK_H
Index: clang-tidy/bugprone/UnusedReturnValueCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/bugprone/UnusedReturnValueCheck.cpp
@@ -0,0 +1,82 @@
+//===--- UnusedReturnValueCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnusedReturnValueCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+
+// Same as matchesName, but instead of fully qualified name, matches on a name
+// where inline namespaces have been ignored.
+AST_MATCHER_P(NamedDecl, matchesInlinedName, std::string, RegExp) {
+  assert(!RegExp.empty());
+  llvm::SmallString<128> InlinedName("::");
+  llvm::raw_svector_ostream OS(InlinedName);
+  PrintingPolicy Policy = Node.getASTContext().getPrintingPolicy();
+  Policy.SuppressUnwrittenScope = true;
+  Node.printQualifiedName(OS, Policy);
+  return llvm::Regex(RegExp).match(OS.str());
+}
+
+} // namespace
+
+UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name,
+                                               ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      FuncRegex(Options.get("FunctionsRegex",
+                            "^::std::async$|"
+                            "^::std::launder$|"
+                            "^::std::unique_ptr<.*>::release$|"
+                            "^::std::.*::allocate$|"
+                            "^::std::(.*::)*empty$")) {}
+
+void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "FunctionsRegex", FuncRegex);
+}
+
+void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
+  // Detect unused return values by finding CallExprs with CompoundStmt parent,
+  // ignoring any implicit nodes and parentheses in between.
+  // Disable the check inside GNU statement expressions by ignoring
+  // CompoundStmts with StmtExpr parent.
+  Finder->addMatcher(
+      compoundStmt(
+          forEach(expr(ignoringImplicit(ignoringParenImpCasts(
+              callExpr(callee(functionDecl(matchesInlinedName(FuncRegex))))
+                  .bind("match"))))),
+          unless(hasParent(stmtExpr()))),
+      this);
+}
+
+void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("match")) {
+    diag(Matched->getLocStart(),
+         "the value returned by this function should be used")
+        << Matched->getSourceRange();
+    diag(Matched->getLocStart(),
+         "cast the expression to void to silence this warning",
+         DiagnosticIDs::Note);
+  }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/bugprone/CMakeLists.txt
===================================================================
--- clang-tidy/bugprone/CMakeLists.txt
+++ clang-tidy/bugprone/CMakeLists.txt
@@ -17,6 +17,7 @@
   StringConstructorCheck.cpp
   SuspiciousMemsetUsageCheck.cpp
   UndefinedMemoryManipulationCheck.cpp
+  UnusedReturnValueCheck.cpp
   UseAfterMoveCheck.cpp
   VirtualNearMissCheck.cpp
 
Index: clang-tidy/bugprone/BugproneTidyModule.cpp
===================================================================
--- clang-tidy/bugprone/BugproneTidyModule.cpp
+++ clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -25,6 +25,7 @@
 #include "StringConstructorCheck.h"
 #include "SuspiciousMemsetUsageCheck.h"
 #include "UndefinedMemoryManipulationCheck.h"
+#include "UnusedReturnValueCheck.h"
 #include "UseAfterMoveCheck.h"
 #include "VirtualNearMissCheck.h"
 
@@ -65,6 +66,8 @@
         "bugprone-suspicious-memset-usage");
     CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
         "bugprone-undefined-memory-manipulation");
+    CheckFactories.registerCheck<UnusedReturnValueCheck>(
+        "bugprone-unused-return-value");
     CheckFactories.registerCheck<UseAfterMoveCheck>(
         "bugprone-use-after-move");
     CheckFactories.registerCheck<VirtualNearMissCheck>(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to