1 | //===-- ASTOps.h -------------------------------*- 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 | // Operations on AST nodes that are used in flow-sensitive analysis. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ASTOPS_H |
14 | #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ASTOPS_H |
15 | |
16 | #include "clang/AST/Decl.h" |
17 | #include "clang/AST/Expr.h" |
18 | #include "clang/AST/RecursiveASTVisitor.h" |
19 | #include "clang/AST/Type.h" |
20 | #include "clang/Analysis/FlowSensitive/StorageLocation.h" |
21 | #include "llvm/ADT/DenseSet.h" |
22 | #include "llvm/ADT/SetVector.h" |
23 | |
24 | namespace clang { |
25 | namespace dataflow { |
26 | |
27 | /// Skip past nodes that the CFG does not emit. These nodes are invisible to |
28 | /// flow-sensitive analysis, and should be ignored as they will effectively not |
29 | /// exist. |
30 | /// |
31 | /// * `ParenExpr` - The CFG takes the operator precedence into account, but |
32 | /// otherwise omits the node afterwards. |
33 | /// |
34 | /// * `ExprWithCleanups` - The CFG will generate the appropriate calls to |
35 | /// destructors and then omit the node. |
36 | /// |
37 | const Expr &ignoreCFGOmittedNodes(const Expr &E); |
38 | const Stmt &ignoreCFGOmittedNodes(const Stmt &S); |
39 | |
40 | /// A set of `FieldDecl *`. Use `SmallSetVector` to guarantee deterministic |
41 | /// iteration order. |
42 | using FieldSet = llvm::SmallSetVector<const FieldDecl *, 4>; |
43 | |
44 | /// Returns the set of all fields in the type. |
45 | FieldSet getObjectFields(QualType Type); |
46 | |
47 | /// Returns whether `Fields` and `FieldLocs` contain the same fields. |
48 | bool containsSameFields(const FieldSet &Fields, |
49 | const RecordStorageLocation::FieldToLoc &FieldLocs); |
50 | |
51 | /// Helper class for initialization of a record with an `InitListExpr`. |
52 | /// `InitListExpr::inits()` contains the initializers for both the base classes |
53 | /// and the fields of the record; this helper class separates these out into two |
54 | /// different lists. In addition, it deals with special cases associated with |
55 | /// unions. |
56 | class RecordInitListHelper { |
57 | public: |
58 | // `InitList` must have record type. |
59 | RecordInitListHelper(const InitListExpr *InitList); |
60 | RecordInitListHelper(const CXXParenListInitExpr *ParenInitList); |
61 | |
62 | // Base classes with their associated initializer expressions. |
63 | ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const { |
64 | return BaseInits; |
65 | } |
66 | |
67 | // Fields with their associated initializer expressions. |
68 | ArrayRef<std::pair<const FieldDecl *, Expr *>> field_inits() const { |
69 | return FieldInits; |
70 | } |
71 | |
72 | private: |
73 | RecordInitListHelper(QualType Ty, std::vector<const FieldDecl *> Fields, |
74 | ArrayRef<Expr *> Inits); |
75 | |
76 | SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits; |
77 | SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits; |
78 | |
79 | // We potentially synthesize an `ImplicitValueInitExpr` for unions. It's a |
80 | // member variable because we store a pointer to it in `FieldInits`. |
81 | std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion; |
82 | }; |
83 | |
84 | /// Specialization of `RecursiveASTVisitor` that visits those nodes that are |
85 | /// relevant to the dataflow analysis; generally, these are the ones that also |
86 | /// appear in the CFG. |
87 | /// To start the traversal, call `TraverseStmt()` on the statement or body of |
88 | /// the function to analyze. Don't call `TraverseDecl()` on the function itself; |
89 | /// this won't work as `TraverseDecl()` contains code to avoid traversing nested |
90 | /// functions. |
91 | template <class Derived> |
92 | class AnalysisASTVisitor : public RecursiveASTVisitor<Derived> { |
93 | public: |
94 | bool shouldVisitImplicitCode() { return true; } |
95 | |
96 | bool shouldVisitLambdaBody() const { return false; } |
97 | |
98 | bool TraverseDecl(Decl *D) { |
99 | // Don't traverse nested record or function declarations. |
100 | // - We won't be analyzing code contained in these anyway |
101 | // - We don't model fields that are used only in these nested declaration, |
102 | // so trying to propagate a result object to initializers of such fields |
103 | // would cause an error. |
104 | if (isa_and_nonnull<RecordDecl>(Val: D) || isa_and_nonnull<FunctionDecl>(Val: D)) |
105 | return true; |
106 | |
107 | return RecursiveASTVisitor<Derived>::TraverseDecl(D); |
108 | } |
109 | |
110 | // Don't traverse expressions in unevaluated contexts, as we don't model |
111 | // fields that are only used in these. |
112 | // Note: The operand of the `noexcept` operator is an unevaluated operand, but |
113 | // nevertheless it appears in the Clang CFG, so we don't exclude it here. |
114 | bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; } |
115 | bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; } |
116 | bool TraverseCXXTypeidExpr(CXXTypeidExpr *TIE) { |
117 | if (TIE->isPotentiallyEvaluated()) |
118 | return RecursiveASTVisitor<Derived>::TraverseCXXTypeidExpr(TIE); |
119 | return true; |
120 | } |
121 | bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) { |
122 | return true; |
123 | } |
124 | |
125 | bool TraverseBindingDecl(BindingDecl *BD) { |
126 | // `RecursiveASTVisitor` doesn't traverse holding variables for |
127 | // `BindingDecl`s by itself, so we need to tell it to. |
128 | if (VarDecl *HoldingVar = BD->getHoldingVar()) |
129 | TraverseDecl(D: HoldingVar); |
130 | return RecursiveASTVisitor<Derived>::TraverseBindingDecl(BD); |
131 | } |
132 | }; |
133 | |
134 | /// A collection of several types of declarations, all referenced from the same |
135 | /// function. |
136 | struct ReferencedDecls { |
137 | /// Non-static member variables. |
138 | FieldSet Fields; |
139 | /// All variables with static storage duration, notably including static |
140 | /// member variables and static variables declared within a function. |
141 | llvm::DenseSet<const VarDecl *> Globals; |
142 | /// Free functions and member functions which are referenced (but not |
143 | /// necessarily called). |
144 | llvm::DenseSet<const FunctionDecl *> Functions; |
145 | }; |
146 | |
147 | /// Returns declarations that are declared in or referenced from `FD`. |
148 | ReferencedDecls getReferencedDecls(const FunctionDecl &FD); |
149 | |
150 | /// Returns declarations that are declared in or referenced from `S`. |
151 | ReferencedDecls getReferencedDecls(const Stmt &S); |
152 | |
153 | } // namespace dataflow |
154 | } // namespace clang |
155 | |
156 | #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ASTOPS_H |
157 | |