| 1 | //===-- ChromiumCheckModel.cpp ----------------------------------*- C++ -*-===// |
|---|---|
| 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 "clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h" |
| 10 | #include "clang/AST/Decl.h" |
| 11 | #include "clang/AST/DeclCXX.h" |
| 12 | #include "llvm/ADT/DenseSet.h" |
| 13 | |
| 14 | namespace clang { |
| 15 | namespace dataflow { |
| 16 | |
| 17 | /// Determines whether `D` is one of the methods used to implement Chromium's |
| 18 | /// `CHECK` macros. Populates `CheckDecls`, if empty. |
| 19 | static bool |
| 20 | isCheckLikeMethod(llvm::SmallDenseSet<const CXXMethodDecl *> &CheckDecls, |
| 21 | const CXXMethodDecl &D) { |
| 22 | // All of the methods of interest are static, so avoid any lookup for |
| 23 | // non-static methods (the common case). |
| 24 | if (!D.isStatic()) |
| 25 | return false; |
| 26 | |
| 27 | if (CheckDecls.empty()) { |
| 28 | // Attempt to initialize `CheckDecls` with the methods in class |
| 29 | // `CheckError`. |
| 30 | const CXXRecordDecl *ParentClass = D.getParent(); |
| 31 | if (ParentClass == nullptr || !ParentClass->getDeclName().isIdentifier() || |
| 32 | ParentClass->getName() != "CheckError") |
| 33 | return false; |
| 34 | |
| 35 | // Check whether namespace is "logging". |
| 36 | const auto *N = |
| 37 | dyn_cast_or_null<NamespaceDecl>(Val: ParentClass->getDeclContext()); |
| 38 | if (N == nullptr || !N->getDeclName().isIdentifier() || |
| 39 | N->getName() != "logging") |
| 40 | return false; |
| 41 | |
| 42 | // Check whether "logging" is a top-level namespace. |
| 43 | if (N->getParent() == nullptr || !N->getParent()->isTranslationUnit()) |
| 44 | return false; |
| 45 | |
| 46 | for (const CXXMethodDecl *M : ParentClass->methods()) |
| 47 | if (M->getDeclName().isIdentifier() && M->getName().ends_with(Suffix: "Check")) |
| 48 | CheckDecls.insert(V: M); |
| 49 | } |
| 50 | |
| 51 | return CheckDecls.contains(V: &D); |
| 52 | } |
| 53 | |
| 54 | bool ChromiumCheckModel::transfer(const CFGElement &Element, Environment &Env) { |
| 55 | auto CS = Element.getAs<CFGStmt>(); |
| 56 | if (!CS) |
| 57 | return false; |
| 58 | auto Stmt = CS->getStmt(); |
| 59 | if (const auto *Call = dyn_cast<CallExpr>(Val: Stmt)) { |
| 60 | if (const auto *M = |
| 61 | dyn_cast_or_null<CXXMethodDecl>(Val: Call->getDirectCallee())) { |
| 62 | if (isCheckLikeMethod(CheckDecls, D: *M)) { |
| 63 | // Mark this branch as unreachable. |
| 64 | Env.assume(Env.arena().makeLiteral(Value: false)); |
| 65 | return true; |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | return false; |
| 70 | } |
| 71 | |
| 72 | } // namespace dataflow |
| 73 | } // namespace clang |
| 74 |