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