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