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