xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
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 (const auto *Op = dyn_cast<UnaryOperator>(&E))
97     if (Op->getOpcode() == UO_Deref) {
98       // Strip leading '*'.
99       StringRef Text =
100           getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
101       if (Text.empty())
102         return llvm::None;
103       return Text.str();
104     }
105   // Add leading '&'.
106   StringRef Text = getText(E, Context);
107   if (Text.empty())
108     return llvm::None;
109   if (needParensAfterUnaryOperator(E)) {
110     return ("&(" + Text + ")").str();
111   }
112   return ("&" + Text).str();
113 }
114 
115 llvm::Optional<std::string> tooling::buildDot(const Expr &E,
116                                               const ASTContext &Context) {
117   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
118     if (Op->getOpcode() == UO_Deref) {
119       // Strip leading '*', add following '->'.
120       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
121       StringRef DerefText = getText(*SubExpr, Context);
122       if (DerefText.empty())
123         return llvm::None;
124       if (needParensBeforeDotOrArrow(*SubExpr))
125         return ("(" + DerefText + ")->").str();
126       return (DerefText + "->").str();
127     }
128 
129   // Add following '.'.
130   StringRef Text = getText(E, Context);
131   if (Text.empty())
132     return llvm::None;
133   if (needParensBeforeDotOrArrow(E)) {
134     return ("(" + Text + ").").str();
135   }
136   return (Text + ".").str();
137 }
138 
139 llvm::Optional<std::string> tooling::buildArrow(const Expr &E,
140                                                 const ASTContext &Context) {
141   if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
142     if (Op->getOpcode() == UO_AddrOf) {
143       // Strip leading '&', add following '.'.
144       const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
145       StringRef DerefText = getText(*SubExpr, Context);
146       if (DerefText.empty())
147         return llvm::None;
148       if (needParensBeforeDotOrArrow(*SubExpr))
149         return ("(" + DerefText + ").").str();
150       return (DerefText + ".").str();
151     }
152 
153   // Add following '->'.
154   StringRef Text = getText(E, Context);
155   if (Text.empty())
156     return llvm::None;
157   if (needParensBeforeDotOrArrow(E))
158     return ("(" + Text + ")->").str();
159   return (Text + "->").str();
160 }
161