brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.4 KiB · 10588a3 Raw
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