[LoongArch][Clang] Make the parameters and return value of {x,}vorn.v builti ns ...
[llvm-project.git] / clang-tools-extra / clangd / refactor / tweaks / SwapBinaryOperands.cpp
blob9ad0089a5d0359661c17c698b2eac1c81b048d3c
1 //===--- SwapBinaryOperands.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 #include "ParsedAST.h"
9 #include "Protocol.h"
10 #include "Selection.h"
11 #include "SourceCode.h"
12 #include "refactor/Tweak.h"
13 #include "support/Logger.h"
14 #include "clang/AST/ASTContext.h"
15 #include "clang/AST/Expr.h"
16 #include "clang/AST/OperationKinds.h"
17 #include "clang/AST/Stmt.h"
18 #include "clang/Basic/LLVM.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Tooling/Core/Replacement.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/Casting.h"
23 #include "llvm/Support/FormatVariadic.h"
24 #include <string>
25 #include <utility>
27 namespace clang {
28 namespace clangd {
29 namespace {
30 /// Check whether it makes logical sense to swap operands to an operator.
31 /// Assignment or member access operators are rarely swappable
32 /// while keeping the meaning intact, whereas comparison operators, mathematical
33 /// operators, etc. are often desired to be swappable for readability, avoiding
34 /// bugs by assigning to nullptr when comparison was desired, etc.
35 bool isOpSwappable(const BinaryOperatorKind Opcode) {
36 switch (Opcode) {
37 case BinaryOperatorKind::BO_Mul:
38 case BinaryOperatorKind::BO_Add:
39 case BinaryOperatorKind::BO_LT:
40 case BinaryOperatorKind::BO_GT:
41 case BinaryOperatorKind::BO_LE:
42 case BinaryOperatorKind::BO_GE:
43 case BinaryOperatorKind::BO_EQ:
44 case BinaryOperatorKind::BO_NE:
45 case BinaryOperatorKind::BO_And:
46 case BinaryOperatorKind::BO_Xor:
47 case BinaryOperatorKind::BO_Or:
48 case BinaryOperatorKind::BO_LAnd:
49 case BinaryOperatorKind::BO_LOr:
50 case BinaryOperatorKind::BO_Comma:
51 return true;
52 // Noncommutative operators:
53 case BinaryOperatorKind::BO_Div:
54 case BinaryOperatorKind::BO_Sub:
55 case BinaryOperatorKind::BO_Shl:
56 case BinaryOperatorKind::BO_Shr:
57 case BinaryOperatorKind::BO_Rem:
58 // <=> is noncommutative
59 case BinaryOperatorKind::BO_Cmp:
60 // Member access:
61 case BinaryOperatorKind::BO_PtrMemD:
62 case BinaryOperatorKind::BO_PtrMemI:
63 // Assignment:
64 case BinaryOperatorKind::BO_Assign:
65 case BinaryOperatorKind::BO_MulAssign:
66 case BinaryOperatorKind::BO_DivAssign:
67 case BinaryOperatorKind::BO_RemAssign:
68 case BinaryOperatorKind::BO_AddAssign:
69 case BinaryOperatorKind::BO_SubAssign:
70 case BinaryOperatorKind::BO_ShlAssign:
71 case BinaryOperatorKind::BO_ShrAssign:
72 case BinaryOperatorKind::BO_AndAssign:
73 case BinaryOperatorKind::BO_XorAssign:
74 case BinaryOperatorKind::BO_OrAssign:
75 return false;
77 return false;
80 /// Some operators are asymmetric and need to be flipped when swapping their
81 /// operands
82 /// @param[out] Opcode the opcode to potentially swap
83 /// If the opcode does not need to be swapped or is not swappable, does nothing
84 BinaryOperatorKind swapOperator(const BinaryOperatorKind Opcode) {
85 switch (Opcode) {
86 case BinaryOperatorKind::BO_LT:
87 return BinaryOperatorKind::BO_GT;
89 case BinaryOperatorKind::BO_GT:
90 return BinaryOperatorKind::BO_LT;
92 case BinaryOperatorKind::BO_LE:
93 return BinaryOperatorKind::BO_GE;
95 case BinaryOperatorKind::BO_GE:
96 return BinaryOperatorKind::BO_LE;
98 case BinaryOperatorKind::BO_Mul:
99 case BinaryOperatorKind::BO_Add:
100 case BinaryOperatorKind::BO_Cmp:
101 case BinaryOperatorKind::BO_EQ:
102 case BinaryOperatorKind::BO_NE:
103 case BinaryOperatorKind::BO_And:
104 case BinaryOperatorKind::BO_Xor:
105 case BinaryOperatorKind::BO_Or:
106 case BinaryOperatorKind::BO_LAnd:
107 case BinaryOperatorKind::BO_LOr:
108 case BinaryOperatorKind::BO_Comma:
109 case BinaryOperatorKind::BO_Div:
110 case BinaryOperatorKind::BO_Sub:
111 case BinaryOperatorKind::BO_Shl:
112 case BinaryOperatorKind::BO_Shr:
113 case BinaryOperatorKind::BO_Rem:
114 case BinaryOperatorKind::BO_PtrMemD:
115 case BinaryOperatorKind::BO_PtrMemI:
116 case BinaryOperatorKind::BO_Assign:
117 case BinaryOperatorKind::BO_MulAssign:
118 case BinaryOperatorKind::BO_DivAssign:
119 case BinaryOperatorKind::BO_RemAssign:
120 case BinaryOperatorKind::BO_AddAssign:
121 case BinaryOperatorKind::BO_SubAssign:
122 case BinaryOperatorKind::BO_ShlAssign:
123 case BinaryOperatorKind::BO_ShrAssign:
124 case BinaryOperatorKind::BO_AndAssign:
125 case BinaryOperatorKind::BO_XorAssign:
126 case BinaryOperatorKind::BO_OrAssign:
127 return Opcode;
129 llvm_unreachable("Unknown BinaryOperatorKind enum");
132 /// Swaps the operands to a binary operator
133 /// Before:
134 /// x != nullptr
135 /// ^ ^^^^^^^
136 /// After:
137 /// nullptr != x
138 class SwapBinaryOperands : public Tweak {
139 public:
140 const char *id() const final;
142 bool prepare(const Selection &Inputs) override;
143 Expected<Effect> apply(const Selection &Inputs) override;
144 std::string title() const override {
145 return llvm::formatv("Swap operands to {0}",
146 Op ? Op->getOpcodeStr() : "binary operator");
148 llvm::StringLiteral kind() const override {
149 return CodeAction::REFACTOR_KIND;
151 bool hidden() const override { return false; }
153 private:
154 const BinaryOperator *Op;
157 REGISTER_TWEAK(SwapBinaryOperands)
159 bool SwapBinaryOperands::prepare(const Selection &Inputs) {
160 for (const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
161 N && !Op; N = N->Parent) {
162 // Stop once we hit a block, e.g. a lambda in one of the operands.
163 // This makes sure that the selection point is in the 'scope' of the binary
164 // operator, not from somewhere inside a lambda for example
165 // (5 < [](){ ^return 1; })
166 if (llvm::isa_and_nonnull<CompoundStmt>(N->ASTNode.get<Stmt>()))
167 return false;
168 Op = dyn_cast_or_null<BinaryOperator>(N->ASTNode.get<Stmt>());
169 // If we hit upon a nonswappable binary operator, ignore and keep going
170 if (Op && !isOpSwappable(Op->getOpcode())) {
171 Op = nullptr;
174 return Op != nullptr;
177 Expected<Tweak::Effect> SwapBinaryOperands::apply(const Selection &Inputs) {
178 const auto &Ctx = Inputs.AST->getASTContext();
179 const auto &SrcMgr = Inputs.AST->getSourceManager();
181 const auto LHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(),
182 Op->getLHS()->getSourceRange());
183 if (!LHSRng)
184 return error(
185 "Could not obtain range of the 'lhs' of the operator. Macros?");
186 const auto RHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(),
187 Op->getRHS()->getSourceRange());
188 if (!RHSRng)
189 return error(
190 "Could not obtain range of the 'rhs' of the operator. Macros?");
191 const auto OpRng =
192 toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), Op->getOperatorLoc());
193 if (!OpRng)
194 return error("Could not obtain range of the operator itself. Macros?");
196 const auto LHSCode = toSourceCode(SrcMgr, *LHSRng);
197 const auto RHSCode = toSourceCode(SrcMgr, *RHSRng);
198 const auto OperatorCode = toSourceCode(SrcMgr, *OpRng);
200 tooling::Replacements Result;
201 if (auto Err = Result.add(tooling::Replacement(
202 Ctx.getSourceManager(), LHSRng->getBegin(), LHSCode.size(), RHSCode)))
203 return std::move(Err);
204 if (auto Err = Result.add(tooling::Replacement(
205 Ctx.getSourceManager(), RHSRng->getBegin(), RHSCode.size(), LHSCode)))
206 return std::move(Err);
207 const auto SwappedOperator = swapOperator(Op->getOpcode());
208 if (auto Err = Result.add(tooling::Replacement(
209 Ctx.getSourceManager(), OpRng->getBegin(), OperatorCode.size(),
210 Op->getOpcodeStr(SwappedOperator))))
211 return std::move(Err);
212 return Effect::mainFileEdit(SrcMgr, std::move(Result));
215 } // namespace
216 } // namespace clangd
217 } // namespace clang