xref: /openbsd-src/gnu/llvm/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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