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