1519fc96cSDimitry Andric //===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
2519fc96cSDimitry Andric //
3519fc96cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4519fc96cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5519fc96cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6519fc96cSDimitry Andric //
7519fc96cSDimitry Andric //===----------------------------------------------------------------------===//
8519fc96cSDimitry Andric
9519fc96cSDimitry Andric #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10519fc96cSDimitry Andric #include "clang/AST/ASTContext.h"
11519fc96cSDimitry Andric #include "clang/AST/Expr.h"
12519fc96cSDimitry Andric #include "clang/AST/ExprCXX.h"
136f8fc217SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
146f8fc217SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
15519fc96cSDimitry Andric #include "clang/Tooling/Transformer/SourceCode.h"
16519fc96cSDimitry Andric #include "llvm/ADT/Twine.h"
17519fc96cSDimitry Andric #include <string>
18519fc96cSDimitry Andric
19519fc96cSDimitry Andric using namespace clang;
20519fc96cSDimitry Andric using namespace tooling;
21519fc96cSDimitry Andric
reallyIgnoreImplicit(const Expr & E)22519fc96cSDimitry Andric const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23519fc96cSDimitry Andric const Expr *Expr = E.IgnoreImplicit();
24519fc96cSDimitry Andric if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
25519fc96cSDimitry Andric if (CE->getNumArgs() > 0 &&
26519fc96cSDimitry Andric CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
27519fc96cSDimitry Andric return CE->getArg(0)->IgnoreImplicit();
28519fc96cSDimitry Andric }
29519fc96cSDimitry Andric return Expr;
30519fc96cSDimitry Andric }
31519fc96cSDimitry Andric
mayEverNeedParens(const Expr & E)32519fc96cSDimitry Andric bool tooling::mayEverNeedParens(const Expr &E) {
33519fc96cSDimitry Andric const Expr *Expr = reallyIgnoreImplicit(E);
34519fc96cSDimitry Andric // We always want parens around unary, binary, and ternary operators, because
35519fc96cSDimitry Andric // they are lower precedence.
36519fc96cSDimitry Andric if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
37519fc96cSDimitry Andric isa<AbstractConditionalOperator>(Expr))
38519fc96cSDimitry Andric return true;
39519fc96cSDimitry Andric
40519fc96cSDimitry Andric // We need parens around calls to all overloaded operators except: function
41519fc96cSDimitry Andric // calls, subscripts, and expressions that are already part of an (implicit)
42519fc96cSDimitry Andric // call to operator->. These latter are all in the same precedence level as
43519fc96cSDimitry Andric // dot/arrow and that level is left associative, so they don't need parens
44519fc96cSDimitry Andric // when appearing on the left.
45519fc96cSDimitry Andric if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
46519fc96cSDimitry Andric return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47519fc96cSDimitry Andric Op->getOperator() != OO_Arrow;
48519fc96cSDimitry Andric
49519fc96cSDimitry Andric return false;
50519fc96cSDimitry Andric }
51519fc96cSDimitry Andric
needParensAfterUnaryOperator(const Expr & E)52519fc96cSDimitry Andric bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53519fc96cSDimitry Andric const Expr *Expr = reallyIgnoreImplicit(E);
54519fc96cSDimitry Andric if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
55519fc96cSDimitry Andric return true;
56519fc96cSDimitry Andric
57519fc96cSDimitry Andric if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
58519fc96cSDimitry Andric return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
59519fc96cSDimitry Andric Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
60519fc96cSDimitry Andric Op->getOperator() != OO_Subscript;
61519fc96cSDimitry Andric
62519fc96cSDimitry Andric return false;
63519fc96cSDimitry Andric }
64519fc96cSDimitry Andric
isKnownPointerLikeType(QualType Ty,ASTContext & Context)656f8fc217SDimitry Andric bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
666f8fc217SDimitry Andric using namespace ast_matchers;
676f8fc217SDimitry Andric const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
686f8fc217SDimitry Andric recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
696f8fc217SDimitry Andric "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
706f8fc217SDimitry Andric "::std::optional", "::absl::optional", "::llvm::Optional",
716f8fc217SDimitry Andric "absl::StatusOr", "::llvm::Expected"))))));
726f8fc217SDimitry Andric return match(PointerLikeTy, Ty, Context).size() > 0;
736f8fc217SDimitry Andric }
746f8fc217SDimitry Andric
buildParens(const Expr & E,const ASTContext & Context)75e3b55780SDimitry Andric std::optional<std::string> tooling::buildParens(const Expr &E,
76519fc96cSDimitry Andric const ASTContext &Context) {
77519fc96cSDimitry Andric StringRef Text = getText(E, Context);
78519fc96cSDimitry Andric if (Text.empty())
79e3b55780SDimitry Andric return std::nullopt;
80519fc96cSDimitry Andric if (mayEverNeedParens(E))
81519fc96cSDimitry Andric return ("(" + Text + ")").str();
82519fc96cSDimitry Andric return Text.str();
83519fc96cSDimitry Andric }
84519fc96cSDimitry Andric
85e3b55780SDimitry Andric std::optional<std::string>
buildDereference(const Expr & E,const ASTContext & Context)86519fc96cSDimitry Andric tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87519fc96cSDimitry Andric if (const auto *Op = dyn_cast<UnaryOperator>(&E))
88519fc96cSDimitry Andric if (Op->getOpcode() == UO_AddrOf) {
89519fc96cSDimitry Andric // Strip leading '&'.
90519fc96cSDimitry Andric StringRef Text =
91519fc96cSDimitry Andric getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92519fc96cSDimitry Andric if (Text.empty())
93e3b55780SDimitry Andric return std::nullopt;
94519fc96cSDimitry Andric return Text.str();
95519fc96cSDimitry Andric }
96519fc96cSDimitry Andric
97519fc96cSDimitry Andric StringRef Text = getText(E, Context);
98519fc96cSDimitry Andric if (Text.empty())
99e3b55780SDimitry Andric return std::nullopt;
100519fc96cSDimitry Andric // Add leading '*'.
101519fc96cSDimitry Andric if (needParensAfterUnaryOperator(E))
102519fc96cSDimitry Andric return ("*(" + Text + ")").str();
103519fc96cSDimitry Andric return ("*" + Text).str();
104519fc96cSDimitry Andric }
105519fc96cSDimitry Andric
buildAddressOf(const Expr & E,const ASTContext & Context)106e3b55780SDimitry Andric std::optional<std::string> tooling::buildAddressOf(const Expr &E,
107519fc96cSDimitry Andric const ASTContext &Context) {
108344a3780SDimitry Andric if (E.isImplicitCXXThis())
109344a3780SDimitry Andric return std::string("this");
110519fc96cSDimitry Andric if (const auto *Op = dyn_cast<UnaryOperator>(&E))
111519fc96cSDimitry Andric if (Op->getOpcode() == UO_Deref) {
112519fc96cSDimitry Andric // Strip leading '*'.
113519fc96cSDimitry Andric StringRef Text =
114519fc96cSDimitry Andric getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115519fc96cSDimitry Andric if (Text.empty())
116e3b55780SDimitry Andric return std::nullopt;
117519fc96cSDimitry Andric return Text.str();
118519fc96cSDimitry Andric }
119519fc96cSDimitry Andric // Add leading '&'.
120519fc96cSDimitry Andric StringRef Text = getText(E, Context);
121519fc96cSDimitry Andric if (Text.empty())
122e3b55780SDimitry Andric return std::nullopt;
123519fc96cSDimitry Andric if (needParensAfterUnaryOperator(E)) {
124519fc96cSDimitry Andric return ("&(" + Text + ")").str();
125519fc96cSDimitry Andric }
126519fc96cSDimitry Andric return ("&" + Text).str();
127519fc96cSDimitry Andric }
128519fc96cSDimitry Andric
1296f8fc217SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
1306f8fc217SDimitry Andric // is a non-pointer value.
131e3b55780SDimitry Andric static std::optional<std::string>
buildAccessForValue(const Expr & E,const ASTContext & Context)1326f8fc217SDimitry Andric buildAccessForValue(const Expr &E, const ASTContext &Context) {
133519fc96cSDimitry Andric if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
134519fc96cSDimitry Andric if (Op->getOpcode() == UO_Deref) {
135519fc96cSDimitry Andric // Strip leading '*', add following '->'.
136519fc96cSDimitry Andric const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137519fc96cSDimitry Andric StringRef DerefText = getText(*SubExpr, Context);
138519fc96cSDimitry Andric if (DerefText.empty())
139e3b55780SDimitry Andric return std::nullopt;
140519fc96cSDimitry Andric if (needParensBeforeDotOrArrow(*SubExpr))
141519fc96cSDimitry Andric return ("(" + DerefText + ")->").str();
142519fc96cSDimitry Andric return (DerefText + "->").str();
143519fc96cSDimitry Andric }
144519fc96cSDimitry Andric
145519fc96cSDimitry Andric // Add following '.'.
146519fc96cSDimitry Andric StringRef Text = getText(E, Context);
147519fc96cSDimitry Andric if (Text.empty())
148e3b55780SDimitry Andric return std::nullopt;
149519fc96cSDimitry Andric if (needParensBeforeDotOrArrow(E)) {
150519fc96cSDimitry Andric return ("(" + Text + ").").str();
151519fc96cSDimitry Andric }
152519fc96cSDimitry Andric return (Text + ".").str();
153519fc96cSDimitry Andric }
154519fc96cSDimitry Andric
1556f8fc217SDimitry Andric // Append the appropriate access operation (syntactically) to `E`, assuming `E`
1566f8fc217SDimitry Andric // is a pointer value.
157e3b55780SDimitry Andric static std::optional<std::string>
buildAccessForPointer(const Expr & E,const ASTContext & Context)1586f8fc217SDimitry Andric buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159519fc96cSDimitry Andric if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
160519fc96cSDimitry Andric if (Op->getOpcode() == UO_AddrOf) {
161519fc96cSDimitry Andric // Strip leading '&', add following '.'.
162519fc96cSDimitry Andric const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163519fc96cSDimitry Andric StringRef DerefText = getText(*SubExpr, Context);
164519fc96cSDimitry Andric if (DerefText.empty())
165e3b55780SDimitry Andric return std::nullopt;
166519fc96cSDimitry Andric if (needParensBeforeDotOrArrow(*SubExpr))
167519fc96cSDimitry Andric return ("(" + DerefText + ").").str();
168519fc96cSDimitry Andric return (DerefText + ".").str();
169519fc96cSDimitry Andric }
170519fc96cSDimitry Andric
171519fc96cSDimitry Andric // Add following '->'.
172519fc96cSDimitry Andric StringRef Text = getText(E, Context);
173519fc96cSDimitry Andric if (Text.empty())
174e3b55780SDimitry Andric return std::nullopt;
175519fc96cSDimitry Andric if (needParensBeforeDotOrArrow(E))
176519fc96cSDimitry Andric return ("(" + Text + ")->").str();
177519fc96cSDimitry Andric return (Text + "->").str();
178519fc96cSDimitry Andric }
1796f8fc217SDimitry Andric
buildDot(const Expr & E,const ASTContext & Context)180e3b55780SDimitry Andric std::optional<std::string> tooling::buildDot(const Expr &E,
1816f8fc217SDimitry Andric const ASTContext &Context) {
1826f8fc217SDimitry Andric return buildAccessForValue(E, Context);
1836f8fc217SDimitry Andric }
1846f8fc217SDimitry Andric
buildArrow(const Expr & E,const ASTContext & Context)185e3b55780SDimitry Andric std::optional<std::string> tooling::buildArrow(const Expr &E,
1866f8fc217SDimitry Andric const ASTContext &Context) {
1876f8fc217SDimitry Andric return buildAccessForPointer(E, Context);
1886f8fc217SDimitry Andric }
1896f8fc217SDimitry Andric
1906f8fc217SDimitry Andric // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
1916f8fc217SDimitry Andric // `O`. Otherwise, returns `nullptr`.
maybeGetOperatorObjectArg(const Expr & E,OverloadedOperatorKind K)1926f8fc217SDimitry Andric static const Expr *maybeGetOperatorObjectArg(const Expr &E,
1936f8fc217SDimitry Andric OverloadedOperatorKind K) {
1946f8fc217SDimitry Andric if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) {
1956f8fc217SDimitry Andric if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)
1966f8fc217SDimitry Andric return OpCall->getArg(0);
1976f8fc217SDimitry Andric }
1986f8fc217SDimitry Andric return nullptr;
1996f8fc217SDimitry Andric }
2006f8fc217SDimitry Andric
treatLikePointer(QualType Ty,PLTClass C,ASTContext & Context)2016f8fc217SDimitry Andric static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
2026f8fc217SDimitry Andric switch (C) {
2036f8fc217SDimitry Andric case PLTClass::Value:
2046f8fc217SDimitry Andric return false;
2056f8fc217SDimitry Andric case PLTClass::Pointer:
2066f8fc217SDimitry Andric return isKnownPointerLikeType(Ty, Context);
2076f8fc217SDimitry Andric }
2086f8fc217SDimitry Andric llvm_unreachable("Unknown PLTClass enum");
2096f8fc217SDimitry Andric }
2106f8fc217SDimitry Andric
2116f8fc217SDimitry Andric // FIXME: move over the other `maybe` functionality from Stencil. Should all be
2126f8fc217SDimitry Andric // in one place.
buildAccess(const Expr & RawExpression,ASTContext & Context,PLTClass Classification)213e3b55780SDimitry Andric std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
2146f8fc217SDimitry Andric ASTContext &Context,
2156f8fc217SDimitry Andric PLTClass Classification) {
2166f8fc217SDimitry Andric if (RawExpression.isImplicitCXXThis())
217e3b55780SDimitry Andric // Return the empty string, because `std::nullopt` signifies some sort of
218e3b55780SDimitry Andric // failure.
2196f8fc217SDimitry Andric return std::string();
2206f8fc217SDimitry Andric
2216f8fc217SDimitry Andric const Expr *E = RawExpression.IgnoreImplicitAsWritten();
2226f8fc217SDimitry Andric
2236f8fc217SDimitry Andric if (E->getType()->isAnyPointerType() ||
2246f8fc217SDimitry Andric treatLikePointer(E->getType(), Classification, Context)) {
2256f8fc217SDimitry Andric // Strip off operator-> calls. They can only occur inside an actual arrow
2266f8fc217SDimitry Andric // member access, so we treat them as equivalent to an actual object
2276f8fc217SDimitry Andric // expression.
2286f8fc217SDimitry Andric if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
2296f8fc217SDimitry Andric E = Obj;
2306f8fc217SDimitry Andric return buildAccessForPointer(*E, Context);
2316f8fc217SDimitry Andric }
2326f8fc217SDimitry Andric
2336f8fc217SDimitry Andric if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
2346f8fc217SDimitry Andric if (treatLikePointer(Obj->getType(), Classification, Context))
2356f8fc217SDimitry Andric return buildAccessForPointer(*Obj, Context);
2366f8fc217SDimitry Andric };
2376f8fc217SDimitry Andric
2386f8fc217SDimitry Andric return buildAccessForValue(*E, Context);
2396f8fc217SDimitry Andric }
240