123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- //===--- 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 <string>
- using namespace clang;
- using namespace tooling;
- const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
- const Expr *Expr = E.IgnoreImplicit();
- if (const auto *CE = dyn_cast<CXXConstructExpr>(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<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
- isa<AbstractConditionalOperator>(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<CXXOperatorCallExpr>(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<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
- return true;
- if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(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;
- }
- llvm::Optional<std::string> tooling::buildParens(const Expr &E,
- const ASTContext &Context) {
- StringRef Text = getText(E, Context);
- if (Text.empty())
- return llvm::None;
- if (mayEverNeedParens(E))
- return ("(" + Text + ")").str();
- return Text.str();
- }
- llvm::Optional<std::string>
- tooling::buildDereference(const Expr &E, const ASTContext &Context) {
- if (const auto *Op = dyn_cast<UnaryOperator>(&E))
- if (Op->getOpcode() == UO_AddrOf) {
- // Strip leading '&'.
- StringRef Text =
- getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
- if (Text.empty())
- return llvm::None;
- return Text.str();
- }
- StringRef Text = getText(E, Context);
- if (Text.empty())
- return llvm::None;
- // Add leading '*'.
- if (needParensAfterUnaryOperator(E))
- return ("*(" + Text + ")").str();
- return ("*" + Text).str();
- }
- llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E,
- const ASTContext &Context) {
- if (E.isImplicitCXXThis())
- return std::string("this");
- if (const auto *Op = dyn_cast<UnaryOperator>(&E))
- if (Op->getOpcode() == UO_Deref) {
- // Strip leading '*'.
- StringRef Text =
- getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
- if (Text.empty())
- return llvm::None;
- return Text.str();
- }
- // Add leading '&'.
- StringRef Text = getText(E, Context);
- if (Text.empty())
- return llvm::None;
- 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 llvm::Optional<std::string>
- buildAccessForValue(const Expr &E, const ASTContext &Context) {
- if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&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 llvm::None;
- if (needParensBeforeDotOrArrow(*SubExpr))
- return ("(" + DerefText + ")->").str();
- return (DerefText + "->").str();
- }
- // Add following '.'.
- StringRef Text = getText(E, Context);
- if (Text.empty())
- return llvm::None;
- if (needParensBeforeDotOrArrow(E)) {
- return ("(" + Text + ").").str();
- }
- return (Text + ".").str();
- }
- // Append the appropriate access operation (syntactically) to `E`, assuming `E`
- // is a pointer value.
- static llvm::Optional<std::string>
- buildAccessForPointer(const Expr &E, const ASTContext &Context) {
- if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&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 llvm::None;
- if (needParensBeforeDotOrArrow(*SubExpr))
- return ("(" + DerefText + ").").str();
- return (DerefText + ".").str();
- }
- // Add following '->'.
- StringRef Text = getText(E, Context);
- if (Text.empty())
- return llvm::None;
- if (needParensBeforeDotOrArrow(E))
- return ("(" + Text + ")->").str();
- return (Text + "->").str();
- }
- llvm::Optional<std::string> tooling::buildDot(const Expr &E,
- const ASTContext &Context) {
- return buildAccessForValue(E, Context);
- }
- llvm::Optional<std::string> 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<clang::CXXOperatorCallExpr>(&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.
- llvm::Optional<std::string> tooling::buildAccess(const Expr &RawExpression,
- ASTContext &Context,
- PLTClass Classification) {
- if (RawExpression.isImplicitCXXThis())
- // Return the empty string, because `None` 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);
- }
|