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