xref: /src/contrib/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1519fc96cSDimitry Andric //===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
2519fc96cSDimitry Andric //
3519fc96cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4519fc96cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5519fc96cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6519fc96cSDimitry Andric //
7519fc96cSDimitry Andric //===----------------------------------------------------------------------===//
8519fc96cSDimitry Andric 
9519fc96cSDimitry Andric #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10519fc96cSDimitry Andric #include "clang/AST/ASTContext.h"
11519fc96cSDimitry Andric #include "clang/AST/Expr.h"
12519fc96cSDimitry Andric #include "clang/AST/ExprCXX.h"
136f8fc217SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
146f8fc217SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
15519fc96cSDimitry Andric #include "clang/Tooling/Transformer/SourceCode.h"
16519fc96cSDimitry Andric #include "llvm/ADT/Twine.h"
17519fc96cSDimitry Andric #include <string>
18519fc96cSDimitry Andric 
19519fc96cSDimitry Andric using namespace clang;
20519fc96cSDimitry Andric using namespace tooling;
21519fc96cSDimitry Andric 
reallyIgnoreImplicit(const Expr & E)22519fc96cSDimitry Andric const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23519fc96cSDimitry Andric   const Expr *Expr = E.IgnoreImplicit();
24519fc96cSDimitry Andric   if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
25519fc96cSDimitry Andric     if (CE->getNumArgs() > 0 &&
26519fc96cSDimitry Andric         CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
27519fc96cSDimitry Andric       return CE->getArg(0)->IgnoreImplicit();
28519fc96cSDimitry Andric   }
29519fc96cSDimitry Andric   return Expr;
30519fc96cSDimitry Andric }
31519fc96cSDimitry Andric 
mayEverNeedParens(const Expr & E)32519fc96cSDimitry Andric bool tooling::mayEverNeedParens(const Expr &E) {
33519fc96cSDimitry Andric   const Expr *Expr = reallyIgnoreImplicit(E);
34519fc96cSDimitry Andric   // We always want parens around unary, binary, and ternary operators, because
35519fc96cSDimitry Andric   // they are lower precedence.
36519fc96cSDimitry Andric   if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
37519fc96cSDimitry Andric       isa<AbstractConditionalOperator>(Expr))
38519fc96cSDimitry Andric     return true;
39519fc96cSDimitry Andric 
40519fc96cSDimitry Andric   // We need parens around calls to all overloaded operators except: function
41519fc96cSDimitry Andric   // calls, subscripts, and expressions that are already part of an (implicit)
42519fc96cSDimitry Andric   // call to operator->. These latter are all in the same precedence level as
43519fc96cSDimitry Andric   // dot/arrow and that level is left associative, so they don't need parens
44519fc96cSDimitry Andric   // when appearing on the left.
45519fc96cSDimitry Andric   if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
46519fc96cSDimitry Andric     return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47519fc96cSDimitry Andric            Op->getOperator() != OO_Arrow;
48519fc96cSDimitry Andric 
49519fc96cSDimitry Andric   return false;
50519fc96cSDimitry Andric }
51519fc96cSDimitry Andric 
needParensAfterUnaryOperator(const Expr & E)52519fc96cSDimitry Andric bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53519fc96cSDimitry Andric   const Expr *Expr = reallyIgnoreImplicit(E);
54519fc96cSDimitry Andric   if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
55519fc96cSDimitry Andric     return true;
56519fc96cSDimitry Andric 
57519fc96cSDimitry Andric   if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
58519fc96cSDimitry Andric     return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
59519fc96cSDimitry Andric            Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
60519fc96cSDimitry Andric            Op->getOperator() != OO_Subscript;
61519fc96cSDimitry Andric 
62519fc96cSDimitry Andric   return false;
63519fc96cSDimitry Andric }
64519fc96cSDimitry Andric 
isKnownPointerLikeType(QualType Ty,ASTContext & Context)656f8fc217SDimitry Andric bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
666f8fc217SDimitry Andric   using namespace ast_matchers;
676f8fc217SDimitry Andric   const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
686f8fc217SDimitry Andric       recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
696f8fc217SDimitry Andric           "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
706f8fc217SDimitry Andric           "::std::optional", "::absl::optional", "::llvm::Optional",
716f8fc217SDimitry Andric           "absl::StatusOr", "::llvm::Expected"))))));
726f8fc217SDimitry Andric   return match(PointerLikeTy, Ty, Context).size() > 0;
736f8fc217SDimitry Andric }
746f8fc217SDimitry Andric 
buildParens(const Expr & E,const ASTContext & Context)75e3b55780SDimitry Andric std::optional<std::string> tooling::buildParens(const Expr &E,
76519fc96cSDimitry Andric                                                 const ASTContext &Context) {
77519fc96cSDimitry Andric   StringRef Text = getText(E, Context);
78519fc96cSDimitry Andric   if (Text.empty())
79e3b55780SDimitry Andric     return std::nullopt;
80519fc96cSDimitry Andric   if (mayEverNeedParens(E))
81519fc96cSDimitry Andric     return ("(" + Text + ")").str();
82519fc96cSDimitry Andric   return Text.str();
83519fc96cSDimitry Andric }
84519fc96cSDimitry Andric 
85e3b55780SDimitry Andric std::optional<std::string>
buildDereference(const Expr & E,const ASTContext & Context)86519fc96cSDimitry Andric tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87519fc96cSDimitry Andric   if (const auto *Op = dyn_cast<UnaryOperator>(&E))
88519fc96cSDimitry Andric     if (Op->getOpcode() == UO_AddrOf) {
89519fc96cSDimitry Andric       // Strip leading '&'.
90519fc96cSDimitry Andric       StringRef Text =
91519fc96cSDimitry Andric           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92519fc96cSDimitry Andric       if (Text.empty())
93e3b55780SDimitry Andric         return std::nullopt;
94519fc96cSDimitry Andric       return Text.str();
95519fc96cSDimitry Andric     }
96519fc96cSDimitry Andric 
97519fc96cSDimitry Andric   StringRef Text = getText(E, Context);
98519fc96cSDimitry Andric   if (Text.empty())
99e3b55780SDimitry Andric     return std::nullopt;
100519fc96cSDimitry Andric   // Add leading '*'.
101519fc96cSDimitry Andric   if (needParensAfterUnaryOperator(E))
102519fc96cSDimitry Andric     return ("*(" + Text + ")").str();
103519fc96cSDimitry Andric   return ("*" + Text).str();
104519fc96cSDimitry Andric }
105519fc96cSDimitry Andric 
buildAddressOf(const Expr & E,const ASTContext & Context)106e3b55780SDimitry Andric std::optional<std::string> tooling::buildAddressOf(const Expr &E,
107519fc96cSDimitry Andric                                                    const ASTContext &Context) {
108344a3780SDimitry Andric   if (E.isImplicitCXXThis())
109344a3780SDimitry Andric     return std::string("this");
110519fc96cSDimitry Andric   if (const auto *Op = dyn_cast<UnaryOperator>(&E))
111519fc96cSDimitry Andric     if (Op->getOpcode() == UO_Deref) {
112519fc96cSDimitry Andric       // Strip leading '*'.
113519fc96cSDimitry Andric       StringRef Text =
114519fc96cSDimitry Andric           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115519fc96cSDimitry Andric       if (Text.empty())
116e3b55780SDimitry Andric         return std::nullopt;
117519fc96cSDimitry Andric       return Text.str();
118519fc96cSDimitry Andric     }
119519fc96cSDimitry Andric   // Add leading '&'.
120519fc96cSDimitry Andric   StringRef Text = getText(E, Context);
121519fc96cSDimitry Andric   if (Text.empty())
122e3b55780SDimitry Andric     return std::nullopt;
123519fc96cSDimitry Andric   if (needParensAfterUnaryOperator(E)) {
124519fc96cSDimitry Andric     return ("&(" + Text + ")").str();
125519fc96cSDimitry Andric   }
126519fc96cSDimitry Andric   return ("&" + Text).str();
127519fc96cSDimitry Andric }
128519fc96cSDimitry Andric 
1296f8fc217SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
1306f8fc217SDimitry Andric // is a non-pointer value.
131e3b55780SDimitry Andric static std::optional<std::string>
buildAccessForValue(const Expr & E,const ASTContext & Context)1326f8fc217SDimitry Andric buildAccessForValue(const Expr &E, const ASTContext &Context) {
133519fc96cSDimitry Andric   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
134519fc96cSDimitry Andric     if (Op->getOpcode() == UO_Deref) {
135519fc96cSDimitry Andric       // Strip leading '*', add following '->'.
136519fc96cSDimitry Andric       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137519fc96cSDimitry Andric       StringRef DerefText = getText(*SubExpr, Context);
138519fc96cSDimitry Andric       if (DerefText.empty())
139e3b55780SDimitry Andric         return std::nullopt;
140519fc96cSDimitry Andric       if (needParensBeforeDotOrArrow(*SubExpr))
141519fc96cSDimitry Andric         return ("(" + DerefText + ")->").str();
142519fc96cSDimitry Andric       return (DerefText + "->").str();
143519fc96cSDimitry Andric     }
144519fc96cSDimitry Andric 
145519fc96cSDimitry Andric   // Add following '.'.
146519fc96cSDimitry Andric   StringRef Text = getText(E, Context);
147519fc96cSDimitry Andric   if (Text.empty())
148e3b55780SDimitry Andric     return std::nullopt;
149519fc96cSDimitry Andric   if (needParensBeforeDotOrArrow(E)) {
150519fc96cSDimitry Andric     return ("(" + Text + ").").str();
151519fc96cSDimitry Andric   }
152519fc96cSDimitry Andric   return (Text + ".").str();
153519fc96cSDimitry Andric }
154519fc96cSDimitry Andric 
1556f8fc217SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
1566f8fc217SDimitry Andric // is a pointer value.
157e3b55780SDimitry Andric static std::optional<std::string>
buildAccessForPointer(const Expr & E,const ASTContext & Context)1586f8fc217SDimitry Andric buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159519fc96cSDimitry Andric   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
160519fc96cSDimitry Andric     if (Op->getOpcode() == UO_AddrOf) {
161519fc96cSDimitry Andric       // Strip leading '&', add following '.'.
162519fc96cSDimitry Andric       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163519fc96cSDimitry Andric       StringRef DerefText = getText(*SubExpr, Context);
164519fc96cSDimitry Andric       if (DerefText.empty())
165e3b55780SDimitry Andric         return std::nullopt;
166519fc96cSDimitry Andric       if (needParensBeforeDotOrArrow(*SubExpr))
167519fc96cSDimitry Andric         return ("(" + DerefText + ").").str();
168519fc96cSDimitry Andric       return (DerefText + ".").str();
169519fc96cSDimitry Andric     }
170519fc96cSDimitry Andric 
171519fc96cSDimitry Andric   // Add following '->'.
172519fc96cSDimitry Andric   StringRef Text = getText(E, Context);
173519fc96cSDimitry Andric   if (Text.empty())
174e3b55780SDimitry Andric     return std::nullopt;
175519fc96cSDimitry Andric   if (needParensBeforeDotOrArrow(E))
176519fc96cSDimitry Andric     return ("(" + Text + ")->").str();
177519fc96cSDimitry Andric   return (Text + "->").str();
178519fc96cSDimitry Andric }
1796f8fc217SDimitry Andric 
buildDot(const Expr & E,const ASTContext & Context)180e3b55780SDimitry Andric std::optional<std::string> tooling::buildDot(const Expr &E,
1816f8fc217SDimitry Andric                                              const ASTContext &Context) {
1826f8fc217SDimitry Andric   return buildAccessForValue(E, Context);
1836f8fc217SDimitry Andric }
1846f8fc217SDimitry Andric 
buildArrow(const Expr & E,const ASTContext & Context)185e3b55780SDimitry Andric std::optional<std::string> tooling::buildArrow(const Expr &E,
1866f8fc217SDimitry Andric                                                const ASTContext &Context) {
1876f8fc217SDimitry Andric   return buildAccessForPointer(E, Context);
1886f8fc217SDimitry Andric }
1896f8fc217SDimitry Andric 
1906f8fc217SDimitry Andric // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
1916f8fc217SDimitry Andric // `O`. Otherwise, returns `nullptr`.
maybeGetOperatorObjectArg(const Expr & E,OverloadedOperatorKind K)1926f8fc217SDimitry Andric static const Expr *maybeGetOperatorObjectArg(const Expr &E,
1936f8fc217SDimitry Andric                                              OverloadedOperatorKind K) {
1946f8fc217SDimitry Andric   if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {
1956f8fc217SDimitry Andric     if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)
1966f8fc217SDimitry Andric       return OpCall->getArg(0);
1976f8fc217SDimitry Andric   }
1986f8fc217SDimitry Andric   return nullptr;
1996f8fc217SDimitry Andric }
2006f8fc217SDimitry Andric 
treatLikePointer(QualType Ty,PLTClass C,ASTContext & Context)2016f8fc217SDimitry Andric static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
2026f8fc217SDimitry Andric   switch (C) {
2036f8fc217SDimitry Andric   case PLTClass::Value:
2046f8fc217SDimitry Andric     return false;
2056f8fc217SDimitry Andric   case PLTClass::Pointer:
2066f8fc217SDimitry Andric     return isKnownPointerLikeType(Ty, Context);
2076f8fc217SDimitry Andric   }
2086f8fc217SDimitry Andric   llvm_unreachable("Unknown PLTClass enum");
2096f8fc217SDimitry Andric }
2106f8fc217SDimitry Andric 
2116f8fc217SDimitry Andric // FIXME: move over the other `maybe` functionality from Stencil. Should all be
2126f8fc217SDimitry Andric // in one place.
buildAccess(const Expr & RawExpression,ASTContext & Context,PLTClass Classification)213e3b55780SDimitry Andric std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
2146f8fc217SDimitry Andric                                                 ASTContext &Context,
2156f8fc217SDimitry Andric                                                 PLTClass Classification) {
2166f8fc217SDimitry Andric   if (RawExpression.isImplicitCXXThis())
217e3b55780SDimitry Andric     // Return the empty string, because `std::nullopt` signifies some sort of
218e3b55780SDimitry Andric     // failure.
2196f8fc217SDimitry Andric     return std::string();
2206f8fc217SDimitry Andric 
2216f8fc217SDimitry Andric   const Expr *E = RawExpression.IgnoreImplicitAsWritten();
2226f8fc217SDimitry Andric 
2236f8fc217SDimitry Andric   if (E->getType()->isAnyPointerType() ||
2246f8fc217SDimitry Andric       treatLikePointer(E->getType(), Classification, Context)) {
2256f8fc217SDimitry Andric     // Strip off operator-> calls. They can only occur inside an actual arrow
2266f8fc217SDimitry Andric     // member access, so we treat them as equivalent to an actual object
2276f8fc217SDimitry Andric     // expression.
2286f8fc217SDimitry Andric     if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
2296f8fc217SDimitry Andric       E = Obj;
2306f8fc217SDimitry Andric     return buildAccessForPointer(*E, Context);
2316f8fc217SDimitry Andric   }
2326f8fc217SDimitry Andric 
2336f8fc217SDimitry Andric   if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
2346f8fc217SDimitry Andric     if (treatLikePointer(Obj->getType(), Classification, Context))
2356f8fc217SDimitry Andric       return buildAccessForPointer(*Obj, Context);
2366f8fc217SDimitry Andric   };
2376f8fc217SDimitry Andric 
2386f8fc217SDimitry Andric   return buildAccessForValue(*E, Context);
2396f8fc217SDimitry Andric }
240