#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);
if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
isa<AbstractConditionalOperator>(Expr))
return true;
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) {
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;
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) {
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;
if (needParensAfterUnaryOperator(E)) {
return ("&(" + Text + ")").str();
}
return ("&" + Text).str();
}
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) {
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();
}
StringRef Text = getText(E, Context);
if (Text.empty())
return llvm::None;
if (needParensBeforeDotOrArrow(E)) {
return ("(" + Text + ").").str();
}
return (Text + ".").str();
}
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) {
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();
}
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);
}
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");
}
llvm::Optional<std::string> tooling::buildAccess(const Expr &RawExpression,
ASTContext &Context,
PLTClass Classification) {
if (RawExpression.isImplicitCXXThis())
return std::string();
const Expr *E = RawExpression.IgnoreImplicitAsWritten();
if (E->getType()->isAnyPointerType() ||
treatLikePointer(E->getType(), Classification, Context)) {
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);
}