//===--- NoSuspendWithLockCheck.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 "NoSuspendWithLockCheck.h" #include "../utils/ExprSequence.h" #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Analysis/CFG.h" using namespace clang::ast_matchers; namespace clang::tidy::cppcoreguidelines { void NoSuspendWithLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "LockGuards", LockGuards); } void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) { auto LockType = elaboratedType(namesType(templateSpecializationType( hasDeclaration(namedDecl(matchers::matchesAnyListedName( utils::options::parseStringList(LockGuards))))))); StatementMatcher Lock = declStmt(has(varDecl(hasType(LockType)).bind("lock-decl"))) .bind("lock-decl-stmt"); Finder->addMatcher( expr(anyOf(coawaitExpr(), coyieldExpr(), dependentCoawaitExpr()), forCallable(functionDecl().bind("function")), unless(isInTemplateInstantiation()), hasAncestor( compoundStmt(has(Lock), forCallable(equalsBoundNode("function"))) .bind("block"))) .bind("suspend"), this); } void NoSuspendWithLockCheck::check(const MatchFinder::MatchResult &Result) { const auto *Block = Result.Nodes.getNodeAs("block"); const auto *Suspend = Result.Nodes.getNodeAs("suspend"); const auto *LockDecl = Result.Nodes.getNodeAs("lock-decl"); const auto *LockStmt = Result.Nodes.getNodeAs("lock-decl-stmt"); if (!Block || !Suspend || !LockDecl || !LockStmt) return; ASTContext &Context = *Result.Context; CFG::BuildOptions Options; Options.AddImplicitDtors = true; Options.AddTemporaryDtors = true; std::unique_ptr TheCFG = CFG::buildCFG( nullptr, const_cast(Block), &Context, Options); if (!TheCFG) return; utils::ExprSequence Sequence(TheCFG.get(), Block, &Context); const Stmt *LastBlockStmt = Block->body_back(); if (Sequence.inSequence(LockStmt, Suspend) && (Suspend == LastBlockStmt || Sequence.inSequence(Suspend, LastBlockStmt))) { diag(Suspend->getBeginLoc(), "coroutine suspended with lock %0 held") << LockDecl; } } } // namespace clang::tidy::cppcoreguidelines