File: SwapBinaryOperands.cpp

package info (click to toggle)
llvm-toolchain-20 1%3A20.1.8-1~exp1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,111,388 kB
  • sloc: cpp: 7,438,767; ansic: 1,393,871; asm: 1,012,926; python: 241,728; f90: 86,635; objc: 75,411; lisp: 42,144; pascal: 17,286; sh: 10,027; ml: 5,082; perl: 4,730; awk: 3,523; makefile: 3,349; javascript: 2,251; xml: 892; fortran: 672
file content (217 lines) | stat: -rw-r--r-- 7,749 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//===--- SwapBinaryOperands.cpp ----------------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ParsedAST.h"
#include "Protocol.h"
#include "Selection.h"
#include "SourceCode.h"
#include "refactor/Tweak.h"
#include "support/Logger.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/FormatVariadic.h"
#include <string>
#include <utility>

namespace clang {
namespace clangd {
namespace {
/// Check whether it makes logical sense to swap operands to an operator.
/// Assignment or member access operators are rarely swappable
/// while keeping the meaning intact, whereas comparison operators, mathematical
/// operators, etc. are often desired to be swappable for readability, avoiding
/// bugs by assigning to nullptr when comparison was desired, etc.
bool isOpSwappable(const BinaryOperatorKind Opcode) {
  switch (Opcode) {
  case BinaryOperatorKind::BO_Mul:
  case BinaryOperatorKind::BO_Add:
  case BinaryOperatorKind::BO_LT:
  case BinaryOperatorKind::BO_GT:
  case BinaryOperatorKind::BO_LE:
  case BinaryOperatorKind::BO_GE:
  case BinaryOperatorKind::BO_EQ:
  case BinaryOperatorKind::BO_NE:
  case BinaryOperatorKind::BO_And:
  case BinaryOperatorKind::BO_Xor:
  case BinaryOperatorKind::BO_Or:
  case BinaryOperatorKind::BO_LAnd:
  case BinaryOperatorKind::BO_LOr:
  case BinaryOperatorKind::BO_Comma:
    return true;
  // Noncommutative operators:
  case BinaryOperatorKind::BO_Div:
  case BinaryOperatorKind::BO_Sub:
  case BinaryOperatorKind::BO_Shl:
  case BinaryOperatorKind::BO_Shr:
  case BinaryOperatorKind::BO_Rem:
  // <=> is noncommutative
  case BinaryOperatorKind::BO_Cmp:
  // Member access:
  case BinaryOperatorKind::BO_PtrMemD:
  case BinaryOperatorKind::BO_PtrMemI:
  // Assignment:
  case BinaryOperatorKind::BO_Assign:
  case BinaryOperatorKind::BO_MulAssign:
  case BinaryOperatorKind::BO_DivAssign:
  case BinaryOperatorKind::BO_RemAssign:
  case BinaryOperatorKind::BO_AddAssign:
  case BinaryOperatorKind::BO_SubAssign:
  case BinaryOperatorKind::BO_ShlAssign:
  case BinaryOperatorKind::BO_ShrAssign:
  case BinaryOperatorKind::BO_AndAssign:
  case BinaryOperatorKind::BO_XorAssign:
  case BinaryOperatorKind::BO_OrAssign:
    return false;
  }
  return false;
}

/// Some operators are asymmetric and need to be flipped when swapping their
/// operands
/// @param[out] Opcode the opcode to potentially swap
/// If the opcode does not need to be swapped or is not swappable, does nothing
BinaryOperatorKind swapOperator(const BinaryOperatorKind Opcode) {
  switch (Opcode) {
  case BinaryOperatorKind::BO_LT:
    return BinaryOperatorKind::BO_GT;

  case BinaryOperatorKind::BO_GT:
    return BinaryOperatorKind::BO_LT;

  case BinaryOperatorKind::BO_LE:
    return BinaryOperatorKind::BO_GE;

  case BinaryOperatorKind::BO_GE:
    return BinaryOperatorKind::BO_LE;

  case BinaryOperatorKind::BO_Mul:
  case BinaryOperatorKind::BO_Add:
  case BinaryOperatorKind::BO_Cmp:
  case BinaryOperatorKind::BO_EQ:
  case BinaryOperatorKind::BO_NE:
  case BinaryOperatorKind::BO_And:
  case BinaryOperatorKind::BO_Xor:
  case BinaryOperatorKind::BO_Or:
  case BinaryOperatorKind::BO_LAnd:
  case BinaryOperatorKind::BO_LOr:
  case BinaryOperatorKind::BO_Comma:
  case BinaryOperatorKind::BO_Div:
  case BinaryOperatorKind::BO_Sub:
  case BinaryOperatorKind::BO_Shl:
  case BinaryOperatorKind::BO_Shr:
  case BinaryOperatorKind::BO_Rem:
  case BinaryOperatorKind::BO_PtrMemD:
  case BinaryOperatorKind::BO_PtrMemI:
  case BinaryOperatorKind::BO_Assign:
  case BinaryOperatorKind::BO_MulAssign:
  case BinaryOperatorKind::BO_DivAssign:
  case BinaryOperatorKind::BO_RemAssign:
  case BinaryOperatorKind::BO_AddAssign:
  case BinaryOperatorKind::BO_SubAssign:
  case BinaryOperatorKind::BO_ShlAssign:
  case BinaryOperatorKind::BO_ShrAssign:
  case BinaryOperatorKind::BO_AndAssign:
  case BinaryOperatorKind::BO_XorAssign:
  case BinaryOperatorKind::BO_OrAssign:
    return Opcode;
  }
  llvm_unreachable("Unknown BinaryOperatorKind enum");
}

/// Swaps the operands to a binary operator
/// Before:
///   x != nullptr
///   ^    ^^^^^^^
/// After:
///   nullptr != x
class SwapBinaryOperands : public Tweak {
public:
  const char *id() const final;

