[clang-tools-extra] 0f4c70d - [clang-tidy] Add spuriously-wake-up-functions check

via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 21 04:04:41 PDT 2020


Author: abelkocsis
Date: 2020-03-21T12:04:03+01:00
New Revision: 0f4c70dd3ec6d7ee831f868e3e483273daec18f0

URL: https://github.com/llvm/llvm-project/commit/0f4c70dd3ec6d7ee831f868e3e483273daec18f0
DIFF: https://github.com/llvm/llvm-project/commit/0f4c70dd3ec6d7ee831f868e3e483273daec18f0.diff

LOG: [clang-tidy] Add spuriously-wake-up-functions check

Summary:
According to
https://wiki.sei.cmu.edu/confluence/display/cplusplus/CON54-CPP.+Wrap+functions+that+can+spuriously+wake+up+in+a+loop
and
https://wiki.sei.cmu.edu/confluence/display/c/CON36-C.+Wrap+functions+that+can+spuriously+wake+up+in+a+loop
misc-spuriously-wake-up-functions check is created. The check finds
`cnd_wait` or `wait` function calls in an `IfStmt` and  warns the user to
replace it with a `WhileStmt` or use it with a lambda parameter.

Reviewers: aaron.ballman, alexfh, hokein, jfb, Charusso

Reviewed By: aaron.ballman

Subscribers: sylvestre.ledru, whisperity, Eugene.Zelenko, mgorny, dexonsmith, cfe-commits, gerazo, xazax.hun, steakhal, Charusso

Tags: #clang-tools-extra, #clang

Differential Revision: https://reviews.llvm.org/D70876

Added: 
    clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst
    clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst
    clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c
    clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 9dcb315a257a..d010c3ce7e52 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -41,6 +41,7 @@
 #include "SignedCharMisuseCheck.h"
 #include "SizeofContainerCheck.h"
 #include "SizeofExpressionCheck.h"
+#include "SpuriouslyWakeUpFunctionsCheck.h"
 #include "StringConstructorCheck.h"
 #include "StringIntegerAssignmentCheck.h"
 #include "StringLiteralWithEmbeddedNulCheck.h"
@@ -133,6 +134,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-sizeof-container");
     CheckFactories.registerCheck<SizeofExpressionCheck>(
         "bugprone-sizeof-expression");
