mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
239 lines
8.4 KiB
C++
239 lines
8.4 KiB
C++
//===--- 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);
|
|
}
|