1 //===--- SpuriouslyWakeUpFunctionsCheck.cpp - clang-tidy ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "SpuriouslyWakeUpFunctionsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace bugprone {
18 
registerMatchers(MatchFinder * Finder)19 void SpuriouslyWakeUpFunctionsCheck::registerMatchers(MatchFinder *Finder) {
20 
21   auto hasUniqueLock = hasDescendant(declRefExpr(
22       hasDeclaration(varDecl(hasType(recordDecl(classTemplateSpecializationDecl(
23           hasName("::std::unique_lock"),
24           hasTemplateArgument(
25               0, templateArgument(refersToType(qualType(hasDeclaration(
26                      cxxRecordDecl(hasName("::std::mutex"))))))))))))));
27 
28   auto hasWaitDescendantCPP = hasDescendant(
29       cxxMemberCallExpr(
30           anyOf(
31               allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
32                         allOf(hasName("::std::condition_variable::wait"),
33                               parameterCountIs(1)))))),
34                     onImplicitObjectArgument(
35                         declRefExpr(to(varDecl(hasType(references(recordDecl(
36                             hasName("::std::condition_variable")))))))),
37                     hasUniqueLock),
38               allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
39                         allOf(hasName("::std::condition_variable::wait_for"),
40                               parameterCountIs(2)))))),
41                     onImplicitObjectArgument(
42                         declRefExpr(to(varDecl(hasType(references(recordDecl(
43                             hasName("::std::condition_variable")))))))),
44                     hasUniqueLock),
45               allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
46                         allOf(hasName("::std::condition_variable::wait_until"),
47                               parameterCountIs(2)))))),
48                     onImplicitObjectArgument(
49                         declRefExpr(to(varDecl(hasType(references(recordDecl(
50                             hasName("::std::condition_variable")))))))),
51                     hasUniqueLock)
52 
53                   ))
54           .bind("wait"));
55 
56   auto hasWaitDescendantC = hasDescendant(
57       callExpr(callee(functionDecl(hasAnyName("cnd_wait", "cnd_timedwait"))))
58           .bind("wait"));
59   if (getLangOpts().CPlusPlus) {
60     // Check for `CON54-CPP`
61     Finder->addMatcher(
62         ifStmt(
63 
64             allOf(hasWaitDescendantCPP,
65                   unless(anyOf(hasDescendant(ifStmt(hasWaitDescendantCPP)),
66                                hasDescendant(whileStmt(hasWaitDescendantCPP)),
67                                hasDescendant(forStmt(hasWaitDescendantCPP)),
68                                hasDescendant(doStmt(hasWaitDescendantCPP)))))
69 
70                 ),
71         this);
72   } else {
73     // Check for `CON36-C`
74     Finder->addMatcher(
75 
76         ifStmt(
77             allOf(hasWaitDescendantC,
78                   unless(anyOf(hasDescendant(ifStmt(hasWaitDescendantC)),
79                                hasDescendant(whileStmt(hasWaitDescendantC)),
80                                hasDescendant(forStmt(hasWaitDescendantC)),
81                                hasDescendant(doStmt(hasWaitDescendantC)),
82                                hasParent(whileStmt()),
83                                hasParent(compoundStmt(hasParent(whileStmt()))),
84                                hasParent(forStmt()),
85                                hasParent(compoundStmt(hasParent(forStmt()))),
86                                hasParent(doStmt()),
87                                hasParent(compoundStmt(hasParent(doStmt())))))
88 
89                       ))
90 
91             ,
92         this);
93   }
94 }
95 
check(const MatchFinder::MatchResult & Result)96 void SpuriouslyWakeUpFunctionsCheck::check(
97     const MatchFinder::MatchResult &Result) {
98   const auto *MatchedWait = Result.Nodes.getNodeAs<CallExpr>("wait");
99   StringRef WaitName = MatchedWait->getDirectCallee()->getName();
100   diag(MatchedWait->getExprLoc(),
101        "'%0' should be placed inside a while statement %select{|or used with a "
102        "conditional parameter}1")
103       << WaitName << (WaitName != "cnd_wait" && WaitName != "cnd_timedwait");
104 }
105 } // namespace bugprone
106 } // namespace tidy
107 } // namespace clang
108