1 //===---- CheckerHelpers.cpp - Helper functions for checkers ----*- 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 // This file defines several static functions for use in checkers.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/Expr.h"
16 #include "clang/Lex/Preprocessor.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
18 #include <optional>
19
20 namespace clang {
21
22 namespace ento {
23
24 // Recursively find any substatements containing macros
containsMacro(const Stmt * S)25 bool containsMacro(const Stmt *S) {
26 if (S->getBeginLoc().isMacroID())
27 return true;
28
29 if (S->getEndLoc().isMacroID())
30 return true;
31
32 for (const Stmt *Child : S->children())
33 if (Child && containsMacro(Child))
34 return true;
35
36 return false;
37 }
38
39 // Recursively find any substatements containing enum constants
containsEnum(const Stmt * S)40 bool containsEnum(const Stmt *S) {
41 const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
42
43 if (DR && isa<EnumConstantDecl>(DR->getDecl()))
44 return true;
45
46 for (const Stmt *Child : S->children())
47 if (Child && containsEnum(Child))
48 return true;
49
50 return false;
51 }
52
53 // Recursively find any substatements containing static vars
containsStaticLocal(const Stmt * S)54 bool containsStaticLocal(const Stmt *S) {
55 const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
56
57 if (DR)
58 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
59 if (VD->isStaticLocal())
60 return true;
61
62 for (const Stmt *Child : S->children())
63 if (Child && containsStaticLocal(Child))
64 return true;
65
66 return false;
67 }
68
69 // Recursively find any substatements containing __builtin_offsetof
containsBuiltinOffsetOf(const Stmt * S)70 bool containsBuiltinOffsetOf(const Stmt *S) {
71 if (isa<OffsetOfExpr>(S))
72 return true;
73
74 for (const Stmt *Child : S->children())
75 if (Child && containsBuiltinOffsetOf(Child))
76 return true;
77
78 return false;
79 }
80
81 // Extract lhs and rhs from assignment statement
82 std::pair<const clang::VarDecl *, const clang::Expr *>
parseAssignment(const Stmt * S)83 parseAssignment(const Stmt *S) {
84 const VarDecl *VD = nullptr;
85 const Expr *RHS = nullptr;
86
87 if (auto Assign = dyn_cast_or_null<BinaryOperator>(S)) {
88 if (Assign->isAssignmentOp()) {
89 // Ordinary assignment
90 RHS = Assign->getRHS();
91 if (auto DE = dyn_cast_or_null<DeclRefExpr>(Assign->getLHS()))
92 VD = dyn_cast_or_null<VarDecl>(DE->getDecl());
93 }
94 } else if (auto PD = dyn_cast_or_null<DeclStmt>(S)) {
95 // Initialization
96 assert(PD->isSingleDecl() && "We process decls one by one");
97 VD = cast<VarDecl>(PD->getSingleDecl());
98 RHS = VD->getAnyInitializer();
99 }
100
101 return std::make_pair(VD, RHS);
102 }
103
getNullabilityAnnotation(QualType Type)104 Nullability getNullabilityAnnotation(QualType Type) {
105 const auto *AttrType = Type->getAs<AttributedType>();
106 if (!AttrType)
107 return Nullability::Unspecified;
108 if (AttrType->getAttrKind() == attr::TypeNullable)
109 return Nullability::Nullable;
110 else if (AttrType->getAttrKind() == attr::TypeNonNull)
111 return Nullability::Nonnull;
112 return Nullability::Unspecified;
113 }
114
tryExpandAsInteger(StringRef Macro,const Preprocessor & PP)115 std::optional<int> tryExpandAsInteger(StringRef Macro, const Preprocessor &PP) {
116 const auto *MacroII = PP.getIdentifierInfo(Macro);
117 if (!MacroII)
118 return std::nullopt;
119 const MacroInfo *MI = PP.getMacroInfo(MacroII);
120 if (!MI)
121 return std::nullopt;
122
123 // Filter out parens.
124 std::vector<Token> FilteredTokens;
125 FilteredTokens.reserve(MI->tokens().size());
126 for (auto &T : MI->tokens())
127 if (!T.isOneOf(tok::l_paren, tok::r_paren))
128 FilteredTokens.push_back(T);
129
130 // Parse an integer at the end of the macro definition.
131 const Token &T = FilteredTokens.back();
132 // FIXME: EOF macro token coming from a PCH file on macOS while marked as
133 // literal, doesn't contain any literal data
134 if (!T.isLiteral() || !T.getLiteralData())
135 return std::nullopt;
136 StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
137 llvm::APInt IntValue;
138 constexpr unsigned AutoSenseRadix = 0;
139 if (ValueStr.getAsInteger(AutoSenseRadix, IntValue))
140 return std::nullopt;
141
142 // Parse an optional minus sign.
143 size_t Size = FilteredTokens.size();
144 if (Size >= 2) {
145 if (FilteredTokens[Size - 2].is(tok::minus))
146 IntValue = -IntValue;
147 }
148
149 return IntValue.getSExtValue();
150 }
151
operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,bool IsBinary)152 OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,
153 bool IsBinary) {
154 llvm::StringMap<BinaryOperatorKind> BinOps{
155 #define BINARY_OPERATION(Name, Spelling) {Spelling, BO_##Name},
156 #include "clang/AST/OperationKinds.def"
157 };
158 llvm::StringMap<UnaryOperatorKind> UnOps{
159 #define UNARY_OPERATION(Name, Spelling) {Spelling, UO_##Name},
160 #include "clang/AST/OperationKinds.def"
161 };
162
163 switch (OOK) {
164 #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
165 case OO_##Name: \
166 if (IsBinary) { \
167 auto BinOpIt = BinOps.find(Spelling); \
168 if (BinOpIt != BinOps.end()) \
169 return OperatorKind(BinOpIt->second); \
170 else \
171 llvm_unreachable("operator was expected to be binary but is not"); \
172 } else { \
173 auto UnOpIt = UnOps.find(Spelling); \
174 if (UnOpIt != UnOps.end()) \
175 return OperatorKind(UnOpIt->second); \
176 else \
177 llvm_unreachable("operator was expected to be unary but is not"); \
178 } \
179 break;
180 #include "clang/Basic/OperatorKinds.def"
181 default:
182 llvm_unreachable("unexpected operator kind");
183 }
184 }
185
getPointeeVal(SVal PtrSVal,ProgramStateRef State)186 std::optional<SVal> getPointeeVal(SVal PtrSVal, ProgramStateRef State) {
187 if (const auto *Ptr = PtrSVal.getAsRegion()) {
188 return State->getSVal(Ptr);
189 }
190 return std::nullopt;
191 }
192
193 } // namespace ento
194 } // namespace clang
195