[clang] [clang][analyzer] Introduce MutexModeling checker (PR #111381)
DonĂ¡t Nagy via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 8 06:44:37 PDT 2024
================
@@ -0,0 +1,139 @@
+//===--- MutexRegionExtractor.h - Modeling of mutexes ---------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines modeling checker for tracking mutex states.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_MUTEXREGIONEXTRACTOR_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MUTEXMODELING_MUTEXREGIONEXTRACTOR_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include <variant>
+
+namespace clang::ento::mutex_modeling {
+
+// Extracts the mutex region from the first argument of a function call
+class FirstArgMutexExtractor {
+ CallDescription CD;
+
+public:
+ template <typename T>
+ FirstArgMutexExtractor(T &&CD) : CD(std::forward<T>(CD)) {}
+
+ [[nodiscard]] bool matches(const CallEvent &Call) const {
+ return CD.matches(Call);
+ }
+
+ [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call) const {
+ return Call.getArgSVal(0).getAsRegion();
+ }
+};
+
+// Extracts the mutex region from the 'this' pointer of a member function call
+class MemberMutexExtractor {
+ CallDescription CD;
+
+public:
+ template <typename T>
+ MemberMutexExtractor(T &&CD) : CD(std::forward<T>(CD)) {}
+
+ [[nodiscard]] bool matches(const CallEvent &Call) const {
+ return CD.matches(Call);
+ }
+
+ [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call) const {
+ return llvm::cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
+ }
+};
+
+// Template class for extracting mutex regions from RAII-style lock/unlock
+// operations
+template <bool IsLock> class RAIIMutexExtractor {
+ mutable const clang::IdentifierInfo *Guard{};
+ mutable bool IdentifierInfoInitialized{};
+ mutable llvm::SmallString<32> GuardName{};
+
+ void initIdentifierInfo(const CallEvent &Call) const {
+ if (!IdentifierInfoInitialized) {
+ // In case of checking C code, or when the corresponding headers are not
+ // included, we might end up query the identifier table every time when
+ // this function is called instead of early returning it. To avoid this,
+ // a bool variable (IdentifierInfoInitialized) is used and the function
+ // will be run only once.
+ const auto &ASTCtx = Call.getState()->getStateManager().getContext();
+ Guard = &ASTCtx.Idents.get(GuardName);
+ }
+ }
+
+ template <typename T> bool matchesImpl(const CallEvent &Call) const {
+ const T *C = llvm::dyn_cast<T>(&Call);
+ if (!C)
+ return false;
+ const clang::IdentifierInfo *II =
+ llvm::cast<clang::CXXRecordDecl>(C->getDecl()->getParent())
+ ->getIdentifier();
+ return II == Guard;
+ }
+
+public:
+ RAIIMutexExtractor(llvm::StringRef GuardName) : GuardName(GuardName) {}
+ [[nodiscard]] bool matches(const CallEvent &Call) const {
+ initIdentifierInfo(Call);
+ if constexpr (IsLock) {
+ return matchesImpl<CXXConstructorCall>(Call);
+ } else {
+ return matchesImpl<CXXDestructorCall>(Call);
+ }
+ }
+ [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call) const {
+ const MemRegion *MutexRegion = nullptr;
+ if constexpr (IsLock) {
+ if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
+ MutexRegion = Object->getAsRegion();
+ }
+ } else {
+ MutexRegion =
+ llvm::cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
+ }
+ return MutexRegion;
+ }
+};
+
+// Specializations for RAII-style lock and release operations
+using RAIILockExtractor = RAIIMutexExtractor<true>;
+using RAIIReleaseExtractor = RAIIMutexExtractor<false>;
+
+// Variant type that can hold any of the mutex region extractor types
+using MutexRegionExtractor =
+ std::variant<FirstArgMutexExtractor, MemberMutexExtractor,
+ RAIILockExtractor, RAIIReleaseExtractor>;
+
+// Helper functions for working with MutexRegionExtractor variant
+inline const MemRegion *getRegion(const MutexRegionExtractor &Extractor,
+ const CallEvent &Call) {
+ return std::visit(
+ [&Call](auto &&Descriptor) { return Descriptor.getRegion(Call); },
+ Extractor);
+}
+
+inline bool operator==(const MutexRegionExtractor &LHS,
+ const MutexRegionExtractor &RHS) {
+ return std::visit([](auto &&LHS, auto &&RHS) { return LHS == RHS; }, LHS,
+ RHS);
+}
----------------
NagyDonat wrote:
Do you need to define this explicitly? It seems that in C++17 or later, `std::variant` has `operator==` and `operator!=` with the natural semantics that we want here: https://en.cppreference.com/w/cpp/utility/variant/operator_cmp .
https://github.com/llvm/llvm-project/pull/111381
More information about the cfe-commits
mailing list