brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.6 KiB · 9ad0089 Raw
218 lines · cpp
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-exception6//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 swappable32/// while keeping the meaning intact, whereas comparison operators, mathematical33/// operators, etc. are often desired to be swappable for readability, avoiding34/// 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 noncommutative59  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 their81/// operands82/// @param[out] Opcode the opcode to potentially swap83/// If the opcode does not need to be swapped or is not swappable, does nothing84BinaryOperatorKind 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 operator133/// Before:134///   x != nullptr135///   ^    ^^^^^^^136/// After:137///   nullptr != x138class 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 {149    return CodeAction::REFACTOR_KIND;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 binary164    // operator, not from somewhere inside a lambda for example165    // (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 going170    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} // namespace216} // namespace clangd217} // namespace clang218