+    CheckFactories.registerCheck<SpuriouslyWakeUpFunctionsCheck>(
+        "bugprone-spuriously-wake-up-functions");
     CheckFactories.registerCheck<StringConstructorCheck>(
         "bugprone-string-constructor");
     CheckFactories.registerCheck<StringIntegerAssignmentCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index a24f3bc7eb0d..4aa3b325ce24 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -33,6 +33,7 @@ add_clang_library(clangTidyBugproneModule
   SignedCharMisuseCheck.cpp
   SizeofContainerCheck.cpp
   SizeofExpressionCheck.cpp
+  SpuriouslyWakeUpFunctionsCheck.cpp
   StringConstructorCheck.cpp
   StringIntegerAssignmentCheck.cpp
   StringLiteralWithEmbeddedNulCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp
new file mode 100644
index 000000000000..844d672f121f
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp
@@ -0,0 +1,108 @@
+//===--- SpuriouslyWakeUpFunctionsCheck.cpp - clang-tidy ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SpuriouslyWakeUpFunctionsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void SpuriouslyWakeUpFunctionsCheck::registerMatchers(MatchFinder *Finder) {
+
+  auto hasUniqueLock = hasDescendant(declRefExpr(
+      hasDeclaration(varDecl(hasType(recordDecl(classTemplateSpecializationDecl(
+          hasName("::std::unique_lock"),
+          hasTemplateArgument(
+              0, templateArgument(refersToType(qualType(hasDeclaration(
+                     cxxRecordDecl(hasName("::std::mutex"))))))))))))));
+
+  auto hasWaitDescendantCPP = hasDescendant(
+      cxxMemberCallExpr(
+          anyOf(
+              allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
+                        allOf(hasName("::std::condition_variable::wait"),
+                              parameterCountIs(1)))))),
+                    onImplicitObjectArgument(
+                        declRefExpr(to(varDecl(hasType(references(recordDecl(
+                            hasName("::std::condition_variable")))))))),
+                    hasUniqueLock),
+              allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
+                        allOf(hasName("::std::condition_variable::wait_for"),
+                              parameterCountIs(2)))))),
+                    onImplicitObjectArgument(
+                        declRefExpr(to(varDecl(hasType(references(recordDecl(
+                            hasName("::std::condition_variable")))))))),
+                    hasUniqueLock),
+              allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
+                        allOf(hasName("::std::condition_variable::wait_until"),
+                              parameterCountIs(2)))))),
+                    onImplicitObjectArgument(
+                        declRefExpr(to(varDecl(hasType(references(recordDecl(
+                            hasName("::std::condition_variable")))))))),
+                    hasUniqueLock)
+
+                  ))
+          .bind("wait"));
+
+  auto hasWaitDescendantC = hasDescendant(
+      callExpr(callee(functionDecl(
+                   anyOf(hasName("cnd_wait"), hasName("cnd_timedwait")))))
+          .bind("wait"));
+  if (getLangOpts().CPlusPlus) {
+    // Check for `CON54-CPP`
+    Finder->addMatcher(
+        ifStmt(
+
+            allOf(hasWaitDescendantCPP,
+                  unless(anyOf(hasDescendant(ifStmt(hasWaitDescendantCPP)),
+                               hasDescendant(whileStmt(hasWaitDescendantCPP)),
+                               hasDescendant(forStmt(hasWaitDescendantCPP)),
+                               hasDescendant(doStmt(hasWaitDescendantCPP)))))
+
+                ),
+        this);
+  } else {
+    // Check for `CON36-C`
+    Finder->addMatcher(
+
+        ifStmt(
+            allOf(hasWaitDescendantC,
+                  unless(anyOf(hasDescendant(ifStmt(hasWaitDescendantC)),
+                               hasDescendant(whileStmt(hasWaitDescendantC)),
+                               hasDescendant(forStmt(hasWaitDescendantC)),
+                               hasDescendant(doStmt(hasWaitDescendantC)),
+                               hasParent(whileStmt()),
+                               hasParent(compoundStmt(hasParent(whileStmt()))),
+                               hasParent(forStmt()),
+                               hasParent(compoundStmt(hasParent(forStmt()))),
+                               hasParent(doStmt()),
+                               hasParent(compoundStmt(hasParent(doStmt())))))
+
+                      ))
+
+            ,
+        this);
+  }
+}
+
+void SpuriouslyWakeUpFunctionsCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedWait = Result.Nodes.getNodeAs<CallExpr>("wait");
+  StringRef WaitName = MatchedWait->getDirectCallee()->getName();
+  diag(MatchedWait->getExprLoc(),
+       "'%0' should be placed inside a while statement %select{|or used with a "
+       "conditional parameter}1")
+      << WaitName << (WaitName != "cnd_wait" && WaitName != "cnd_timedwait");
+}
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang

