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)75fbdf8352SYitzhak Mandelbaum llvm::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())
79fbdf8352SYitzhak Mandelbaum     return llvm::None;
80fbdf8352SYitzhak Mandelbaum   if (mayEverNeedParens(E))
81fbdf8352SYitzhak Mandelbaum     return ("(" + Text + ")").str();
82fbdf8352SYitzhak Mandelbaum   return Text.str();
83fbdf8352SYitzhak Mandelbaum }
84fbdf8352SYitzhak Mandelbaum 
85fbdf8352SYitzhak Mandelbaum llvm::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())
93fbdf8352SYitzhak Mandelbaum         return llvm::None;
94fbdf8352SYitzhak Mandelbaum       return Text.str();
95fbdf8352SYitzhak Mandelbaum     }
96fbdf8352SYitzhak Mandelbaum 
97fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
98fbdf8352SYitzhak Mandelbaum   if (Text.empty())
99fbdf8352SYitzhak Mandelbaum     return llvm::None;
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)106fbdf8352SYitzhak Mandelbaum llvm::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())
116fbdf8352SYitzhak Mandelbaum         return llvm::None;
117fbdf8352SYitzhak Mandelbaum       return Text.str();
118fbdf8352SYitzhak Mandelbaum     }
119fbdf8352SYitzhak Mandelbaum   // Add leading '&'.
120fbdf8352SYitzhak Mandelbaum   StringRef Text = getText(E, Context);
121fbdf8352SYitzhak Mandelbaum   if (Text.empty())
122fbdf8352SYitzhak Mandelbaum     return llvm::None;
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.
1310944c196SYitzhak Mandelbaum static llvm::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())
139fbdf8352SYitzhak Mandelbaum         return llvm::None;
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())
148fbdf8352SYitzhak Mandelbaum     return llvm::None;
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.
1570944c196SYitzhak Mandelbaum static llvm::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())
165fbdf8352SYitzhak Mandelbaum         return llvm::None;
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())
174fbdf8352SYitzhak Mandelbaum     return llvm::None;
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)1800944c196SYitzhak Mandelbaum llvm::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)1850944c196SYitzhak Mandelbaum llvm::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   }
208*04754af9SSimon 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)2130944c196SYitzhak Mandelbaum llvm::Optional<std::string> tooling::buildAccess(const Expr &RawExpression,
2140944c196SYitzhak Mandelbaum                                                  ASTContext &Context,
2150944c196SYitzhak Mandelbaum                                                  PLTClass Classification) {
2160944c196SYitzhak Mandelbaum   if (RawExpression.isImplicitCXXThis())
2170944c196SYitzhak Mandelbaum     // Return the empty string, because `None` signifies some sort of failure.
2180944c196SYitzhak Mandelbaum     return std::string();
2190944c196SYitzhak Mandelbaum 
2200944c196SYitzhak Mandelbaum   const Expr *E = RawExpression.IgnoreImplicitAsWritten();
2210944c196SYitzhak Mandelbaum 
2220944c196SYitzhak Mandelbaum   if (E->getType()->isAnyPointerType() ||
2230944c196SYitzhak Mandelbaum       treatLikePointer(E->getType(), Classification, Context)) {
2240944c196SYitzhak Mandelbaum     // Strip off operator-> calls. They can only occur inside an actual arrow
2250944c196SYitzhak Mandelbaum     // member access, so we treat them as equivalent to an actual object
2260944c196SYitzhak Mandelbaum     // expression.
2270944c196SYitzhak Mandelbaum     if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
2280944c196SYitzhak Mandelbaum       E = Obj;
2290944c196SYitzhak Mandelbaum     return buildAccessForPointer(*E, Context);
2300944c196SYitzhak Mandelbaum   }
2310944c196SYitzhak Mandelbaum 
2320944c196SYitzhak Mandelbaum   if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
2330944c196SYitzhak Mandelbaum     if (treatLikePointer(Obj->getType(), Classification, Context))
2340944c196SYitzhak Mandelbaum       return buildAccessForPointer(*Obj, Context);
2350944c196SYitzhak Mandelbaum   };
2360944c196SYitzhak Mandelbaum 
2370944c196SYitzhak Mandelbaum   return buildAccessForValue(*E, Context);
2380944c196SYitzhak Mandelbaum }
239