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
17using namespace clang;
18using namespace tooling;
19
20const 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
30bool 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
50bool 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
63llvm::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
73llvm::Optional<std::string>
74tooling::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
94llvm::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
115llvm::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
139llvm::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