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 std::optional<std::string> tooling::buildParens(const Expr &E, 76 const ASTContext &Context) { 77 StringRef Text = getText(E, Context); 78 if (Text.empty()) 79 return std::nullopt; 80 if (mayEverNeedParens(E)) 81 return ("(" + Text + ")").str(); 82 return Text.str(); 83 } 84 85 std::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 std::nullopt; 94 return Text.str(); 95 } 96 97 StringRef Text = getText(E, Context); 98 if (Text.empty()) 99 return std::nullopt; 100 // Add leading '*'. 101 if (needParensAfterUnaryOperator(E)) 102 return ("*(" + Text + ")").str(); 103 return ("*" + Text).str(); 104 } 105 106 std::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 std::nullopt; 117 return Text.str(); 118 } 119 // Add leading '&'. 120 StringRef Text = getText(E, Context); 121 if (Text.empty()) 122 return std::nullopt; 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 std::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 std::nullopt; 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 std::nullopt; 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 std::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 std::nullopt; 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 std::nullopt; 175 if (needParensBeforeDotOrArrow(E)) 176 return ("(" + Text + ")->").str(); 177 return (Text + "->").str(); 178 } 179 180 std::optional<std::string> tooling::buildDot(const Expr &E, 181 const ASTContext &Context) { 182 return buildAccessForValue(E, Context); 183 } 184 185 std::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 std::optional<std::string> tooling::buildAccess(const Expr &RawExpression, 214 ASTContext &Context, 215 PLTClass Classification) { 216 if (RawExpression.isImplicitCXXThis()) 217 // Return the empty string, because `std::nullopt` signifies some sort of 218 // failure. 219 return std::string(); 220 221 const Expr *E = RawExpression.IgnoreImplicitAsWritten(); 222 223 if (E->getType()->isAnyPointerType() || 224 treatLikePointer(E->getType(), Classification, Context)) { 225 // Strip off operator-> calls. They can only occur inside an actual arrow 226 // member access, so we treat them as equivalent to an actual object 227 // expression. 228 if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow)) 229 E = Obj; 230 return buildAccessForPointer(*E, Context); 231 } 232 233 if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) { 234 if (treatLikePointer(Obj->getType(), Classification, Context)) 235 return buildAccessForPointer(*Obj, Context); 236 }; 237 238 return buildAccessForValue(*E, Context); 239 } 240