diff  --git a/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h
new file mode 100644
index 000000000000..d2d3745769f7
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h
@@ -0,0 +1,37 @@
+//===--- SpuriouslyWakeUpFunctionsCheck.h - clang-tidy ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPURIOUSLYWAKEUPFUNCTIONSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPURIOUSLYWAKEUPFUNCTIONSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or 
+/// ``wait_until`` function calls when the function is not invoked from a loop 
+/// that checks whether a condition predicate holds or the function has a 
+/// condition parameter.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-spuriously-wake-up-functions.html
+class SpuriouslyWakeUpFunctionsCheck : public ClangTidyCheck {
+public:
+  SpuriouslyWakeUpFunctionsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPURIOUSLYWAKEUPFUNCTIONSCHECK_H

diff  --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
index 226526d31970..6459dcf5627d 100644
--- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -11,6 +11,7 @@
 #include "../ClangTidyModuleRegistry.h"
 #include "../bugprone/BadSignalToKillThreadCheck.h"
 #include "../bugprone/ReservedIdentifierCheck.h"
+#include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h"
 #include "../bugprone/UnhandledSelfAssignmentCheck.h"
 #include "../google/UnnamedNamespaceInHeaderCheck.h"
 #include "../misc/NewDeleteOverloadsCheck.h"
@@ -42,6 +43,9 @@ class CERTModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     // C++ checkers
+    // CON
+    CheckFactories.registerCheck<bugprone::SpuriouslyWakeUpFunctionsCheck>(
+        "cert-con54-cpp");
     // DCL
     CheckFactories.registerCheck<PostfixOperatorCheck>(
         "cert-dcl21-cpp");
@@ -80,6 +84,9 @@ class CERTModule : public ClangTidyModule {
         "cert-oop58-cpp");
 
     // C checkers
+    // CON
+    CheckFactories.registerCheck<bugprone::SpuriouslyWakeUpFunctionsCheck>(
+        "cert-con36-c");
     // DCL
     CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c");
     CheckFactories.registerCheck<readability::UppercaseLiteralSuffixCheck>(

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3b9212f6723c..745a1d1035ed 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -87,6 +87,14 @@ New checks
   result of a memory allocation function (``malloc()``, ``calloc()``,
   ``realloc()``, ``alloca()``) instead of its argument.
 
+- New :doc:`bugprone-spuriously-wake-up-functions
+  <clang-tidy/checks/bugprone-spuriously-wake-up-functions>` check.
+
+  Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or
+  ``wait_until`` function calls when the function is not invoked from a loop
+  that checks whether a condition predicate holds or the function has a 
+  condition parameter.
+
 - New :doc:`bugprone-reserved-identifier
   <clang-tidy/checks/bugprone-reserved-identifier>` check.
 
@@ -124,6 +132,16 @@ New checks
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
+- New alias :doc:`cert-con36-c
+  <clang-tidy/checks/cert-con36-c>` to
+  :doc:`bugprone-spuriously-wake-up-functions
+  <clang-tidy/checks/bugprone-spuriously-wake-up-functions>` was added.
+
+- New alias :doc:`cert-con54-cpp
+  <clang-tidy/checks/cert-con54-cpp>` to
+  :doc:`bugprone-spuriously-wake-up-functions
+  <clang-tidy/checks/bugprone-spuriously-wake-up-functions>` was added.
+
 - New alias :doc:`cert-dcl37-c
   <clang-tidy/checks/cert-dcl37-c>` to
   :doc:`bugprone-reserved-identifier

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst
new file mode 100644
index 000000000000..17b81e13f579
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst
@@ -0,0 +1,29 @@
+.. title:: clang-tidy - bugprone-spuriously-wake-up-functions
+
+bugprone-spuriously-wake-up-functions
+=====================================
+
+Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or 
+``wait_until`` function calls when the function is not invoked from a loop
+that checks whether a condition predicate holds or the function has a 
+condition parameter.
+
+.. code-block: c++
+
+    if (condition_predicate) {
+        condition.wait(lk);
+    }
+
+.. code-block: c
+
+    if (condition_predicate) {
+        if (thrd_success != cnd_wait(&condition, &lock)) {
+        }
+    }
+
+This check corresponds to the CERT C++ Coding Standard rule
+`CON54-CPP. Wrap functions that can spuriously wake up in a loop
+<https://wiki.sei.cmu.edu/confluence/display/cplusplus/CON54-CPP.+Wrap+functions+that+can+spuriously+wake+up+in+a+loop>`_.
+and CERT C Coding Standard rule
+`CON36-C. Wrap functions that can spuriously wake up in a loop
+<https://wiki.sei.cmu.edu/confluence/display/c/CON36-C.+Wrap+functions+that+can+spuriously+wake+up+in+a+loop>`_.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst
new file mode 100644
index 000000000000..54fecfc2e90d
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-con36-c
+.. meta::
+:http-equiv=refresh: 5;URL=bugprone-spuriously-wake-up-functions.html
+	
+cert-con36-c
+============
+
+The cert-con36-c check is an alias, please see
+`bugprone-spuriously-wake-up-functions <bugprone-spuriously-wake-up-functions.html>`_ 
+for more information.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst
new file mode 100644
index 000000000000..3dbe89517474
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-con54-cpp
+.. meta::
+:http-equiv=refresh: 5;URL=bugprone-spuriously-wake-up-functions.html
+	
+cert-con54-cpp
+==============
+
+The cert-con54-cpp check is an alias, please see
+`bugprone-spuriously-wake-up-functions <bugprone-spuriously-wake-up-functions.html>`_ 
+for more information.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 37f020d6018f..333a7ea4d5b4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -76,6 +76,7 @@ Clang-Tidy Checks
    `bugprone-signed-char-misuse <bugprone-signed-char-misuse.html>`_,
    `bugprone-sizeof-container <bugprone-sizeof-container.html>`_,
    `bugprone-sizeof-expression <bugprone-sizeof-expression.html>`_,
+   `bugprone-spuriously-wake-up-functions <bugprone-spuriously-wake-up-functions.html>`_,
    `bugprone-string-constructor <bugprone-string-constructor.html>`_, "Yes"
    `bugprone-string-integer-assignment <bugprone-string-integer-assignment.html>`_, "Yes"
    `bugprone-string-literal-with-embedded-nul <bugprone-string-literal-with-embedded-nul.html>`_,
@@ -300,6 +301,8 @@ Clang-Tidy Checks
 .. csv-table:: Aliases..
    :header: "Name", "Redirect", "Offers fixes"
 
+   `cert-con36-c <cert-con36-c.html>`_, `bugprone-spuriously-wake-up-functions <bugprone-spuriously-wake-up-functions.html>`_,
+   `cert-con54-cpp <cert-con54-cpp.html>`_, `bugprone-spuriously-wake-up-functions <bugprone-spuriously-wake-up-functions.html>`_,
    `cert-dcl03-c <cert-dcl03-c.html>`_, `misc-static-assert <misc-static-assert.html>`_, "Yes"
    `cert-dcl16-c <cert-dcl16-c.html>`_, `readability-uppercase-literal-suffix <readability-uppercase-literal-suffix.html>`_, "Yes"
    `cert-dcl37-c <cert-dcl37-c.html>`_, `bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_, "Yes"

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c
new file mode 100644
index 000000000000..fd3b94081c20
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c
@@ -0,0 +1,164 @@
+// RUN: %check_clang_tidy %s bugprone-spuriously-wake-up-functions %t -- --
+#define NULL 0
+
+struct Node1 {
+  void *Node1;
+  struct Node1 *next;
+};
+
+typedef struct mtx_t {
+} mtx_t;
+typedef struct cnd_t {
+} cnd_t;
+struct timespec {};
+
+int cnd_wait(cnd_t *cond, mtx_t *mutex){};
+int cnd_timedwait(cnd_t *cond, mtx_t *mutex,
+                  const struct timespec *time_point){};
+
+struct Node1 list_c;
+static mtx_t lock;
+static cnd_t condition_c;
+struct timespec ts;
+
+void consume_list_element(void) {
+
+  if (list_c.next == NULL) {
+    if (0 != cnd_wait(&condition_c, &lock)) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+    }
+  }
+  if (list_c.next == NULL)
+    if (0 != cnd_wait(&condition_c, &lock))
+      // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+      ;
+  if (list_c.next == NULL && 0 != cnd_wait(&condition_c, &lock))
+    // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+    ;
+  while (list_c.next == NULL) {
+    if (0 != cnd_wait(&condition_c, &lock)) {
+    }
+  }
+  while (list_c.next == NULL)
+    if (0 != cnd_wait(&condition_c, &lock)) {
+    }
+  while (list_c.next == NULL)
+    if (0 != cnd_wait(&condition_c, &lock))
+      ;
+  if (list_c.next == NULL) {
+    cnd_wait(&condition_c, &lock);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+  }
+  if (list_c.next == NULL)
+    cnd_wait(&condition_c, &lock);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+  while (list_c.next == NULL) {
+    cnd_wait(&condition_c, &lock);
+  }
+  while (list_c.next == NULL)
+    cnd_wait(&condition_c, &lock);
+
+  do {
+    if (0 != cnd_wait(&condition_c, &lock)) {
+    }
+  } while (list_c.next == NULL);
+  do
+    if (0 != cnd_wait(&condition_c, &lock)) {
+    }
+  while (list_c.next == NULL);
+  do
+    if (0 != cnd_wait(&condition_c, &lock))
+      ;
+  while (list_c.next == NULL);
+  do {
+    cnd_wait(&condition_c, &lock);
+  } while (list_c.next == NULL);
+  do
+    cnd_wait(&condition_c, &lock);
+  while (list_c.next == NULL);
+  for (;; list_c.next == NULL) {
+    if (0 != cnd_wait(&condition_c, &lock)) {
+    }
+  }
+  for (;; list_c.next == NULL)
+    if (0 != cnd_wait(&condition_c, &lock)) {
+    }
+  for (;; list_c.next == NULL)
+    if (0 != cnd_wait(&condition_c, &lock))
+      ;
+  for (;; list_c.next == NULL) {
+    cnd_wait(&condition_c, &lock);
+  }
+  for (;; list_c.next == NULL)
+    cnd_wait(&condition_c, &lock);
+
+  if (list_c.next == NULL) {
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+    }
+  }
+  if (list_c.next == NULL)
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts))
+      // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+      ;
+  if (list_c.next == NULL && 0 != cnd_timedwait(&condition_c, &lock, &ts))
+    // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+    ;
+  while (list_c.next == NULL) {
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+    }
+  }
+  while (list_c.next == NULL)
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+    }
+  while (list_c.next == NULL)
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts))
+      ;
+  if (list_c.next == NULL) {
+    cnd_timedwait(&condition_c, &lock, &ts);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+  }
+  if (list_c.next == NULL)
+    cnd_timedwait(&condition_c, &lock, &ts);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions]
+  while (list_c.next == NULL) {
+    cnd_timedwait(&condition_c, &lock, &ts);
+  }
+  while (list_c.next == NULL)
+    cnd_timedwait(&condition_c, &lock, &ts);
+
+  do {
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+    }
+  } while (list_c.next == NULL);
+  do
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+    }
+  while (list_c.next == NULL);
+  do
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts))
+      ;
+  while (list_c.next == NULL);
+  do {
+    cnd_timedwait(&condition_c, &lock, &ts);
+  } while (list_c.next == NULL);
+  do
+    cnd_timedwait(&condition_c, &lock, &ts);
+  while (list_c.next == NULL);
+  for (;; list_c.next == NULL) {
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+    }
+  }
+  for (;; list_c.next == NULL)
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts)) {
+    }
+  for (;; list_c.next == NULL)
+    if (0 != cnd_timedwait(&condition_c, &lock, &ts))
+      ;
+  for (;; list_c.next == NULL) {
+    cnd_timedwait(&condition_c, &lock, &ts);
+  }
+  for (;; list_c.next == NULL)
+    cnd_timedwait(&condition_c, &lock, &ts);
+}
+int main() { return 0; }

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp
new file mode 100644
index 000000000000..6db92ef939fa
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp
@@ -0,0 +1,191 @@
+// RUN: %check_clang_tidy %s bugprone-spuriously-wake-up-functions %t -- --
+#define NULL 0
+
+namespace std {
+using intmax_t = int;
+
+template <intmax_t N, intmax_t D = 1>
+class ratio {
+public:
+  static constexpr intmax_t num = 0;
+  static constexpr intmax_t den = 0;
+  typedef ratio<num, den> type;
+};
+typedef ratio<1, 1000> milli;
+namespace chrono {
+
+template <class Rep, class Period = ratio<1>>
+class duration {
+public:
+  using rep = Rep;
+  using period = Period;
+
+public:
+  constexpr duration() = default;
+  template <class Rep2>
+  constexpr explicit duration(const Rep2 &r);
+  template <class Rep2, class Period2>
+  constexpr duration(const duration<Rep2, Period2> &d);
+  ~duration() = default;
+  duration(const duration &) = default;
+};
+
+template <class Clock, class Duration = typename Clock::duration>
+class time_point {
+public:
+  using clock = Clock;
+  using duration = Duration;
+
+public:
+  constexpr time_point();
+  constexpr explicit time_point(const duration &d);
+  template <class Duration2>
+  constexpr time_point(const time_point<clock, Duration2> &t);
+};
+
+using milliseconds = duration<int, milli>;
+
+class system_clock {
+public:
+  typedef milliseconds duration;
+  typedef duration::rep rep;
+  typedef duration::period period;
+  typedef chrono::time_point<system_clock> time_point;
+
+  static time_point now() noexcept;
+};
+} // namespace chrono
+
+class mutex;
+template <class Mutex>
+class unique_lock {
+public:
+  typedef Mutex mutex_type;
+
+  unique_lock() noexcept;
+  explicit unique_lock(mutex_type &m);
+};
+
+class mutex {
+public:
+  constexpr mutex() noexcept;
+  ~mutex();
+  mutex(const mutex &) = delete;
+  mutex &operator=(const mutex &) = delete;
+};
+
+enum class cv_status {
+  no_timeout,
+  timeout
+};
+
+class condition_variable {
+public:
+  condition_variable();
+  ~condition_variable();
+  condition_variable(const condition_variable &) = delete;
+
+  void wait(unique_lock<mutex> &lock);
+  template <class Predicate>
+  void wait(unique_lock<mutex> &lock, Predicate pred);
+  template <class Clock, class Duration>
+  cv_status wait_until(unique_lock<mutex> &lock,
+                       const chrono::time_point<Clock, Duration> &abs_time){};
+  template <class Clock, class Duration, class Predicate>
+  bool wait_until(unique_lock<mutex> &lock,
+                  const chrono::time_point<Clock, Duration> &abs_time,
+                  Predicate pred){};
+  template <class Rep, class Period>
+  cv_status wait_for(unique_lock<mutex> &lock,
+                     const chrono::duration<Rep, Period> &rel_time){};
+  template <class Rep, class Period, class Predicate>
+  bool wait_for(unique_lock<mutex> &lock,
+                const chrono::duration<Rep, Period> &rel_time,
+                Predicate pred){};
+};
+
+} // namespace std
+
+struct Node1 {
+  void *Node1;
+  struct Node1 *next;
+};
+
+static Node1 list;
+static std::mutex m;
+static std::condition_variable condition;
+
+void consume_list_element(std::condition_variable &condition) {
+  std::unique_lock<std::mutex> lk(m);
+
+  if (list.next == nullptr) {
+    condition.wait(lk);
+    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'wait' should be placed inside a while statement or used with a conditional parameter [bugprone-spuriously-wake-up-functions]
+  }
+
+  while (list.next == nullptr) {
+    condition.wait(lk);
+  }
+
+  do {
+    condition.wait(lk);
+  } while (list.next == nullptr);
+
+  for (;; list.next == nullptr) {
+    condition.wait(lk);
+  }
+
+  if (list.next == nullptr) {
+    while (list.next == nullptr) {
+      condition.wait(lk);
+    }
+  }
+
+  if (list.next == nullptr) {
+    do {
+      condition.wait(lk);
+    } while (list.next == nullptr);
+  }
+
+  if (list.next == nullptr) {
+    for (;; list.next == nullptr) {
+      condition.wait(lk);
+    }
+  }
+  using durtype = std::chrono::duration<int, std::milli>;
+  durtype dur = std::chrono::duration<int, std::milli>();
+  if (list.next == nullptr) {
+    condition.wait_for(lk, dur);
+    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'wait_for' should be placed inside a while statement or used with a conditional parameter [bugprone-spuriously-wake-up-functions]
+  }
+  if (list.next == nullptr) {
+    condition.wait_for(lk, dur, [] { return 1; });
+  }
+  while (list.next == nullptr) {
+    condition.wait_for(lk, dur);
+  }
+  do {
+    condition.wait_for(lk, dur);
+  } while (list.next == nullptr);
+  for (;; list.next == nullptr) {
+    condition.wait_for(lk, dur);
+  }
+
+  auto now = std::chrono::system_clock::now();
+  if (list.next == nullptr) {
+    condition.wait_until(lk, now);
+    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'wait_until' should be placed inside a while statement or used with a conditional parameter [bugprone-spuriously-wake-up-functions]
+  }
+  if (list.next == nullptr) {
+    condition.wait_until(lk, now, [] { return 1; });
+  }
+  while (list.next == nullptr) {
+    condition.wait_until(lk, now);
+  }
+  do {
+    condition.wait_until(lk, now);
+  } while (list.next == nullptr);
+  for (;; list.next == nullptr) {
+    condition.wait_until(lk, now);
+  }
+}


        


More information about the cfe-commits mailing list