//===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Tooling/Transformer/SourceCodeBuilders.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Transformer/SourceCode.h" #include "llvm/ADT/Twine.h" #include using namespace clang; using namespace tooling; const Expr *tooling::reallyIgnoreImplicit(const Expr &E) { const Expr *Expr = E.IgnoreImplicit(); if (const auto *CE = dyn_cast(Expr)) { if (CE->getNumArgs() > 0 && CE->getArg(0)->getSourceRange() == Expr->getSourceRange()) return CE->getArg(0)->IgnoreImplicit(); } return Expr; } bool tooling::mayEverNeedParens(const Expr &E) { const Expr *Expr = reallyIgnoreImplicit(E); // We always want parens around unary, binary, and ternary operators, because // they are lower precedence. if (isa(Expr) || isa(Expr) || isa(Expr)) return true; // We need parens around calls to all overloaded operators except: function // calls, subscripts, and expressions that are already part of an (implicit) // call to operator->. These latter are all in the same precedence level as // dot/arrow and that level is left associative, so they don't need parens // when appearing on the left. if (const auto *Op = dyn_cast(Expr)) return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript && Op->getOperator() != OO_Arrow; return false; } bool tooling::needParensAfterUnaryOperator(const Expr &E) { const Expr *Expr = reallyIgnoreImplicit(E); if (isa(Expr) || isa(Expr)) return true; if (const auto *Op = dyn_cast(Expr)) return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus && Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript; return false; } bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) { using namespace ast_matchers; const auto PointerLikeTy = type(hasUnqualifiedDesugaredType( recordType(hasDeclaration(cxxRecordDecl(hasAnyName( "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr", "::std::optional", "::absl::optional", "::llvm::Optional", "absl::StatusOr", "::llvm::Expected")))))); return match(PointerLikeTy, Ty, Context).size() > 0; } std::optional tooling::buildParens(const Expr &E, const ASTContext &Context) { StringRef Text = getText(E, Context); if (Text.empty()) return std::nullopt; if (mayEverNeedParens(E)) return ("(" + Text + ")").str(); return Text.str(); } std::optional tooling::buildDereference(const Expr &E, const ASTContext &Context) { if (const auto *Op = dyn_cast(&E)) if (Op->getOpcode() == UO_AddrOf) { // Strip leading '&'. StringRef Text = getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); if (Text.empty()) return std::nullopt; return Text.str(); } StringRef Text = getText(E, Context); if (Text.empty()) return std::nullopt; // Add leading '*'. if (needParensAfterUnaryOperator(E)) return ("*(" + Text + ")").str(); return ("*" + Text).str(); } std::optional tooling::buildAddressOf(const Expr &E, const ASTContext &Context) { if (E.isImplicitCXXThis()) return std::string("this"); if (const auto *Op = dyn_cast(&E)) if (Op->getOpcode() == UO_Deref) { // Strip leading '*'. StringRef Text = getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); if (Text.empty()) return std::nullopt; return Text.str(); } // Add leading '&'. StringRef Text = getText(E, Context); if (Text.empty()) return std::nullopt; if (needParensAfterUnaryOperator(E)) { return ("&(" + Text + ")").str(); } return ("&" + Text).str(); } // Append the appropriate access operation (syntactically) to `E`, assuming `E` // is a non-pointer value. static std::optional buildAccessForValue(const Expr &E, const ASTContext &Context) { if (const auto *Op = llvm::dyn_cast(&E)) if (Op->getOpcode() == UO_Deref) { // Strip leading '*', add following '->'. const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); StringRef DerefText = getText(*SubExpr, Context); if (DerefText.empty()) return std::nullopt; if (needParensBeforeDotOrArrow(*SubExpr)) return ("(" + DerefText + ")->").str(); return (DerefText + "->").str(); } // Add following '.'. StringRef Text = getText(E, Context); if (Text.empty()) return std::nullopt; if (needParensBeforeDotOrArrow(E)) { return ("(" + Text + ").").str(); } return (Text + ".").str(); } // Append the appropriate access operation (syntactically) to `E`, assuming `E` // is a pointer value. static std::optional buildAccessForPointer(const Expr &E, const ASTContext &Context) { if (const auto *Op = llvm::dyn_cast(&E)) if (Op->getOpcode() == UO_AddrOf) { // Strip leading '&', add following '.'. const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); StringRef DerefText = getText(*SubExpr, Context); if (DerefText.empty()) return std::nullopt; if (needParensBeforeDotOrArrow(*SubExpr)) return ("(" + DerefText + ").").str(); return (DerefText + ".").str(); } // Add following '->'. StringRef Text = getText(E, Context); if (Text.empty()) return std::nullopt; if (needParensBeforeDotOrArrow(E)) return ("(" + Text + ")->").str(); return (Text + "->").str(); } std::optional tooling::buildDot(const Expr &E, const ASTContext &Context) { return buildAccessForValue(E, Context); } std::optional tooling::buildArrow(const Expr &E, const ASTContext &Context) { return buildAccessForPointer(E, Context); } // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns // `O`. Otherwise, returns `nullptr`. static const Expr *maybeGetOperatorObjectArg(const Expr &E, OverloadedOperatorKind K) { if (const auto *OpCall = dyn_cast(&E)) { if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1) return OpCall->getArg(0); } return nullptr; } static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) { switch (C) { case PLTClass::Value: return false; case PLTClass::Pointer: return isKnownPointerLikeType(Ty, Context); } llvm_unreachable("Unknown PLTClass enum"); } // FIXME: move over the other `maybe` functionality from Stencil. Should all be // in one place. std::optional tooling::buildAccess(const Expr &RawExpression, ASTContext &Context, PLTClass Classification) { if (RawExpression.isImplicitCXXThis()) // Return the empty string, because `std::nullopt` signifies some sort of // failure. return std::string(); const Expr *E = RawExpression.IgnoreImplicitAsWritten(); if (E->getType()->isAnyPointerType() || treatLikePointer(E->getType(), Classification, Context)) { // Strip off operator-> calls. They can only occur inside an actual arrow // member access, so we treat them as equivalent to an actual object // expression. if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow)) E = Obj; return buildAccessForPointer(*E, Context); } if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) { if (treatLikePointer(Obj->getType(), Classification, Context)) return buildAccessForPointer(*Obj, Context); }; return buildAccessForValue(*E, Context); }