xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp (revision df0c8f25145047731fb95b4ce7153ce6fb5b6f5d)
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::tidy::bugprone {
16 
registerMatchers(MatchFinder * Finder)17 void SpuriouslyWakeUpFunctionsCheck::registerMatchers(MatchFinder *Finder) {
18 
19   auto HasUniqueLock = hasDescendant(declRefExpr(
20       hasDeclaration(varDecl(hasType(recordDecl(classTemplateSpecializationDecl(
21           hasName("::std::unique_lock"),
22           hasTemplateArgument(
23               0, templateArgument(refersToType(qualType(hasDeclaration(
24                      cxxRecordDecl(hasName("::std::mutex"))))))))))))));
25 
26   auto HasWaitDescendantCpp = hasDescendant(
27       cxxMemberCallExpr(
28           anyOf(allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
29                           hasName("::std::condition_variable::wait"),
30                           parameterCountIs(1))))),
31                       onImplicitObjectArgument(
32                           declRefExpr(to(varDecl(hasType(references(recordDecl(
33                               hasName("::std::condition_variable")))))))),
34                       HasUniqueLock),
35                 allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
36                           hasName("::std::condition_variable::wait_for"),
37                           parameterCountIs(2))))),
38                       onImplicitObjectArgument(
39                           declRefExpr(to(varDecl(hasType(references(recordDecl(
40                               hasName("::std::condition_variable")))))))),
41                       HasUniqueLock),
42                 allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl(
43                           hasName("::std::condition_variable::wait_until"),
44                           parameterCountIs(2))))),
45                       onImplicitObjectArgument(
46                           declRefExpr(to(varDecl(hasType(references(recordDecl(
47                               hasName("::std::condition_variable")))))))),
48                       HasUniqueLock)
49 
50                     ))
51           .bind("wait"));
52 
53   auto HasWaitDescendantC = hasDescendant(
54       callExpr(callee(functionDecl(hasAnyName("cnd_wait", "cnd_timedwait"))))
55           .bind("wait"));
56   if (getLangOpts().CPlusPlus) {
57     // Check for `CON54-CPP`
58     Finder->addMatcher(
59         ifStmt(HasWaitDescendantCpp,
60                unless(hasDescendant(mapAnyOf(ifStmt, whileStmt, forStmt, doStmt)
61                                         .with(HasWaitDescendantCpp)))),
62         this);
63   } else {
64     // Check for `CON36-C`
65     Finder->addMatcher(
66         ifStmt(HasWaitDescendantC,
67                unless(anyOf(
68                    hasDescendant(mapAnyOf(ifStmt, whileStmt, forStmt, doStmt)
69                                      .with(HasWaitDescendantC)),
70                    hasParent(mapAnyOf(whileStmt, forStmt, doStmt)),
71                    hasParent(compoundStmt(
72                        hasParent(mapAnyOf(whileStmt, forStmt, doStmt))))))),
73         this);
74   }
75 }
76 
check(const MatchFinder::MatchResult & Result)77 void SpuriouslyWakeUpFunctionsCheck::check(
78     const MatchFinder::MatchResult &Result) {
79   const auto *MatchedWait = Result.Nodes.getNodeAs<CallExpr>("wait");
80   StringRef WaitName = MatchedWait->getDirectCallee()->getName();
81   diag(MatchedWait->getExprLoc(),
82        "'%0' should be placed inside a while statement %select{|or used with a "
83        "conditional parameter}1")
84       << WaitName << (WaitName != "cnd_wait" && WaitName != "cnd_timedwait");
85 }
86 } // namespace clang::tidy::bugprone
87