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
20namespace clang {
21
22namespace ento {
23
24// Recursively find any substatements containing macros
25bool 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(S: Child))
34 return true;
35
36 return false;
37}
38
39// Recursively find any substatements containing enum constants
40bool containsEnum(const Stmt *S) {
41 const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Val: S);
42
43 if (DR && isa<EnumConstantDecl>(Val: DR->getDecl()))
44 return true;
45
46 for (const Stmt *Child : S->children())
47 if (Child && containsEnum(S: Child))
48 return true;
49
50 return false;
51}
52
53// Recursively find any substatements containing static vars
54bool containsStaticLocal(const Stmt *S) {
55 const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Val: S);
56
57 if (DR)
58 if (const VarDecl *VD = dyn_cast<VarDecl>(Val: DR->getDecl()))
59 if (VD->isStaticLocal())
60 return true;
61
62 for (const Stmt *Child : S->children())
63 if (Child && containsStaticLocal(S: Child))
64 return true;
65
66 return false;
67}
68
69// Recursively find any substatements containing __builtin_offsetof
70bool containsBuiltinOffsetOf(const Stmt *S) {
71 if (isa<OffsetOfExpr>(Val: S))
72 return true;
73
74 for (const Stmt *Child : S->children())
75 if (Child && containsBuiltinOffsetOf(S: Child))
76 return true;
77
78 return false;
79}
80
81// Extract lhs and rhs from assignment statement
82std::pair<const clang::VarDecl *, const clang::Expr *>
83parseAssignment(const Stmt *S) {
84 const VarDecl *VD = nullptr;
85 const Expr *RHS = nullptr;
86
87 if (auto Assign = dyn_cast_or_null<BinaryOperator>(Val: S)) {
88 if (Assign->isAssignmentOp()) {
89 // Ordinary assignment
90 RHS = Assign->getRHS();
91 if (auto DE = dyn_cast_or_null<DeclRefExpr>(Val: Assign->getLHS()))
92 VD = dyn_cast_or_null<VarDecl>(Val: DE->getDecl());
93 }
94 } else if (auto PD = dyn_cast_or_null<DeclStmt>(Val: S)) {
95 // Initialization
96 assert(PD->isSingleDecl() && "We process decls one by one");
97 VD = cast<VarDecl>(Val: PD->getSingleDecl());
98 RHS = VD->getAnyInitializer();
99 }
100
101 return std::make_pair(x&: VD, y&: RHS);
102}
103
104Nullability 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
115std::optional<int> tryExpandAsInteger(StringRef Macro, const Preprocessor &PP) {
116 const auto *MacroII = PP.getIdentifierInfo(Name: Macro);
117 if (!MacroII)
118 return std::nullopt;
119 const MacroInfo *MI = PP.getMacroInfo(II: MacroII);
120 if (!MI)
121 return std::nullopt;
122
123 // Filter out parens.
124 std::vector<Token> FilteredTokens;
125 FilteredTokens.reserve(n: MI->tokens().size());
126 for (auto &T : MI->tokens())
127 if (!T.isOneOf(Ks: tok::l_paren, Ks: tok::r_paren))
128 FilteredTokens.push_back(x: T);
129
130 // Parse an integer at the end of the macro definition.
131 const Token &T = FilteredTokens.back();
132
133 if (!T.isLiteral())
134 return std::nullopt;
135
136 bool InvalidSpelling = false;
137 SmallVector<char> Buffer(T.getLength());
138 // `Preprocessor::getSpelling` can get the spelling of the token regardless of
139 // whether the macro is defined in a PCH or not:
140 StringRef ValueStr = PP.getSpelling(Tok: T, Buffer, Invalid: &InvalidSpelling);
141
142 if (InvalidSpelling)
143 return std::nullopt;
144
145 llvm::APSInt IntValue(/*BitWidth=*/0, /*isUnsigned=*/true);
146 constexpr unsigned AutoSenseRadix = 0;
147 if (ValueStr.getAsInteger(Radix: AutoSenseRadix,
148 Result&: static_cast<llvm::APInt &>(IntValue)))
149 return std::nullopt;
150
151 // Parse an optional minus sign.
152 size_t Size = FilteredTokens.size();
153 if (Size >= 2) {
154 if (FilteredTokens[Size - 2].is(K: tok::minus)) {
155 // Make sure there's space for a sign bit
156 if (IntValue.isSignBitSet())
157 IntValue = IntValue.extend(width: IntValue.getBitWidth() + 1);
158 IntValue.setIsUnsigned(false);
159 IntValue = -IntValue;
160 }
161 }
162
163 return IntValue.getExtValue();
164}
165
166OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,
167 bool IsBinary) {
168 llvm::StringMap<BinaryOperatorKind> BinOps{
169#define BINARY_OPERATION(Name, Spelling) {Spelling, BO_##Name},
170#include "clang/AST/OperationKinds.def"
171 };
172 llvm::StringMap<UnaryOperatorKind> UnOps{
173#define UNARY_OPERATION(Name, Spelling) {Spelling, UO_##Name},
174#include "clang/AST/OperationKinds.def"
175 };
176
177 switch (OOK) {
178#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
179 case OO_##Name: \
180 if (IsBinary) { \
181 auto BinOpIt = BinOps.find(Spelling); \
182 if (BinOpIt != BinOps.end()) \
183 return OperatorKind(BinOpIt->second); \
184 else \
185 llvm_unreachable("operator was expected to be binary but is not"); \
186 } else { \
187 auto UnOpIt = UnOps.find(Spelling); \
188 if (UnOpIt != UnOps.end()) \
189 return OperatorKind(UnOpIt->second); \
190 else \
191 llvm_unreachable("operator was expected to be unary but is not"); \
192 } \
193 break;
194#include "clang/Basic/OperatorKinds.def"
195 default:
196 llvm_unreachable("unexpected operator kind");
197 }
198}
199
200std::optional<SVal> getPointeeVal(SVal PtrSVal, ProgramStateRef State) {
201 if (const auto *Ptr = PtrSVal.getAsRegion()) {
202 return State->getSVal(R: Ptr);
203 }
204 return std::nullopt;
205}
206
207bool isWithinStdNamespace(const Decl *D) {
208 const DeclContext *DC = D->getDeclContext();
209 while (DC) {
210 if (const auto *NS = dyn_cast<NamespaceDecl>(Val: DC);
211 NS && NS->isStdNamespace())
212 return true;
213 DC = DC->getParent();
214 }
215 return false;
216}
217
218} // namespace ento
219} // namespace clang
220