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