240 lines · cpp
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-exception6//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 19using namespace clang;20using namespace tooling;21 22const 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 32bool tooling::mayEverNeedParens(const Expr &E) {33 const Expr *Expr = reallyIgnoreImplicit(E);34 // We always want parens around unary, binary, and ternary operators, because35 // 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: function41 // calls, subscripts, and expressions that are already part of an (implicit)42 // call to operator->. These latter are all in the same precedence level as43 // dot/arrow and that level is left associative, so they don't need parens44 // 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 52bool 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 65bool 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 75std::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 85std::optional<std::string>86tooling::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 106std::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.131static std::optional<std::string>132buildAccessForValue(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.157static std::optional<std::string>158buildAccessForPointer(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 180std::optional<std::string> tooling::buildDot(const Expr &E,181 const ASTContext &Context) {182 return buildAccessForValue(E, Context);183}184 185std::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`, returns191// `O`. Otherwise, returns `nullptr`.192static 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 201static 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 be212// in one place.213std::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 of218 // 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 arrow226 // member access, so we treat them as equivalent to an actual object227 // 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