xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp (revision 963f5dc7a30624e95d72fb7f87b8892651164e46)
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 
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 
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 
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 
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>
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 
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 
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 
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