1a7dea167SDimitry Andric //===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric
9a7dea167SDimitry Andric #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10a7dea167SDimitry Andric #include "clang/AST/ASTContext.h"
11a7dea167SDimitry Andric #include "clang/AST/Expr.h"
12a7dea167SDimitry Andric #include "clang/AST/ExprCXX.h"
1304eeddc0SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
1404eeddc0SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
15a7dea167SDimitry Andric #include "clang/Tooling/Transformer/SourceCode.h"
16a7dea167SDimitry Andric #include "llvm/ADT/Twine.h"
17a7dea167SDimitry Andric #include <string>
18a7dea167SDimitry Andric
19a7dea167SDimitry Andric using namespace clang;
20a7dea167SDimitry Andric using namespace tooling;
21a7dea167SDimitry Andric
reallyIgnoreImplicit(const Expr & E)22a7dea167SDimitry Andric const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23a7dea167SDimitry Andric const Expr *Expr = E.IgnoreImplicit();
24a7dea167SDimitry Andric if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
25a7dea167SDimitry Andric if (CE->getNumArgs() > 0 &&
26a7dea167SDimitry Andric CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
27a7dea167SDimitry Andric return CE->getArg(0)->IgnoreImplicit();
28a7dea167SDimitry Andric }
29a7dea167SDimitry Andric return Expr;
30a7dea167SDimitry Andric }
31a7dea167SDimitry Andric
mayEverNeedParens(const Expr & E)32a7dea167SDimitry Andric bool tooling::mayEverNeedParens(const Expr &E) {
33a7dea167SDimitry Andric const Expr *Expr = reallyIgnoreImplicit(E);
34a7dea167SDimitry Andric // We always want parens around unary, binary, and ternary operators, because
35a7dea167SDimitry Andric // they are lower precedence.
36a7dea167SDimitry Andric if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
37a7dea167SDimitry Andric isa<AbstractConditionalOperator>(Expr))
38a7dea167SDimitry Andric return true;
39a7dea167SDimitry Andric
40a7dea167SDimitry Andric // We need parens around calls to all overloaded operators except: function
41a7dea167SDimitry Andric // calls, subscripts, and expressions that are already part of an (implicit)
42a7dea167SDimitry Andric // call to operator->. These latter are all in the same precedence level as
43a7dea167SDimitry Andric // dot/arrow and that level is left associative, so they don't need parens
44a7dea167SDimitry Andric // when appearing on the left.
45a7dea167SDimitry Andric if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
46a7dea167SDimitry Andric return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47a7dea167SDimitry Andric Op->getOperator() != OO_Arrow;
48a7dea167SDimitry Andric
49a7dea167SDimitry Andric return false;
50a7dea167SDimitry Andric }
51a7dea167SDimitry Andric
needParensAfterUnaryOperator(const Expr & E)52a7dea167SDimitry Andric bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53a7dea167SDimitry Andric const Expr *Expr = reallyIgnoreImplicit(E);
54a7dea167SDimitry Andric if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
55a7dea167SDimitry Andric return true;
56a7dea167SDimitry Andric
57a7dea167SDimitry Andric if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
58a7dea167SDimitry Andric return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
59a7dea167SDimitry Andric Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
60a7dea167SDimitry Andric Op->getOperator() != OO_Subscript;
61a7dea167SDimitry Andric
62a7dea167SDimitry Andric return false;
63a7dea167SDimitry Andric }
64a7dea167SDimitry Andric
isKnownPointerLikeType(QualType Ty,ASTContext & Context)6504eeddc0SDimitry Andric bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
6604eeddc0SDimitry Andric using namespace ast_matchers;
6704eeddc0SDimitry Andric const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
6804eeddc0SDimitry Andric recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
6904eeddc0SDimitry Andric "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
7004eeddc0SDimitry Andric "::std::optional", "::absl::optional", "::llvm::Optional",
7104eeddc0SDimitry Andric "absl::StatusOr", "::llvm::Expected"))))));
7204eeddc0SDimitry Andric return match(PointerLikeTy, Ty, Context).size() > 0;
7304eeddc0SDimitry Andric }
7404eeddc0SDimitry Andric
buildParens(const Expr & E,const ASTContext & Context)75*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildParens(const Expr &E,
76a7dea167SDimitry Andric const ASTContext &Context) {
77a7dea167SDimitry Andric StringRef Text = getText(E, Context);
78a7dea167SDimitry Andric if (Text.empty())
79*bdd1243dSDimitry Andric return std::nullopt;
80a7dea167SDimitry Andric if (mayEverNeedParens(E))
81a7dea167SDimitry Andric return ("(" + Text + ")").str();
82a7dea167SDimitry Andric return Text.str();
83a7dea167SDimitry Andric }
84a7dea167SDimitry Andric
85*bdd1243dSDimitry Andric std::optional<std::string>
buildDereference(const Expr & E,const ASTContext & Context)86a7dea167SDimitry Andric tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87a7dea167SDimitry Andric if (const auto *Op = dyn_cast<UnaryOperator>(&E))
88a7dea167SDimitry Andric if (Op->getOpcode() == UO_AddrOf) {
89a7dea167SDimitry Andric // Strip leading '&'.
90a7dea167SDimitry Andric StringRef Text =
91a7dea167SDimitry Andric getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92a7dea167SDimitry Andric if (Text.empty())
93*bdd1243dSDimitry Andric return std::nullopt;
94a7dea167SDimitry Andric return Text.str();
95a7dea167SDimitry Andric }
96a7dea167SDimitry Andric
97a7dea167SDimitry Andric StringRef Text = getText(E, Context);
98a7dea167SDimitry Andric if (Text.empty())
99*bdd1243dSDimitry Andric return std::nullopt;
100a7dea167SDimitry Andric // Add leading '*'.
101a7dea167SDimitry Andric if (needParensAfterUnaryOperator(E))
102a7dea167SDimitry Andric return ("*(" + Text + ")").str();
103a7dea167SDimitry Andric return ("*" + Text).str();
104a7dea167SDimitry Andric }
105a7dea167SDimitry Andric
buildAddressOf(const Expr & E,const ASTContext & Context)106*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildAddressOf(const Expr &E,
107a7dea167SDimitry Andric const ASTContext &Context) {
108fe6060f1SDimitry Andric if (E.isImplicitCXXThis())
109fe6060f1SDimitry Andric return std::string("this");
110a7dea167SDimitry Andric if (const auto *Op = dyn_cast<UnaryOperator>(&E))
111a7dea167SDimitry Andric if (Op->getOpcode() == UO_Deref) {
112a7dea167SDimitry Andric // Strip leading '*'.
113a7dea167SDimitry Andric StringRef Text =
114a7dea167SDimitry Andric getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115a7dea167SDimitry Andric if (Text.empty())
116*bdd1243dSDimitry Andric return std::nullopt;
117a7dea167SDimitry Andric return Text.str();
118a7dea167SDimitry Andric }
119a7dea167SDimitry Andric // Add leading '&'.
120a7dea167SDimitry Andric StringRef Text = getText(E, Context);
121a7dea167SDimitry Andric if (Text.empty())
122*bdd1243dSDimitry Andric return std::nullopt;
123a7dea167SDimitry Andric if (needParensAfterUnaryOperator(E)) {
124a7dea167SDimitry Andric return ("&(" + Text + ")").str();
125a7dea167SDimitry Andric }
126a7dea167SDimitry Andric return ("&" + Text).str();
127a7dea167SDimitry Andric }
128a7dea167SDimitry Andric
12904eeddc0SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
13004eeddc0SDimitry Andric // is a non-pointer value.
131*bdd1243dSDimitry Andric static std::optional<std::string>
buildAccessForValue(const Expr & E,const ASTContext & Context)13204eeddc0SDimitry Andric buildAccessForValue(const Expr &E, const ASTContext &Context) {
133a7dea167SDimitry Andric if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
134a7dea167SDimitry Andric if (Op->getOpcode() == UO_Deref) {
135a7dea167SDimitry Andric // Strip leading '*', add following '->'.
136a7dea167SDimitry Andric const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137a7dea167SDimitry Andric StringRef DerefText = getText(*SubExpr, Context);
138a7dea167SDimitry Andric if (DerefText.empty())
139*bdd1243dSDimitry Andric return std::nullopt;
140a7dea167SDimitry Andric if (needParensBeforeDotOrArrow(*SubExpr))
141a7dea167SDimitry Andric return ("(" + DerefText + ")->").str();
142a7dea167SDimitry Andric return (DerefText + "->").str();
143a7dea167SDimitry Andric }
144a7dea167SDimitry Andric
145a7dea167SDimitry Andric // Add following '.'.
146a7dea167SDimitry Andric StringRef Text = getText(E, Context);
147a7dea167SDimitry Andric if (Text.empty())
148*bdd1243dSDimitry Andric return std::nullopt;
149a7dea167SDimitry Andric if (needParensBeforeDotOrArrow(E)) {
150a7dea167SDimitry Andric return ("(" + Text + ").").str();
151a7dea167SDimitry Andric }
152a7dea167SDimitry Andric return (Text + ".").str();
153a7dea167SDimitry Andric }
154a7dea167SDimitry Andric
15504eeddc0SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
15604eeddc0SDimitry Andric // is a pointer value.
157*bdd1243dSDimitry Andric static std::optional<std::string>
buildAccessForPointer(const Expr & E,const ASTContext & Context)15804eeddc0SDimitry Andric buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159a7dea167SDimitry Andric if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
160a7dea167SDimitry Andric if (Op->getOpcode() == UO_AddrOf) {
161a7dea167SDimitry Andric // Strip leading '&', add following '.'.
162a7dea167SDimitry Andric const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163a7dea167SDimitry Andric StringRef DerefText = getText(*SubExpr, Context);
164a7dea167SDimitry Andric if (DerefText.empty())
165*bdd1243dSDimitry Andric return std::nullopt;
166a7dea167SDimitry Andric if (needParensBeforeDotOrArrow(*SubExpr))
167a7dea167SDimitry Andric return ("(" + DerefText + ").").str();
168a7dea167SDimitry Andric return (DerefText + ".").str();
169a7dea167SDimitry Andric }
170a7dea167SDimitry Andric
171a7dea167SDimitry Andric // Add following '->'.
172a7dea167SDimitry Andric StringRef Text = getText(E, Context);
173a7dea167SDimitry Andric if (Text.empty())
174*bdd1243dSDimitry Andric return std::nullopt;
175a7dea167SDimitry Andric if (needParensBeforeDotOrArrow(E))
176a7dea167SDimitry Andric return ("(" + Text + ")->").str();
177a7dea167SDimitry Andric return (Text + "->").str();
178a7dea167SDimitry Andric }
17904eeddc0SDimitry Andric
buildDot(const Expr & E,const ASTContext & Context)180*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildDot(const Expr &E,
18104eeddc0SDimitry Andric const ASTContext &Context) {
18204eeddc0SDimitry Andric return buildAccessForValue(E, Context);
18304eeddc0SDimitry Andric }
18404eeddc0SDimitry Andric
buildArrow(const Expr & E,const ASTContext & Context)185*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildArrow(const Expr &E,
18604eeddc0SDimitry Andric const ASTContext &Context) {
18704eeddc0SDimitry Andric return buildAccessForPointer(E, Context);
18804eeddc0SDimitry Andric }
18904eeddc0SDimitry Andric
19004eeddc0SDimitry Andric // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
19104eeddc0SDimitry Andric // `O`. Otherwise, returns `nullptr`.
maybeGetOperatorObjectArg(const Expr & E,OverloadedOperatorKind K)19204eeddc0SDimitry Andric static const Expr *maybeGetOperatorObjectArg(const Expr &E,
19304eeddc0SDimitry Andric OverloadedOperatorKind K) {
19404eeddc0SDimitry Andric if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {
19504eeddc0SDimitry Andric if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)
19604eeddc0SDimitry Andric return OpCall->getArg(0);
19704eeddc0SDimitry Andric }
19804eeddc0SDimitry Andric return nullptr;
19904eeddc0SDimitry Andric }
20004eeddc0SDimitry Andric
treatLikePointer(QualType Ty,PLTClass C,ASTContext & Context)20104eeddc0SDimitry Andric static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
20204eeddc0SDimitry Andric switch (C) {
20304eeddc0SDimitry Andric case PLTClass::Value:
20404eeddc0SDimitry Andric return false;
20504eeddc0SDimitry Andric case PLTClass::Pointer:
20604eeddc0SDimitry Andric return isKnownPointerLikeType(Ty, Context);
20704eeddc0SDimitry Andric }
20804eeddc0SDimitry Andric llvm_unreachable("Unknown PLTClass enum");
20904eeddc0SDimitry Andric }
21004eeddc0SDimitry Andric
21104eeddc0SDimitry Andric // FIXME: move over the other `maybe` functionality from Stencil. Should all be
21204eeddc0SDimitry Andric // in one place.
buildAccess(const Expr & RawExpression,ASTContext & Context,PLTClass Classification)213*bdd1243dSDimitry Andric std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
21404eeddc0SDimitry Andric ASTContext &Context,
21504eeddc0SDimitry Andric PLTClass Classification) {
21604eeddc0SDimitry Andric if (RawExpression.isImplicitCXXThis())
217*bdd1243dSDimitry Andric // Return the empty string, because `std::nullopt` signifies some sort of
218*bdd1243dSDimitry Andric // failure.
21904eeddc0SDimitry Andric return std::string();
22004eeddc0SDimitry Andric
22104eeddc0SDimitry Andric const Expr *E = RawExpression.IgnoreImplicitAsWritten();
22204eeddc0SDimitry Andric
22304eeddc0SDimitry Andric if (E->getType()->isAnyPointerType() ||
22404eeddc0SDimitry Andric treatLikePointer(E->getType(), Classification, Context)) {
22504eeddc0SDimitry Andric // Strip off operator-> calls. They can only occur inside an actual arrow
22604eeddc0SDimitry Andric // member access, so we treat them as equivalent to an actual object
22704eeddc0SDimitry Andric // expression.
22804eeddc0SDimitry Andric if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
22904eeddc0SDimitry Andric E = Obj;
23004eeddc0SDimitry Andric return buildAccessForPointer(*E, Context);
23104eeddc0SDimitry Andric }
23204eeddc0SDimitry Andric
23304eeddc0SDimitry Andric if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
23404eeddc0SDimitry Andric if (treatLikePointer(Obj->getType(), Classification, Context))
23504eeddc0SDimitry Andric return buildAccessForPointer(*Obj, Context);
23604eeddc0SDimitry Andric };
23704eeddc0SDimitry Andric
23804eeddc0SDimitry Andric return buildAccessForValue(*E, Context);
23904eeddc0SDimitry Andric }
240