1*7330f729Sjoerg //===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
2*7330f729Sjoerg //
3*7330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*7330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7330f729Sjoerg //
7*7330f729Sjoerg //===----------------------------------------------------------------------===//
8*7330f729Sjoerg
9*7330f729Sjoerg #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
10*7330f729Sjoerg #include "clang/AST/ASTContext.h"
11*7330f729Sjoerg #include "clang/AST/Expr.h"
12*7330f729Sjoerg #include "clang/AST/ExprCXX.h"
13*7330f729Sjoerg #include "clang/Tooling/Transformer/SourceCode.h"
14*7330f729Sjoerg #include "llvm/ADT/Twine.h"
15*7330f729Sjoerg #include <string>
16*7330f729Sjoerg
17*7330f729Sjoerg using namespace clang;
18*7330f729Sjoerg using namespace tooling;
19*7330f729Sjoerg
reallyIgnoreImplicit(const Expr & E)20*7330f729Sjoerg const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
21*7330f729Sjoerg const Expr *Expr = E.IgnoreImplicit();
22*7330f729Sjoerg if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
23*7330f729Sjoerg if (CE->getNumArgs() > 0 &&
24*7330f729Sjoerg CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
25*7330f729Sjoerg return CE->getArg(0)->IgnoreImplicit();
26*7330f729Sjoerg }
27*7330f729Sjoerg return Expr;
28*7330f729Sjoerg }
29*7330f729Sjoerg
mayEverNeedParens(const Expr & E)30*7330f729Sjoerg bool tooling::mayEverNeedParens(const Expr &E) {
31*7330f729Sjoerg const Expr *Expr = reallyIgnoreImplicit(E);
32*7330f729Sjoerg // We always want parens around unary, binary, and ternary operators, because
33*7330f729Sjoerg // they are lower precedence.
34*7330f729Sjoerg if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
35*7330f729Sjoerg isa<AbstractConditionalOperator>(Expr))
36*7330f729Sjoerg return true;
37*7330f729Sjoerg
38*7330f729Sjoerg // We need parens around calls to all overloaded operators except: function
39*7330f729Sjoerg // calls, subscripts, and expressions that are already part of an (implicit)
40*7330f729Sjoerg // call to operator->. These latter are all in the same precedence level as
41*7330f729Sjoerg // dot/arrow and that level is left associative, so they don't need parens
42*7330f729Sjoerg // when appearing on the left.
43*7330f729Sjoerg if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
44*7330f729Sjoerg return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
45*7330f729Sjoerg Op->getOperator() != OO_Arrow;
46*7330f729Sjoerg
47*7330f729Sjoerg return false;
48*7330f729Sjoerg }
49*7330f729Sjoerg
needParensAfterUnaryOperator(const Expr & E)50*7330f729Sjoerg bool tooling::needParensAfterUnaryOperator(const Expr &E) {
51*7330f729Sjoerg const Expr *Expr = reallyIgnoreImplicit(E);
52*7330f729Sjoerg if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
53*7330f729Sjoerg return true;
54*7330f729Sjoerg
55*7330f729Sjoerg if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
56*7330f729Sjoerg return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
57*7330f729Sjoerg Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
58*7330f729Sjoerg Op->getOperator() != OO_Subscript;
59*7330f729Sjoerg
60*7330f729Sjoerg return false;
61*7330f729Sjoerg }
62*7330f729Sjoerg
buildParens(const Expr & E,const ASTContext & Context)63*7330f729Sjoerg llvm::Optional<std::string> tooling::buildParens(const Expr &E,
64*7330f729Sjoerg const ASTContext &Context) {
65*7330f729Sjoerg StringRef Text = getText(E, Context);
66*7330f729Sjoerg if (Text.empty())
67*7330f729Sjoerg return llvm::None;
68*7330f729Sjoerg if (mayEverNeedParens(E))
69*7330f729Sjoerg return ("(" + Text + ")").str();
70*7330f729Sjoerg return Text.str();
71*7330f729Sjoerg }
72*7330f729Sjoerg
73*7330f729Sjoerg llvm::Optional<std::string>
buildDereference(const Expr & E,const ASTContext & Context)74*7330f729Sjoerg tooling::buildDereference(const Expr &E, const ASTContext &Context) {
75*7330f729Sjoerg if (const auto *Op = dyn_cast<UnaryOperator>(&E))
76*7330f729Sjoerg if (Op->getOpcode() == UO_AddrOf) {
77*7330f729Sjoerg // Strip leading '&'.
78*7330f729Sjoerg StringRef Text =
79*7330f729Sjoerg getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
80*7330f729Sjoerg if (Text.empty())
81*7330f729Sjoerg return llvm::None;
82*7330f729Sjoerg return Text.str();
83*7330f729Sjoerg }
84*7330f729Sjoerg
85*7330f729Sjoerg StringRef Text = getText(E, Context);
86*7330f729Sjoerg if (Text.empty())
87*7330f729Sjoerg return llvm::None;
88*7330f729Sjoerg // Add leading '*'.
89*7330f729Sjoerg if (needParensAfterUnaryOperator(E))
90*7330f729Sjoerg return ("*(" + Text + ")").str();
91*7330f729Sjoerg return ("*" + Text).str();
92*7330f729Sjoerg }
93*7330f729Sjoerg
buildAddressOf(const Expr & E,const ASTContext & Context)94*7330f729Sjoerg llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E,
95*7330f729Sjoerg const ASTContext &Context) {
96*7330f729Sjoerg if (const auto *Op = dyn_cast<UnaryOperator>(&E))
97*7330f729Sjoerg if (Op->getOpcode() == UO_Deref) {
98*7330f729Sjoerg // Strip leading '*'.
99*7330f729Sjoerg StringRef Text =
100*7330f729Sjoerg getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
101*7330f729Sjoerg if (Text.empty())
102*7330f729Sjoerg return llvm::None;
103*7330f729Sjoerg return Text.str();
104*7330f729Sjoerg }
105*7330f729Sjoerg // Add leading '&'.
106*7330f729Sjoerg StringRef Text = getText(E, Context);
107*7330f729Sjoerg if (Text.empty())
108*7330f729Sjoerg return llvm::None;
109*7330f729Sjoerg if (needParensAfterUnaryOperator(E)) {
110*7330f729Sjoerg return ("&(" + Text + ")").str();
111*7330f729Sjoerg }
112*7330f729Sjoerg return ("&" + Text).str();
113*7330f729Sjoerg }
114*7330f729Sjoerg
buildDot(const Expr & E,const ASTContext & Context)115*7330f729Sjoerg llvm::Optional<std::string> tooling::buildDot(const Expr &E,
116*7330f729Sjoerg const ASTContext &Context) {
117*7330f729Sjoerg if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
118*7330f729Sjoerg if (Op->getOpcode() == UO_Deref) {
119*7330f729Sjoerg // Strip leading '*', add following '->'.
120*7330f729Sjoerg const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
121*7330f729Sjoerg StringRef DerefText = getText(*SubExpr, Context);
122*7330f729Sjoerg if (DerefText.empty())
123*7330f729Sjoerg return llvm::None;
124*7330f729Sjoerg if (needParensBeforeDotOrArrow(*SubExpr))
125*7330f729Sjoerg return ("(" + DerefText + ")->").str();
126*7330f729Sjoerg return (DerefText + "->").str();
127*7330f729Sjoerg }
128*7330f729Sjoerg
129*7330f729Sjoerg // Add following '.'.
130*7330f729Sjoerg StringRef Text = getText(E, Context);
131*7330f729Sjoerg if (Text.empty())
132*7330f729Sjoerg return llvm::None;
133*7330f729Sjoerg if (needParensBeforeDotOrArrow(E)) {
134*7330f729Sjoerg return ("(" + Text + ").").str();
135*7330f729Sjoerg }
136*7330f729Sjoerg return (Text + ".").str();
137*7330f729Sjoerg }
138*7330f729Sjoerg
buildArrow(const Expr & E,const ASTContext & Context)139*7330f729Sjoerg llvm::Optional<std::string> tooling::buildArrow(const Expr &E,
140*7330f729Sjoerg const ASTContext &Context) {
141*7330f729Sjoerg if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
142*7330f729Sjoerg if (Op->getOpcode() == UO_AddrOf) {
143*7330f729Sjoerg // Strip leading '&', add following '.'.
144*7330f729Sjoerg const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
145*7330f729Sjoerg StringRef DerefText = getText(*SubExpr, Context);
146*7330f729Sjoerg if (DerefText.empty())
147*7330f729Sjoerg return llvm::None;
148*7330f729Sjoerg if (needParensBeforeDotOrArrow(*SubExpr))
149*7330f729Sjoerg return ("(" + DerefText + ").").str();
150*7330f729Sjoerg return (DerefText + ".").str();
151*7330f729Sjoerg }
152*7330f729Sjoerg
153*7330f729Sjoerg // Add following '->'.
154*7330f729Sjoerg StringRef Text = getText(E, Context);
155*7330f729Sjoerg if (Text.empty())
156*7330f729Sjoerg return llvm::None;
157*7330f729Sjoerg if (needParensBeforeDotOrArrow(E))
158*7330f729Sjoerg return ("(" + Text + ")->").str();
159*7330f729Sjoerg return (Text + "->").str();
160*7330f729Sjoerg }
161