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/Tooling/Transformer/SourceCode.h" 14 #include "llvm/ADT/Twine.h" 15 #include <string> 16 17 using namespace clang; 18 using namespace tooling; 19 20 const Expr *tooling::reallyIgnoreImplicit(const Expr &E) { 21 const Expr *Expr = E.IgnoreImplicit(); 22 if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) { 23 if (CE->getNumArgs() > 0 && 24 CE->getArg(0)->getSourceRange() == Expr->getSourceRange()) 25 return CE->getArg(0)->IgnoreImplicit(); 26 } 27 return Expr; 28 } 29 30 bool tooling::mayEverNeedParens(const Expr &E) { 31 const Expr *Expr = reallyIgnoreImplicit(E); 32 // We always want parens around unary, binary, and ternary operators, because 33 // they are lower precedence. 34 if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) || 35 isa<AbstractConditionalOperator>(Expr)) 36 return true; 37 38 // We need parens around calls to all overloaded operators except: function 39 // calls, subscripts, and expressions that are already part of an (implicit) 40 // call to operator->. These latter are all in the same precedence level as 41 // dot/arrow and that level is left associative, so they don't need parens 42 // when appearing on the left. 43 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr)) 44 return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript && 45 Op->getOperator() != OO_Arrow; 46 47 return false; 48 } 49 50 bool tooling::needParensAfterUnaryOperator(const Expr &E) { 51 const Expr *Expr = reallyIgnoreImplicit(E); 52 if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr)) 53 return true; 54 55 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr)) 56 return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus && 57 Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call && 58 Op->getOperator() != OO_Subscript; 59 60 return false; 61 } 62 63 llvm::Optional<std::string> tooling::buildParens(const Expr &E, 64 const ASTContext &Context) { 65 StringRef Text = getText(E, Context); 66 if (Text.empty()) 67 return llvm::None; 68 if (mayEverNeedParens(E)) 69 return ("(" + Text + ")").str(); 70 return Text.str(); 71 } 72 73 llvm::Optional<std::string> 74 tooling::buildDereference(const Expr &E, const ASTContext &Context) { 75 if (const auto *Op = dyn_cast<UnaryOperator>(&E)) 76 if (Op->getOpcode() == UO_AddrOf) { 77 // Strip leading '&'. 78 StringRef Text = 79 getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); 80 if (Text.empty()) 81 return llvm::None; 82 return Text.str(); 83 } 84 85 StringRef Text = getText(E, Context); 86 if (Text.empty()) 87 return llvm::None; 88 // Add leading '*'. 89 if (needParensAfterUnaryOperator(E)) 90 return ("*(" + Text + ")").str(); 91 return ("*" + Text).str(); 92 } 93 94 llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E, 95 const ASTContext &Context) { 96 if (E.isImplicitCXXThis()) 97 return std::string("this"); 98 if (const auto *Op = dyn_cast<UnaryOperator>(&E)) 99 if (Op->getOpcode() == UO_Deref) { 100 // Strip leading '*'. 101 StringRef Text = 102 getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); 103 if (Text.empty()) 104 return llvm::None; 105 return Text.str(); 106 } 107 // Add leading '&'. 108 StringRef Text = getText(E, Context); 109 if (Text.empty()) 110 return llvm::None; 111 if (needParensAfterUnaryOperator(E)) { 112 return ("&(" + Text + ")").str(); 113 } 114 return ("&" + Text).str(); 115 } 116 117 llvm::Optional<std::string> tooling::buildDot(const Expr &E, 118 const ASTContext &Context) { 119 if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E)) 120 if (Op->getOpcode() == UO_Deref) { 121 // Strip leading '*', add following '->'. 122 const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); 123 StringRef DerefText = getText(*SubExpr, Context); 124 if (DerefText.empty()) 125 return llvm::None; 126 if (needParensBeforeDotOrArrow(*SubExpr)) 127 return ("(" + DerefText + ")->").str(); 128 return (DerefText + "->").str(); 129 } 130 131 // Add following '.'. 132 StringRef Text = getText(E, Context); 133 if (Text.empty()) 134 return llvm::None; 135 if (needParensBeforeDotOrArrow(E)) { 136 return ("(" + Text + ").").str(); 137 } 138 return (Text + ".").str(); 139 } 140 141 llvm::Optional<std::string> tooling::buildArrow(const Expr &E, 142 const ASTContext &Context) { 143 if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E)) 144 if (Op->getOpcode() == UO_AddrOf) { 145 // Strip leading '&', add following '.'. 146 const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); 147 StringRef DerefText = getText(*SubExpr, Context); 148 if (DerefText.empty()) 149 return llvm::None; 150 if (needParensBeforeDotOrArrow(*SubExpr)) 151 return ("(" + DerefText + ").").str(); 152 return (DerefText + ".").str(); 153 } 154 155 // Add following '->'. 156 StringRef Text = getText(E, Context); 157 if (Text.empty()) 158 return llvm::None; 159 if (needParensBeforeDotOrArrow(E)) 160 return ("(" + Text + ")->").str(); 161 return (Text + "->").str(); 162 } 163