Coverage Report

Created: 2023-11-11 10:31

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- SourceCodeBuilder.cpp ----------------------------------*- 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
#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10
#include "clang/AST/ASTContext.h"
11
#include "clang/AST/Expr.h"
12
#include "clang/AST/ExprCXX.h"
13
#include "clang/ASTMatchers/ASTMatchFinder.h"
14
#include "clang/ASTMatchers/ASTMatchers.h"
15
#include "clang/Tooling/Transformer/SourceCode.h"
16
#include "llvm/ADT/Twine.h"
17
#include <string>
18
19
using namespace clang;
20
using namespace tooling;
21
22
78
const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23
78
  const Expr *Expr = E.IgnoreImplicit();
24
78
  if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
25
2
    if (CE->getNumArgs() > 0 &&
26
2
        CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
27
2
      return CE->getArg(0)->IgnoreImplicit();
28
2
  }
29
76
  return Expr;
30
78
}
31
32
53
bool tooling::mayEverNeedParens(const Expr &E) {
33
53
  const Expr *Expr = reallyIgnoreImplicit(E);
34
  // We always want parens around unary, binary, and ternary operators, because
35
  // they are lower precedence.
36
53
  if (isa<UnaryOperator>(Expr) || 
isa<BinaryOperator>(Expr)50
||
37
53
      
isa<AbstractConditionalOperator>(Expr)42
)
38
14
    return true;
39
40
  // We need parens around calls to all overloaded operators except: function
41
  // calls, subscripts, and expressions that are already part of an (implicit)
42
  // call to operator->. These latter are all in the same precedence level as
43
  // dot/arrow and that level is left associative, so they don't need parens
44
  // when appearing on the left.
45
39
  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
46
3
    return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47
3
           Op->getOperator() != OO_Arrow;
48
49
36
  return false;
50
39
}
51
52
25
bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53
25
  const Expr *Expr = reallyIgnoreImplicit(E);
54
25
  if (isa<BinaryOperator>(Expr) || 
isa<AbstractConditionalOperator>(Expr)19
)
55
7
    return true;
56
57
18
  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
58
3
    return Op->getNumArgs() == 2 && 
Op->getOperator() != OO_PlusPlus2
&&
59
3
           
Op->getOperator() != OO_MinusMinus2
&&
Op->getOperator() != OO_Call2
&&
60
3
           
Op->getOperator() != OO_Subscript2
;
61
62
15
  return false;
63
18
}
64
65
23
bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
66
23
  using namespace ast_matchers;
67
23
  const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
68
23
      recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
69
23
          "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
70
23
          "::std::optional", "::absl::optional", "::llvm::Optional",
71
23
          "absl::StatusOr", "::llvm::Expected"))))));
72
23
  return match(PointerLikeTy, Ty, Context).size() > 0;
73
23
}
74
75
std::optional<std::string> tooling::buildParens(const Expr &E,
76
8
                                                const ASTContext &Context) {
77
8
  StringRef Text = getText(E, Context);
78
8
  if (Text.empty())
79
0
    return std::nullopt;
80
8
  if (mayEverNeedParens(E))
81
3
    return ("(" + Text + ")").str();
82
5
  return Text.str();
83
8
}
84
85
std::optional<std::string>
86
12
tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87
12
  if (const auto *Op = dyn_cast<UnaryOperator>(&E))
88
4
    if (Op->getOpcode() == UO_AddrOf) {
89
      // Strip leading '&'.
90
4
      StringRef Text =
91
4
          getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92
4
      if (Text.empty())
93
0
        return std::nullopt;
94
4
      return Text.str();
95
4
    }
96
97
8
  StringRef Text = getText(E, Context);
98
8
  if (Text.empty())
99
0
    return std::nullopt;
100
  // Add leading '*'.
101
8
  if (needParensAfterUnaryOperator(E))
102
3
    return ("*(" + Text + ")").str();
103
5
  return ("*" + Text).str();
