xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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