xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1a7dea167SDimitry Andric //===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric 
9a7dea167SDimitry Andric #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10a7dea167SDimitry Andric #include "clang/AST/ASTContext.h"
11a7dea167SDimitry Andric #include "clang/AST/Expr.h"
12a7dea167SDimitry Andric #include "clang/AST/ExprCXX.h"
1304eeddc0SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
1404eeddc0SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
15a7dea167SDimitry Andric #include "clang/Tooling/Transformer/SourceCode.h"
16a7dea167SDimitry Andric #include "llvm/ADT/Twine.h"
17a7dea167SDimitry Andric #include <string>
18a7dea167SDimitry Andric 
19a7dea167SDimitry Andric using namespace clang;
20a7dea167SDimitry Andric using namespace tooling;
21a7dea167SDimitry Andric 
reallyIgnoreImplicit(const Expr & E)22a7dea167SDimitry Andric const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23a7dea167SDimitry Andric   const Expr *Expr = E.IgnoreImplicit();
24a7dea167SDimitry Andric   if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
25a7dea167SDimitry Andric     if (CE->getNumArgs() > 0 &&
26a7dea167SDimitry Andric         CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
27a7dea167SDimitry Andric       return CE->getArg(0)->IgnoreImplicit();
28a7dea167SDimitry Andric   }
29a7dea167SDimitry Andric   return Expr;
30a7dea167SDimitry Andric }
31a7dea167SDimitry Andric 
mayEverNeedParens(const Expr & E)32a7dea167SDimitry Andric bool tooling::mayEverNeedParens(const Expr &E) {
33a7dea167SDimitry Andric   const Expr *Expr = reallyIgnoreImplicit(E);
34a7dea167SDimitry Andric   // We always want parens around unary, binary, and ternary operators, because
35a7dea167SDimitry Andric   // they are lower precedence.
36a7dea167SDimitry Andric   if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
37a7dea167SDimitry Andric       isa<AbstractConditionalOperator>(Expr))
38a7dea167SDimitry Andric     return true;
39a7dea167SDimitry Andric 
40a7dea167SDimitry Andric   // We need parens around calls to all overloaded operators except: function
41a7dea167SDimitry Andric   // calls, subscripts, and expressions that are already part of an (implicit)
42a7dea167SDimitry Andric   // call to operator->. These latter are all in the same precedence level as
43a7dea167SDimitry Andric   // dot/arrow and that level is left associative, so they don't need parens
44a7dea167SDimitry Andric   // when appearing on the left.
45a7dea167SDimitry Andric   if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
46a7dea167SDimitry Andric     return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47a7dea167SDimitry Andric            Op->getOperator() != OO_Arrow;
48a7dea167SDimitry Andric 
49a7dea167SDimitry Andric   return false;
50a7dea167SDimitry Andric }
51a7dea167SDimitry Andric 
needParensAfterUnaryOperator(const Expr & E)52a7dea167SDimitry Andric bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53a7dea167SDimitry Andric   const Expr *Expr = reallyIgnoreImplicit(E);
54a7dea167SDimitry Andric   if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
55a7dea167SDimitry Andric     return true;
56a7dea167SDimitry Andric 
57a7dea167SDimitry Andric   if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
58a7dea167SDimitry Andric     return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
59a7dea167SDimitry Andric            Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
60a7dea167SDimitry Andric            Op->getOperator() != OO_Subscript;
61a7dea167SDimitry Andric 
62a7dea167SDimitry Andric   return false;
63a7dea167SDimitry Andric }
64a7dea167SDimitry Andric 
isKnownPointerLikeType(QualType Ty,ASTContext & Context)6504eeddc0SDimitry Andric bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
6604eeddc0SDimitry Andric   using namespace ast_matchers;
6704eeddc0SDimitry Andric   const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
6804eeddc0SDimitry Andric       recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
6904eeddc0SDimitry Andric           "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
7004eeddc0SDimitry Andric           "::std::optional", "::absl::optional", "::llvm::Optional",
7104eeddc0SDimitry Andric           "absl::StatusOr", "::llvm::Expected"))))));
7204eeddc0SDimitry Andric   return match(PointerLikeTy, Ty, Context).size() > 0;
7304eeddc0SDimitry Andric }
7404eeddc0SDimitry Andric 
buildParens(const Expr & E,const ASTContext & Context)75*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildParens(const Expr &E,
76a7dea167SDimitry Andric                                                 const ASTContext &Context) {
77a7dea167SDimitry Andric   StringRef Text = getText(E, Context);
78a7dea167SDimitry Andric   if (Text.empty())
79*bdd1243dSDimitry Andric     return std::nullopt;
80a7dea167SDimitry Andric   if (mayEverNeedParens(E))
81a7dea167SDimitry Andric     return ("(" + Text + ")").str();
82a7dea167SDimitry Andric   return Text.str();
83a7dea167SDimitry Andric }
84a7dea167SDimitry Andric 
85*bdd1243dSDimitry Andric std::optional<std::string>
buildDereference(const Expr & E,const ASTContext & Context)86a7dea167SDimitry Andric tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87a7dea167SDimitry Andric   if (const auto *Op = dyn_cast<UnaryOperator>(&E))
88a7dea167SDimitry Andric     if (Op->getOpcode() == UO_AddrOf) {
89a7dea167SDimitry Andric       // Strip leading '&'.
90a7dea167SDimitry Andric       StringRef Text =
91a7dea167SDimitry Andric           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92a7dea167SDimitry Andric       if (Text.empty())
93*bdd1243dSDimitry Andric         return std::nullopt;
94a7dea167SDimitry Andric       return Text.str();
95a7dea167SDimitry Andric     }
96a7dea167SDimitry Andric 
97a7dea167SDimitry Andric   StringRef Text = getText(E, Context);
98a7dea167SDimitry Andric   if (Text.empty())
99*bdd1243dSDimitry Andric     return std::nullopt;
100a7dea167SDimitry Andric   // Add leading '*'.
101a7dea167SDimitry Andric   if (needParensAfterUnaryOperator(E))
102a7dea167SDimitry Andric     return ("*(" + Text + ")").str();
103a7dea167SDimitry Andric   return ("*" + Text).str();
104a7dea167SDimitry Andric }
105a7dea167SDimitry Andric 
buildAddressOf(const Expr & E,const ASTContext & Context)106*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildAddressOf(const Expr &E,
107a7dea167SDimitry Andric                                                    const ASTContext &Context) {
108fe6060f1SDimitry Andric   if (E.isImplicitCXXThis())
109fe6060f1SDimitry Andric     return std::string("this");
110a7dea167SDimitry Andric   if (const auto *Op = dyn_cast<UnaryOperator>(&E))
111a7dea167SDimitry Andric     if (Op->getOpcode() == UO_Deref) {
112a7dea167SDimitry Andric       // Strip leading '*'.
113a7dea167SDimitry Andric       StringRef Text =
114a7dea167SDimitry Andric           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115a7dea167SDimitry Andric       if (Text.empty())
116*bdd1243dSDimitry Andric         return std::nullopt;
117a7dea167SDimitry Andric       return Text.str();
118a7dea167SDimitry Andric     }
119a7dea167SDimitry Andric   // Add leading '&'.
120a7dea167SDimitry Andric   StringRef Text = getText(E, Context);
121a7dea167SDimitry Andric   if (Text.empty())
122*bdd1243dSDimitry Andric     return std::nullopt;
123a7dea167SDimitry Andric   if (needParensAfterUnaryOperator(E)) {
124a7dea167SDimitry Andric     return ("&(" + Text + ")").str();
125a7dea167SDimitry Andric   }
126a7dea167SDimitry Andric   return ("&" + Text).str();
127a7dea167SDimitry Andric }
128a7dea167SDimitry Andric 
12904eeddc0SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
13004eeddc0SDimitry Andric // is a non-pointer value.
131*bdd1243dSDimitry Andric static std::optional<std::string>
buildAccessForValue(const Expr & E,const ASTContext & Context)13204eeddc0SDimitry Andric buildAccessForValue(const Expr &E, const ASTContext &Context) {
133a7dea167SDimitry Andric   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
134a7dea167SDimitry Andric     if (Op->getOpcode() == UO_Deref) {
135a7dea167SDimitry Andric       // Strip leading '*', add following '->'.
136a7dea167SDimitry Andric       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137a7dea167SDimitry Andric       StringRef DerefText = getText(*SubExpr, Context);
138a7dea167SDimitry Andric       if (DerefText.empty())
139*bdd1243dSDimitry Andric         return std::nullopt;
140a7dea167SDimitry Andric       if (needParensBeforeDotOrArrow(*SubExpr))
141a7dea167SDimitry Andric         return ("(" + DerefText + ")->").str();
142a7dea167SDimitry Andric       return (DerefText + "->").str();
143a7dea167SDimitry Andric     }
144a7dea167SDimitry Andric 
145a7dea167SDimitry Andric   // Add following '.'.
146a7dea167SDimitry Andric   StringRef Text = getText(E, Context);
147a7dea167SDimitry Andric   if (Text.empty())
148*bdd1243dSDimitry Andric     return std::nullopt;
149a7dea167SDimitry Andric   if (needParensBeforeDotOrArrow(E)) {
150a7dea167SDimitry Andric     return ("(" + Text + ").").str();
151a7dea167SDimitry Andric   }
152a7dea167SDimitry Andric   return (Text + ".").str();
153a7dea167SDimitry Andric }
154a7dea167SDimitry Andric 
15504eeddc0SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
15604eeddc0SDimitry Andric // is a pointer value.
157*bdd1243dSDimitry Andric static std::optional<std::string>
buildAccessForPointer(const Expr & E,const ASTContext & Context)15804eeddc0SDimitry Andric buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159a7dea167SDimitry Andric   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
160a7dea167SDimitry Andric     if (Op->getOpcode() == UO_AddrOf) {
161a7dea167SDimitry Andric       // Strip leading '&', add following '.'.
162a7dea167SDimitry Andric       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163a7dea167SDimitry Andric       StringRef DerefText = getText(*SubExpr, Context);
164a7dea167SDimitry Andric       if (DerefText.empty())
165*bdd1243dSDimitry Andric         return std::nullopt;
166a7dea167SDimitry Andric       if (needParensBeforeDotOrArrow(*SubExpr))
167a7dea167SDimitry Andric         return ("(" + DerefText + ").").str();
168a7dea167SDimitry Andric       return (DerefText + ".").str();
169a7dea167SDimitry Andric     }
170a7dea167SDimitry Andric 
171a7dea167SDimitry Andric   // Add following '->'.
172a7dea167SDimitry Andric   StringRef Text = getText(E, Context);
173a7dea167SDimitry Andric   if (Text.empty())
174*bdd1243dSDimitry Andric     return std::nullopt;
175a7dea167SDimitry Andric   if (needParensBeforeDotOrArrow(E))
176a7dea167SDimitry Andric     return ("(" + Text + ")->").str();
177a7dea167SDimitry Andric   return (Text + "->").str();
178a7dea167SDimitry Andric }
17904eeddc0SDimitry Andric 
buildDot(const Expr & E,const ASTContext & Context)180*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildDot(const Expr &E,
18104eeddc0SDimitry Andric                                              const ASTContext &Context) {
18204eeddc0SDimitry Andric   return buildAccessForValue(E, Context);
18304eeddc0SDimitry Andric }
18404eeddc0SDimitry Andric 
buildArrow(const Expr & E,const ASTContext & Context)185*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildArrow(const Expr &E,
18604eeddc0SDimitry Andric                                                const ASTContext &Context) {
18704eeddc0SDimitry Andric   return buildAccessForPointer(E, Context);
18804eeddc0SDimitry Andric }
18904eeddc0SDimitry Andric 
19004eeddc0SDimitry Andric // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
19104eeddc0SDimitry Andric // `O`. Otherwise, returns `nullptr`.
maybeGetOperatorObjectArg(const Expr & E,OverloadedOperatorKind K)19204eeddc0SDimitry Andric static const Expr *maybeGetOperatorObjectArg(const Expr &E,
19304eeddc0SDimitry Andric                                              OverloadedOperatorKind K) {
19404eeddc0SDimitry Andric   if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {
19504eeddc0SDimitry Andric     if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)
19604eeddc0SDimitry Andric       return OpCall->getArg(0);
19704eeddc0SDimitry Andric   }
19804eeddc0SDimitry Andric   return nullptr;
19904eeddc0SDimitry Andric }
20004eeddc0SDimitry Andric 
treatLikePointer(QualType Ty,PLTClass C,ASTContext & Context)20104eeddc0SDimitry Andric static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
20204eeddc0SDimitry Andric   switch (C) {
20304eeddc0SDimitry Andric   case PLTClass::Value:
20404eeddc0SDimitry Andric     return false;
20504eeddc0SDimitry Andric   case PLTClass::Pointer:
20604eeddc0SDimitry Andric     return isKnownPointerLikeType(Ty, Context);
20704eeddc0SDimitry Andric   }
20804eeddc0SDimitry Andric   llvm_unreachable("Unknown PLTClass enum");
20904eeddc0SDimitry Andric }
21004eeddc0SDimitry Andric 
21104eeddc0SDimitry Andric // FIXME: move over the other `maybe` functionality from Stencil. Should all be
21204eeddc0SDimitry Andric // in one place.
buildAccess(const Expr & RawExpression,ASTContext & Context,PLTClass Classification)213*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
21404eeddc0SDimitry Andric                                                 ASTContext &Context,
21504eeddc0SDimitry Andric                                                 PLTClass Classification) {
21604eeddc0SDimitry Andric   if (RawExpression.isImplicitCXXThis())
217*bdd1243dSDimitry Andric     // Return the empty string, because `std::nullopt` signifies some sort of
218*bdd1243dSDimitry Andric     // failure.
21904eeddc0SDimitry Andric     return std::string();
22004eeddc0SDimitry Andric 
22104eeddc0SDimitry Andric   const Expr *E = RawExpression.IgnoreImplicitAsWritten();
22204eeddc0SDimitry Andric 
22304eeddc0SDimitry Andric   if (E->getType()->isAnyPointerType() ||
22404eeddc0SDimitry Andric       treatLikePointer(E->getType(), Classification, Context)) {
22504eeddc0SDimitry Andric     // Strip off operator-> calls. They can only occur inside an actual arrow
22604eeddc0SDimitry Andric     // member access, so we treat them as equivalent to an actual object
22704eeddc0SDimitry Andric     // expression.
22804eeddc0SDimitry Andric     if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
22904eeddc0SDimitry Andric       E = Obj;
23004eeddc0SDimitry Andric     return buildAccessForPointer(*E, Context);
23104eeddc0SDimitry Andric   }
23204eeddc0SDimitry Andric 
23304eeddc0SDimitry Andric   if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
23404eeddc0SDimitry Andric     if (treatLikePointer(Obj->getType(), Classification, Context))
23504eeddc0SDimitry Andric       return buildAccessForPointer(*Obj, Context);
23604eeddc0SDimitry Andric   };
23704eeddc0SDimitry Andric 
23804eeddc0SDimitry Andric   return buildAccessForValue(*E, Context);
23904eeddc0SDimitry Andric }
240