  bool prepare(const Selection &Inputs) override;
  Expected<Effect> apply(const Selection &Inputs) override;
  std::string title() const override {
    return llvm::formatv("Swap operands to {0}",
                         Op ? Op->getOpcodeStr() : "binary operator");
  }
  llvm::StringLiteral kind() const override {
    return CodeAction::REFACTOR_KIND;
  }
  bool hidden() const override { return false; }

private:
  const BinaryOperator *Op;
};

REGISTER_TWEAK(SwapBinaryOperands)

bool SwapBinaryOperands::prepare(const Selection &Inputs) {
  for (const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
       N && !Op; N = N->Parent) {
    // Stop once we hit a block, e.g. a lambda in one of the operands.
    // This makes sure that the selection point is in the 'scope' of the binary
    // operator, not from somewhere inside a lambda for example
    // (5 < [](){ ^return 1; })
    if (llvm::isa_and_nonnull<CompoundStmt>(N->ASTNode.get<Stmt>()))
      return false;
    Op = dyn_cast_or_null<BinaryOperator>(N->ASTNode.get<Stmt>());
    // If we hit upon a nonswappable binary operator, ignore and keep going
    if (Op && !isOpSwappable(Op->getOpcode())) {
      Op = nullptr;
    }
  }
  return Op != nullptr;
}

Expected<Tweak::Effect> SwapBinaryOperands::apply(const Selection &Inputs) {
  const auto &Ctx = Inputs.AST->getASTContext();
  const auto &SrcMgr = Inputs.AST->getSourceManager();

  const auto LHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(),
                                          Op->getLHS()->getSourceRange());
  if (!LHSRng)
    return error(
        "Could not obtain range of the 'lhs' of the operator. Macros?");
  const auto RHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(),
                                          Op->getRHS()->getSourceRange());
  if (!RHSRng)
    return error(
        "Could not obtain range of the 'rhs' of the operator. Macros?");
  const auto OpRng =
      toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), Op->getOperatorLoc());
  if (!OpRng)
    return error("Could not obtain range of the operator itself. Macros?");

  const auto LHSCode = toSourceCode(SrcMgr, *LHSRng);
  const auto RHSCode = toSourceCode(SrcMgr, *RHSRng);
  const auto OperatorCode = toSourceCode(SrcMgr, *OpRng);

  tooling::Replacements Result;
  if (auto Err = Result.add(tooling::Replacement(
          Ctx.getSourceManager(), LHSRng->getBegin(), LHSCode.size(), RHSCode)))
    return std::move(Err);
  if (auto Err = Result.add(tooling::Replacement(
          Ctx.getSourceManager(), RHSRng->getBegin(), RHSCode.size(), LHSCode)))
    return std::move(Err);
  const auto SwappedOperator = swapOperator(Op->getOpcode());
  if (auto Err = Result.add(tooling::Replacement(
          Ctx.getSourceManager(), OpRng->getBegin(), OperatorCode.size(),
          Op->getOpcodeStr(SwappedOperator))))
    return std::move(Err);
  return Effect::mainFileEdit(SrcMgr, std::move(Result));
}

} // namespace
} // namespace clangd
} // namespace clang