clang-tools 20.0.0git
SwapBinaryOperands.cpp
Go to the documentation of this file.
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>
26
27namespace clang {
28namespace clangd {
29namespace {
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.
35bool 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;
76 }
77 return false;
78}
79
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
84BinaryOperatorKind swapOperator(const BinaryOperatorKind Opcode) {
85 switch (Opcode) {
86 case BinaryOperatorKind::BO_LT:
87 return BinaryOperatorKind::BO_GT;
88
89 case BinaryOperatorKind::BO_GT:
90 return BinaryOperatorKind::BO_LT;
91
92 case BinaryOperatorKind::BO_LE:
93 return BinaryOperatorKind::BO_GE;
94
95 case BinaryOperatorKind::BO_GE:
96 return BinaryOperatorKind::BO_LE;
97
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;
128 }
129 llvm_unreachable("Unknown BinaryOperatorKind enum");
130}
131
132/// Swaps the operands to a binary operator
133/// Before:
134/// x != nullptr
135/// ^ ^^^^^^^
136/// After:
137/// nullptr != x
138class SwapBinaryOperands : public Tweak {
139public:
140 const char *id() const final;
141
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");
147 }
148 llvm::StringLiteral kind() const override {
150 }
151 bool hidden() const override { return false; }
152
153private:
154 const BinaryOperator *Op;
155};
156
157REGISTER_TWEAK(SwapBinaryOperands)
158
159bool 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;
172 }
173 }
174 return Op != nullptr;
175}
176
177Expected<Tweak::Effect> SwapBinaryOperands::apply(const Selection &Inputs) {
178 const auto &Ctx = Inputs.AST->getASTContext();
179 const auto &SrcMgr = Inputs.AST->getSourceManager();
180
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?");
195
196 const auto LHSCode = toSourceCode(SrcMgr, *LHSRng);
197 const auto RHSCode = toSourceCode(SrcMgr, *RHSRng);
198 const auto OperatorCode = toSourceCode(SrcMgr, *OpRng);
199
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));
213}
214
215} // namespace
216} // namespace clangd
217} // namespace clang
std::vector< const char * > Expected
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:129
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
Definition: SourceCode.cpp:430
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:79
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
Definition: SourceCode.cpp:452
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static const llvm::StringLiteral REFACTOR_KIND
Definition: Protocol.h:1074