CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
218 lines
7.6 KiB
C++
218 lines
7.6 KiB
C++
//===--- 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
|