xref: /llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp (revision b0de3630249aedb6910b4ec287ba152b32fd7f19)
1fbdf8352SYitzhak Mandelbaum //===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
2fbdf8352SYitzhak Mandelbaum //
3fbdf8352SYitzhak Mandelbaum // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fbdf8352SYitzhak Mandelbaum // See https://llvm.org/LICENSE.txt for license information.
5fbdf8352SYitzhak Mandelbaum // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fbdf8352SYitzhak Mandelbaum //
7fbdf8352SYitzhak Mandelbaum //===----------------------------------------------------------------------===//
8fbdf8352SYitzhak Mandelbaum 
9fbdf8352SYitzhak Mandelbaum #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10fbdf8352SYitzhak Mandelbaum #include "clang/AST/ASTContext.h"
11fbdf8352SYitzhak Mandelbaum #include "clang/AST/Expr.h"
12fbdf8352SYitzhak Mandelbaum #include "clang/AST/ExprCXX.h"
130944c196SYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchFinder.h"
140944c196SYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchers.h"
15fbdf8352SYitzhak Mandelbaum #include "clang/Tooling/Transformer/SourceCode.h"
16fbdf8352SYitzhak Mandelbaum #include "llvm/ADT/Twine.h"
17fbdf8352SYitzhak Mandelbaum #include <string>
18fbdf8352SYitzhak Mandelbaum 
19fbdf8352SYitzhak Mandelbaum using namespace clang;
20fbdf8352SYitzhak Mandelbaum using namespace tooling;
21fbdf8352SYitzhak Mandelbaum 
reallyIgnoreImplicit(const Expr & E)22fbdf8352SYitzhak Mandelbaum const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23fbdf8352SYitzhak Mandelbaum   const Expr *Expr = E.IgnoreImplicit();
24fbdf8352SYitzhak Mandelbaum   if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
25fbdf8352SYitzhak Mandelbaum     if (CE->getNumArgs() > 0 &&
26fbdf8352SYitzhak Mandelbaum         CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
27fbdf8352SYitzhak Mandelbaum       return CE->getArg(0)->IgnoreImplicit();
28fbdf8352SYitzhak Mandelbaum   }
29fbdf8352SYitzhak Mandelbaum   return Expr;
30fbdf8352SYitzhak Mandelbaum }
31fbdf8352SYitzhak Mandelbaum 
mayEverNeedParens(const Expr & E)32fbdf8352SYitzhak Mandelbaum bool tooling::mayEverNeedParens(const Expr &E) {
33fbdf8352SYitzhak Mandelbaum   const Expr *Expr = reallyIgnoreImplicit(E);
34fbdf8352SYitzhak Mandelbaum   // We always want parens around unary, binary, and ternary operators, because
35fbdf8352SYitzhak Mandelbaum   // they are lower precedence.
36fbdf8352SYitzhak Mandelbaum   if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
37fbdf8352SYitzhak Mandelbaum       isa<AbstractConditionalOperator>(Expr))
38fbdf8352SYitzhak Mandelbaum     return true;
39fbdf8352SYitzhak Mandelbaum 
40fbdf8352SYitzhak Mandelbaum   // We need parens around calls to all overloaded operators except: function
41fbdf8352SYitzhak Mandelbaum   // calls, subscripts, and expressions that are already part of an (implicit)
42fbdf8352SYitzhak Mandelbaum   // call to operator->. These latter are all in the same precedence level as
43fbdf8352SYitzhak Mandelbaum   // dot/arrow and that level is left associative, so they don't need parens
44fbdf8352SYitzhak Mandelbaum   // when appearing on the left.
45fbdf8352SYitzhak Mandelbaum   if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
46fbdf8352SYitzhak Mandelbaum     return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47fbdf8352SYitzhak Mandelbaum            Op->getOperator() != OO_Arrow;
48fbdf8352SYitzhak Mandelbaum 
49fbdf8352SYitzhak Mandelbaum   return false;
50fbdf8352SYitzhak Mandelbaum }
51fbdf8352SYitzhak Mandelbaum 
needParensAfterUnaryOperator(const Expr & E)52fbdf8352SYitzhak Mandelbaum bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53fbdf8352SYitzhak Mandelbaum   const Expr *Expr = reallyIgnoreImplicit(E);
54fbdf8352SYitzhak Mandelbaum   if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
55fbdf8352SYitzhak Mandelbaum     return true;
56fbdf8352SYitzhak Mandelbaum 
57fbdf8352SYitzhak Mandelbaum   if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
58fbdf8352SYitzhak Mandelbaum     return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
59fbdf8352SYitzhak Mandelbaum            Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
60fbdf8352SYitzhak Mandelbaum            Op->getOperator() != OO_Subscript;
61fbdf8352SYitzhak Mandelbaum 
62fbdf8352SYitzhak Mandelbaum   return false;
63fbdf8352SYitzhak Mandelbaum }
64fbdf8352SYitzhak Mandelbaum 
isKnownPointerLikeType(QualType Ty,ASTContext & Context)650944c196SYitzhak Mandelbaum bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
660944c196SYitzhak Mandelbaum   using namespace ast_matchers;
670944c196SYitzhak Mandelbaum   const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
680944c196SYitzhak Mandelbaum       recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
690944c196SYitzhak Mandelbaum           "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
700944c196SYitzhak Mandelbaum           "::std::optional", "::absl::optional", "::llvm::Optional",
710944c196SYitzhak Mandelbaum           "absl::StatusOr", "::llvm::Expected"))))));
720944c196SYitzhak Mandelbaum   return match(PointerLikeTy, Ty, Context).size() > 0;
730944c196SYitzhak Mandelbaum }
740944c196SYitzhak Mandelbaum 
buildParens(const Expr & E,const ASTContext & Context)75*b0de3630SFangrui Song std::optional<std::string> tooling::buildParens(const Expr &E,
76fbdf8352SYitzhak Mandelbaum                                                 const ASTContext &Context) {
77fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
78fbdf8352SYitzhak Mandelbaum   if (Text.empty())
795891420eSKazu Hirata     return std::nullopt;
80fbdf8352SYitzhak Mandelbaum   if (mayEverNeedParens(E))
81fbdf8352SYitzhak Mandelbaum     return ("(" + Text + ")").str();
82fbdf8352SYitzhak Mandelbaum   return Text.str();
83fbdf8352SYitzhak Mandelbaum }
84fbdf8352SYitzhak Mandelbaum 
85*b0de3630SFangrui Song std::optional<std::string>
buildDereference(const Expr & E,const ASTContext & Context)86fbdf8352SYitzhak Mandelbaum tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87fbdf8352SYitzhak Mandelbaum   if (const auto *Op = dyn_cast<UnaryOperator>(&E))
88fbdf8352SYitzhak Mandelbaum     if (Op->getOpcode() == UO_AddrOf) {
89fbdf8352SYitzhak Mandelbaum       // Strip leading '&'.
90fbdf8352SYitzhak Mandelbaum       StringRef Text =
91fbdf8352SYitzhak Mandelbaum           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92fbdf8352SYitzhak Mandelbaum       if (Text.empty())
935891420eSKazu Hirata         return std::nullopt;
94fbdf8352SYitzhak Mandelbaum       return Text.str();
95fbdf8352SYitzhak Mandelbaum     }
96fbdf8352SYitzhak Mandelbaum 
97fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
98fbdf8352SYitzhak Mandelbaum   if (Text.empty())
995891420eSKazu Hirata     return std::nullopt;
100fbdf8352SYitzhak Mandelbaum   // Add leading '*'.
101fbdf8352SYitzhak Mandelbaum   if (needParensAfterUnaryOperator(E))
102fbdf8352SYitzhak Mandelbaum     return ("*(" + Text + ")").str();
103fbdf8352SYitzhak Mandelbaum   return ("*" + Text).str();
104fbdf8352SYitzhak Mandelbaum }
105fbdf8352SYitzhak Mandelbaum 
buildAddressOf(const Expr & E,const ASTContext & Context)106*b0de3630SFangrui Song std::optional<std::string> tooling::buildAddressOf(const Expr &E,
107fbdf8352SYitzhak Mandelbaum                                                    const ASTContext &Context) {
108d2e32fa4SYitzhak Mandelbaum   if (E.isImplicitCXXThis())
109d2e32fa4SYitzhak Mandelbaum     return std::string("this");
110fbdf8352SYitzhak Mandelbaum   if (const auto *Op = dyn_cast<UnaryOperator>(&E))
111fbdf8352SYitzhak Mandelbaum     if (Op->getOpcode() == UO_Deref) {
112fbdf8352SYitzhak Mandelbaum       // Strip leading '*'.
113fbdf8352SYitzhak Mandelbaum       StringRef Text =
114fbdf8352SYitzhak Mandelbaum           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115fbdf8352SYitzhak Mandelbaum       if (Text.empty())
1165891420eSKazu Hirata         return std::nullopt;
117fbdf8352SYitzhak Mandelbaum       return Text.str();
118fbdf8352SYitzhak Mandelbaum     }
119fbdf8352SYitzhak Mandelbaum   // Add leading '&'.
120fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
121fbdf8352SYitzhak Mandelbaum   if (Text.empty())
1225891420eSKazu Hirata     return std::nullopt;
123fbdf8352SYitzhak Mandelbaum   if (needParensAfterUnaryOperator(E)) {
124fbdf8352SYitzhak Mandelbaum     return ("&(" + Text + ")").str();
125fbdf8352SYitzhak Mandelbaum   }
126fbdf8352SYitzhak Mandelbaum   return ("&" + Text).str();
127fbdf8352SYitzhak Mandelbaum }
128fbdf8352SYitzhak Mandelbaum 
1290944c196SYitzhak Mandelbaum // Append the appropriate access operation (syntactically) to `E`, assuming `E`
1300944c196SYitzhak Mandelbaum // is a non-pointer value.
131*b0de3630SFangrui Song static std::optional<std::string>
buildAccessForValue(const Expr & E,const ASTContext & Context)1320944c196SYitzhak Mandelbaum buildAccessForValue(const Expr &E, const ASTContext &Context) {
133fbdf8352SYitzhak Mandelbaum   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
134fbdf8352SYitzhak Mandelbaum     if (Op->getOpcode() == UO_Deref) {
135fbdf8352SYitzhak Mandelbaum       // Strip leading '*', add following '->'.
136fbdf8352SYitzhak Mandelbaum       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137fbdf8352SYitzhak Mandelbaum       StringRef DerefText = getText(*SubExpr, Context);
138fbdf8352SYitzhak Mandelbaum       if (DerefText.empty())
1395891420eSKazu Hirata         return std::nullopt;
140fbdf8352SYitzhak Mandelbaum       if (needParensBeforeDotOrArrow(*SubExpr))
141fbdf8352SYitzhak Mandelbaum         return ("(" + DerefText + ")->").str();
142fbdf8352SYitzhak Mandelbaum       return (DerefText + "->").str();
143fbdf8352SYitzhak Mandelbaum     }
144fbdf8352SYitzhak Mandelbaum 
145fbdf8352SYitzhak Mandelbaum   // Add following '.'.
146fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
147fbdf8352SYitzhak Mandelbaum   if (Text.empty())
1485891420eSKazu Hirata     return std::nullopt;
149fbdf8352SYitzhak Mandelbaum   if (needParensBeforeDotOrArrow(E)) {
150fbdf8352SYitzhak Mandelbaum     return ("(" + Text + ").").str();
151fbdf8352SYitzhak Mandelbaum   }
152fbdf8352SYitzhak Mandelbaum   return (Text + ".").str();
153fbdf8352SYitzhak Mandelbaum }
154fbdf8352SYitzhak Mandelbaum 
1550944c196SYitzhak Mandelbaum // Append the appropriate access operation (syntactically) to `E`, assuming `E`
1560944c196SYitzhak Mandelbaum // is a pointer value.
157*b0de3630SFangrui Song static std::optional<std::string>
buildAccessForPointer(const Expr & E,const ASTContext & Context)1580944c196SYitzhak Mandelbaum buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159fbdf8352SYitzhak Mandelbaum   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
160fbdf8352SYitzhak Mandelbaum     if (Op->getOpcode() == UO_AddrOf) {
161fbdf8352SYitzhak Mandelbaum       // Strip leading '&', add following '.'.
162fbdf8352SYitzhak Mandelbaum       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163fbdf8352SYitzhak Mandelbaum       StringRef DerefText = getText(*SubExpr, Context);
164fbdf8352SYitzhak Mandelbaum       if (DerefText.empty())
1655891420eSKazu Hirata         return std::nullopt;
166fbdf8352SYitzhak Mandelbaum       if (needParensBeforeDotOrArrow(*SubExpr))
167fbdf8352SYitzhak Mandelbaum         return ("(" + DerefText + ").").str();
168fbdf8352SYitzhak Mandelbaum       return (DerefText + ".").str();
169fbdf8352SYitzhak Mandelbaum     }
170fbdf8352SYitzhak Mandelbaum 
171fbdf8352SYitzhak Mandelbaum   // Add following '->'.
172fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
173fbdf8352SYitzhak Mandelbaum   if (Text.empty())
1745891420eSKazu Hirata     return std::nullopt;
175fbdf8352SYitzhak Mandelbaum   if (needParensBeforeDotOrArrow(E))
176fbdf8352SYitzhak Mandelbaum     return ("(" + Text + ")->").str();
177fbdf8352SYitzhak Mandelbaum   return (Text + "->").str();
178fbdf8352SYitzhak Mandelbaum }
1790944c196SYitzhak Mandelbaum 
buildDot(const Expr & E,const ASTContext & Context)180*b0de3630SFangrui Song std::optional<std::string> tooling::buildDot(const Expr &E,
1810944c196SYitzhak Mandelbaum                                              const ASTContext &Context) {
1820944c196SYitzhak Mandelbaum   return buildAccessForValue(E, Context);
1830944c196SYitzhak Mandelbaum }
1840944c196SYitzhak Mandelbaum 
buildArrow(const Expr & E,const ASTContext & Context)185*b0de3630SFangrui Song std::optional<std::string> tooling::buildArrow(const Expr &E,
1860944c196SYitzhak Mandelbaum                                                const ASTContext &Context) {
1870944c196SYitzhak Mandelbaum   return buildAccessForPointer(E, Context);
1880944c196SYitzhak Mandelbaum }
1890944c196SYitzhak Mandelbaum 
1900944c196SYitzhak Mandelbaum // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
1910944c196SYitzhak Mandelbaum // `O`. Otherwise, returns `nullptr`.
maybeGetOperatorObjectArg(const Expr & E,OverloadedOperatorKind K)1920944c196SYitzhak Mandelbaum static const Expr *maybeGetOperatorObjectArg(const Expr &E,
1930944c196SYitzhak Mandelbaum                                              OverloadedOperatorKind K) {
1940944c196SYitzhak Mandelbaum   if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {
1950944c196SYitzhak Mandelbaum     if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)
1960944c196SYitzhak Mandelbaum       return OpCall->getArg(0);
1970944c196SYitzhak Mandelbaum   }
1980944c196SYitzhak Mandelbaum   return nullptr;
1990944c196SYitzhak Mandelbaum }
2000944c196SYitzhak Mandelbaum 
treatLikePointer(QualType Ty,PLTClass C,ASTContext & Context)2010944c196SYitzhak Mandelbaum static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
2020944c196SYitzhak Mandelbaum   switch (C) {
2030944c196SYitzhak Mandelbaum   case PLTClass::Value:
2040944c196SYitzhak Mandelbaum     return false;
2050944c196SYitzhak Mandelbaum   case PLTClass::Pointer:
2060944c196SYitzhak Mandelbaum     return isKnownPointerLikeType(Ty, Context);
2070944c196SYitzhak Mandelbaum   }
20804754af9SSimon Pilgrim   llvm_unreachable("Unknown PLTClass enum");
2090944c196SYitzhak Mandelbaum }
2100944c196SYitzhak Mandelbaum 
2110944c196SYitzhak Mandelbaum // FIXME: move over the other `maybe` functionality from Stencil. Should all be
2120944c196SYitzhak Mandelbaum // in one place.
buildAccess(const Expr & RawExpression,ASTContext & Context,PLTClass Classification)213*b0de3630SFangrui Song std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
2140944c196SYitzhak Mandelbaum                                                 ASTContext &Context,
2150944c196SYitzhak Mandelbaum                                                 PLTClass Classification) {
2160944c196SYitzhak Mandelbaum   if (RawExpression.isImplicitCXXThis())
2171f88fb40SKazu Hirata     // Return the empty string, because `std::nullopt` signifies some sort of
2181f88fb40SKazu Hirata     // failure.
2190944c196SYitzhak Mandelbaum     return std::string();
2200944c196SYitzhak Mandelbaum 
2210944c196SYitzhak Mandelbaum   const Expr *E = RawExpression.IgnoreImplicitAsWritten();
2220944c196SYitzhak Mandelbaum 
2230944c196SYitzhak Mandelbaum   if (E->getType()->isAnyPointerType() ||
2240944c196SYitzhak Mandelbaum       treatLikePointer(E->getType(), Classification, Context)) {
2250944c196SYitzhak Mandelbaum     // Strip off operator-> calls. They can only occur inside an actual arrow
2260944c196SYitzhak Mandelbaum     // member access, so we treat them as equivalent to an actual object
2270944c196SYitzhak Mandelbaum     // expression.
2280944c196SYitzhak Mandelbaum     if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
2290944c196SYitzhak Mandelbaum       E = Obj;
2300944c196SYitzhak Mandelbaum     return buildAccessForPointer(*E, Context);
2310944c196SYitzhak Mandelbaum   }
2320944c196SYitzhak Mandelbaum 
2330944c196SYitzhak Mandelbaum   if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
2340944c196SYitzhak Mandelbaum     if (treatLikePointer(Obj->getType(), Classification, Context))
2350944c196SYitzhak Mandelbaum       return buildAccessForPointer(*Obj, Context);
2360944c196SYitzhak Mandelbaum   };
2370944c196SYitzhak Mandelbaum 
2380944c196SYitzhak Mandelbaum   return buildAccessForValue(*E, Context);
2390944c196SYitzhak Mandelbaum }
240