104
8
}
105
106
std::optional<std::string> tooling::buildAddressOf(const Expr &E,
107
11
                                                   const ASTContext &Context) {
108
11
  if (E.isImplicitCXXThis())
109
1
    return std::string("this");
110
10
  if (const auto *Op = dyn_cast<UnaryOperator>(&E))
111
4
    if (Op->getOpcode() == UO_Deref) {
112
      // Strip leading '*'.
113
4
      StringRef Text =
114
4
          getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115
4
      if (Text.empty())
116
0
        return std::nullopt;
117
4
      return Text.str();
118
4
    }
119
  // Add leading '&'.
120
6
  StringRef Text = getText(E, Context);
121
6
  if (Text.empty())
122
0
    return std::nullopt;
123
6
  if (needParensAfterUnaryOperator(E)) {
124
2
    return ("&(" + Text + ")").str();
125
2
  }
126
4
  return ("&" + Text).str();
127
6
}
128
129
// Append the appropriate access operation (syntactically) to `E`, assuming `E`
130
// is a non-pointer value.
131
static std::optional<std::string>
132
15
buildAccessForValue(const Expr &E, const ASTContext &Context) {
133
15
  if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
134
7
    if (Op->getOpcode() == UO_Deref) {
135
      // Strip leading '*', add following '->'.
136
7
      const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137
7
      StringRef DerefText = getText(*SubExpr, Context);
138
7
      if (DerefText.empty())
139
0
        return std::nullopt;
140
7
      if (needParensBeforeDotOrArrow(*SubExpr))
141
2
        return ("(" + DerefText + ")->").str();
142
5
      return (DerefText + "->").str();
143
7
    }
144
145
  // Add following '.'.
146
8
  StringRef Text = getText(E, Context);
147
8
  if (Text.empty())
148
0
    return std::nullopt;
149
8
  if (needParensBeforeDotOrArrow(E)) {
150
3
    return ("(" + Text + ").").str();
151
3
  }
152
5
  return (Text + ".").str();
153
8
}
154
155
// Append the appropriate access operation (syntactically) to `E`, assuming `E`
156
// is a pointer value.
157
static std::optional<std::string>
158
20
buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159
20
  if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
160
8
    if (Op->getOpcode() == UO_AddrOf) {
161
      // Strip leading '&', add following '.'.
162
8
      const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163
8
      StringRef DerefText = getText(*SubExpr, Context);
164
8
      if (DerefText.empty())
165
0
        return std::nullopt;
166
8
      if (needParensBeforeDotOrArrow(*SubExpr))
167
2
        return ("(" + DerefText + ").").str();
168
6
      return (DerefText + ".").str();
169
8
    }
170
171
  // Add following '->'.
172
12
  StringRef Text = getText(E, Context);
173
12
  if (Text.empty())
174
0
    return std::nullopt;
175
12
  if (needParensBeforeDotOrArrow(E))
176
2
    return ("(" + Text + ")->").str();
177
10
  return (Text + "->").str();
178
12
}
179
180
std::optional<std::string> tooling::buildDot(const Expr &E,
181
5
                                             const ASTContext &Context) {
182
5
  return buildAccessForValue(E, Context);
183
5
}
184
185
std::optional<std::string> tooling::buildArrow(const Expr &E,
186
5
                                               const ASTContext &Context) {
187
5
  return buildAccessForPointer(E, Context);
188
5
}
189
190
// If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
191
// `O`. Otherwise, returns `nullptr`.
192
static const Expr *maybeGetOperatorObjectArg(const Expr &E,
193
25
                                             OverloadedOperatorKind K) {
194
25
  if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {
195
6
    if (OpCall->getOperator() == K && 
OpCall->getNumArgs() == 15
)
196
5
      return OpCall->getArg(0);
197
6
  }
198
20
  return nullptr;
199
25
}
200
201
17
static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
202
17
  switch (C) {
203
3
  case PLTClass::Value:
204
3
    return false;
205
14
  case PLTClass::Pointer:
206
14
    return isKnownPointerLikeType(Ty, Context);
207
17
  }
208
0
  llvm_unreachable("Unknown PLTClass enum");
209
0
}
210
211
// FIXME: move over the other `maybe` functionality from Stencil. Should all be
212
// in one place.
213
std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
214
                                                ASTContext &Context,
215
28
                                                PLTClass Classification) {
216
28
  if (RawExpression.isImplicitCXXThis())
217
    // Return the empty string, because `std::nullopt` signifies some sort of
218
    // failure.
219
3
    return std::string();
220
221
25
  const Expr *E = RawExpression.IgnoreImplicitAsWritten();
222
223
25
  if (E->getType()->isAnyPointerType() ||
224
25
      
treatLikePointer(E->getType(), Classification, Context)14
) {
225
    // Strip off operator-> calls. They can only occur inside an actual arrow
226
    // member access, so we treat them as equivalent to an actual object
227
    // expression.
228
13
    if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
229
2
      E = Obj;
230
13
    return buildAccessForPointer(*E, Context);
231
13
  }
232
233
12
  if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
234
3
    if (treatLikePointer(Obj->getType(), Classification, Context))
235
2
      return buildAccessForPointer(*Obj, Context);
236
10
  };
237
238
10
  return buildAccessForValue(*E, Context);
239
12
}