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