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