1 //===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines several static functions for use in checkers.
11 //===----------------------------------------------------------------------===//
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"
24 // Recursively find any substatements containing macros
25 bool containsMacro(const Stmt
*S
) {
26 if (S
->getBeginLoc().isMacroID())
29 if (S
->getEndLoc().isMacroID())
32 for (const Stmt
*Child
: S
->children())
33 if (Child
&& containsMacro(Child
))
39 // Recursively find any substatements containing enum constants
40 bool containsEnum(const Stmt
*S
) {
41 const DeclRefExpr
*DR
= dyn_cast
<DeclRefExpr
>(S
);
43 if (DR
&& isa
<EnumConstantDecl
>(DR
->getDecl()))
46 for (const Stmt
*Child
: S
->children())
47 if (Child
&& containsEnum(Child
))
53 // Recursively find any substatements containing static vars
54 bool containsStaticLocal(const Stmt
*S
) {
55 const DeclRefExpr
*DR
= dyn_cast
<DeclRefExpr
>(S
);
58 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl()))
59 if (VD
->isStaticLocal())
62 for (const Stmt
*Child
: S
->children())
63 if (Child
&& containsStaticLocal(Child
))
69 // Recursively find any substatements containing __builtin_offsetof
70 bool containsBuiltinOffsetOf(const Stmt
*S
) {
71 if (isa
<OffsetOfExpr
>(S
))
74 for (const Stmt
*Child
: S
->children())
75 if (Child
&& containsBuiltinOffsetOf(Child
))
81 // Extract lhs and rhs from assignment statement
82 std::pair
<const clang::VarDecl
*, const clang::Expr
*>
83 parseAssignment(const Stmt
*S
) {
84 const VarDecl
*VD
= nullptr;
85 const Expr
*RHS
= nullptr;
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());
94 } else if (auto PD
= dyn_cast_or_null
<DeclStmt
>(S
)) {
96 assert(PD
->isSingleDecl() && "We process decls one by one");
97 VD
= cast
<VarDecl
>(PD
->getSingleDecl());
98 RHS
= VD
->getAnyInitializer();
101 return std::make_pair(VD
, RHS
);
104 Nullability
getNullabilityAnnotation(QualType Type
) {
105 const auto *AttrType
= Type
->getAs
<AttributedType
>();
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
;
115 std::optional
<int> tryExpandAsInteger(StringRef Macro
, const Preprocessor
&PP
) {
116 const auto *MacroII
= PP
.getIdentifierInfo(Macro
);
119 const MacroInfo
*MI
= PP
.getMacroInfo(MacroII
);
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
);
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())
136 StringRef ValueStr
= StringRef(T
.getLiteralData(), T
.getLength());
137 llvm::APInt IntValue
;
138 constexpr unsigned AutoSenseRadix
= 0;
139 if (ValueStr
.getAsInteger(AutoSenseRadix
, IntValue
))
142 // Parse an optional minus sign.
143 size_t Size
= FilteredTokens
.size();
145 if (FilteredTokens
[Size
- 2].is(tok::minus
))
146 IntValue
= -IntValue
;
149 return IntValue
.getSExtValue();
152 OperatorKind
operationKindFromOverloadedOperator(OverloadedOperatorKind OOK
,
154 llvm::StringMap
<BinaryOperatorKind
> BinOps
{
155 #define BINARY_OPERATION(Name, Spelling) {Spelling, BO_##Name},
156 #include "clang/AST/OperationKinds.def"
158 llvm::StringMap
<UnaryOperatorKind
> UnOps
{
159 #define UNARY_OPERATION(Name, Spelling) {Spelling, UO_##Name},
160 #include "clang/AST/OperationKinds.def"
164 #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
167 auto BinOpIt = BinOps.find(Spelling); \
168 if (BinOpIt != BinOps.end()) \
169 return OperatorKind(BinOpIt->second); \
171 llvm_unreachable("operator was expected to be binary but is not"); \
173 auto UnOpIt = UnOps.find(Spelling); \
174 if (UnOpIt != UnOps.end()) \
175 return OperatorKind(UnOpIt->second); \
177 llvm_unreachable("operator was expected to be unary but is not"); \
180 #include "clang/Basic/OperatorKinds.def"
182 llvm_unreachable("unexpected operator kind");
186 std::optional
<SVal
> getPointeeVal(SVal PtrSVal
, ProgramStateRef State
) {
187 if (const auto *Ptr
= PtrSVal
.getAsRegion()) {
188 return State
->getSVal(Ptr
);
193 bool isWithinStdNamespace(const Decl
*D
) {
194 const DeclContext
*DC
= D
->getDeclContext();
196 if (const auto *NS
= dyn_cast
<NamespaceDecl
>(DC
);
197 NS
&& NS
->isStdNamespace())
199 DC
= DC
->getParent();