Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
35266 views
//===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Tooling/Transformer/SourceCodeBuilders.h"9#include "clang/AST/ASTContext.h"10#include "clang/AST/Expr.h"11#include "clang/AST/ExprCXX.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Tooling/Transformer/SourceCode.h"15#include "llvm/ADT/Twine.h"16#include <string>1718using namespace clang;19using namespace tooling;2021const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {22const Expr *Expr = E.IgnoreImplicit();23if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {24if (CE->getNumArgs() > 0 &&25CE->getArg(0)->getSourceRange() == Expr->getSourceRange())26return CE->getArg(0)->IgnoreImplicit();27}28return Expr;29}3031bool tooling::mayEverNeedParens(const Expr &E) {32const Expr *Expr = reallyIgnoreImplicit(E);33// We always want parens around unary, binary, and ternary operators, because34// they are lower precedence.35if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||36isa<AbstractConditionalOperator>(Expr))37return true;3839// We need parens around calls to all overloaded operators except: function40// calls, subscripts, and expressions that are already part of an (implicit)41// call to operator->. These latter are all in the same precedence level as42// dot/arrow and that level is left associative, so they don't need parens43// when appearing on the left.44if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))45return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&46Op->getOperator() != OO_Arrow;4748return false;49}5051bool tooling::needParensAfterUnaryOperator(const Expr &E) {52const Expr *Expr = reallyIgnoreImplicit(E);53if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))54return true;5556if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))57return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&58Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&59Op->getOperator() != OO_Subscript;6061return false;62}6364bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {65using namespace ast_matchers;66const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(67recordType(hasDeclaration(cxxRecordDecl(hasAnyName(68"::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",69"::std::optional", "::absl::optional", "::llvm::Optional",70"absl::StatusOr", "::llvm::Expected"))))));71return match(PointerLikeTy, Ty, Context).size() > 0;72}7374std::optional<std::string> tooling::buildParens(const Expr &E,75const ASTContext &Context) {76StringRef Text = getText(E, Context);77if (Text.empty())78return std::nullopt;79if (mayEverNeedParens(E))80return ("(" + Text + ")").str();81return Text.str();82}8384std::optional<std::string>85tooling::buildDereference(const Expr &E, const ASTContext &Context) {86if (const auto *Op = dyn_cast<UnaryOperator>(&E))87if (Op->getOpcode() == UO_AddrOf) {88// Strip leading '&'.89StringRef Text =90getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);91if (Text.empty())92return std::nullopt;93return Text.str();94}9596StringRef Text = getText(E, Context);97if (Text.empty())98return std::nullopt;99// Add leading '*'.100if (needParensAfterUnaryOperator(E))101return ("*(" + Text + ")").str();102return ("*" + Text).str();103}104105std::optional<std::string> tooling::buildAddressOf(const Expr &E,106const ASTContext &Context) {107if (E.isImplicitCXXThis())108return std::string("this");109if (const auto *Op = dyn_cast<UnaryOperator>(&E))110if (Op->getOpcode() == UO_Deref) {111// Strip leading '*'.112StringRef Text =113getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);114if (Text.empty())115return std::nullopt;116return Text.str();117}118// Add leading '&'.119StringRef Text = getText(E, Context);120if (Text.empty())121return std::nullopt;122if (needParensAfterUnaryOperator(E)) {123return ("&(" + Text + ")").str();124}125return ("&" + Text).str();126}127128// Append the appropriate access operation (syntactically) to `E`, assuming `E`129// is a non-pointer value.130static std::optional<std::string>131buildAccessForValue(const Expr &E, const ASTContext &Context) {132if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))133if (Op->getOpcode() == UO_Deref) {134// Strip leading '*', add following '->'.135const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();136StringRef DerefText = getText(*SubExpr, Context);137if (DerefText.empty())138return std::nullopt;139if (needParensBeforeDotOrArrow(*SubExpr))140return ("(" + DerefText + ")->").str();141return (DerefText + "->").str();142}143144// Add following '.'.145StringRef Text = getText(E, Context);146if (Text.empty())147return std::nullopt;148if (needParensBeforeDotOrArrow(E)) {149return ("(" + Text + ").").str();150}151return (Text + ".").str();152}153154// Append the appropriate access operation (syntactically) to `E`, assuming `E`155// is a pointer value.156static std::optional<std::string>157buildAccessForPointer(const Expr &E, const ASTContext &Context) {158if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))159if (Op->getOpcode() == UO_AddrOf) {160// Strip leading '&', add following '.'.161const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();162StringRef DerefText = getText(*SubExpr, Context);163if (DerefText.empty())164return std::nullopt;165if (needParensBeforeDotOrArrow(*SubExpr))166return ("(" + DerefText + ").").str();167return (DerefText + ".").str();168}169170// Add following '->'.171StringRef Text = getText(E, Context);172if (Text.empty())173return std::nullopt;174if (needParensBeforeDotOrArrow(E))175return ("(" + Text + ")->").str();176return (Text + "->").str();177}178179std::optional<std::string> tooling::buildDot(const Expr &E,180const ASTContext &Context) {181return buildAccessForValue(E, Context);182}183184std::optional<std::string> tooling::buildArrow(const Expr &E,185const ASTContext &Context) {186return buildAccessForPointer(E, Context);187}188189// If `E` is an overloaded-operator call of kind `K` on an object `O`, returns190// `O`. Otherwise, returns `nullptr`.191static const Expr *maybeGetOperatorObjectArg(const Expr &E,192OverloadedOperatorKind K) {193if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {194if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)195return OpCall->getArg(0);196}197return nullptr;198}199200static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {201switch (C) {202case PLTClass::Value:203return false;204case PLTClass::Pointer:205return isKnownPointerLikeType(Ty, Context);206}207llvm_unreachable("Unknown PLTClass enum");208}209210// FIXME: move over the other `maybe` functionality from Stencil. Should all be211// in one place.212std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,213ASTContext &Context,214PLTClass Classification) {215if (RawExpression.isImplicitCXXThis())216// Return the empty string, because `std::nullopt` signifies some sort of217// failure.218return std::string();219220const Expr *E = RawExpression.IgnoreImplicitAsWritten();221222if (E->getType()->isAnyPointerType() ||223treatLikePointer(E->getType(), Classification, Context)) {224// Strip off operator-> calls. They can only occur inside an actual arrow225// member access, so we treat them as equivalent to an actual object226// expression.227if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))228E = Obj;229return buildAccessForPointer(*E, Context);230}231232if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {233if (treatLikePointer(Obj->getType(), Classification, Context))234return buildAccessForPointer(*Obj, Context);235};236237return buildAccessForValue(*E, Context);238}239240241