Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -72,6 +72,7 @@ refactor/Tweak.cpp refactor/tweaks/QualifyName.cpp + refactor/tweaks/SwapIfBranches.cpp LINK_LIBS clangAST Index: clangd/refactor/tweaks/SwapIfBranches.h =================================================================== --- /dev/null +++ clangd/refactor/tweaks/SwapIfBranches.h @@ -0,0 +1,41 @@ +//===--- SwapIfBrances.h -----------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Swaps the 'then' and 'else' branch of the if statement. +// Before: +// if (foo) { return 10; } else { continue; } +// After: +// if (foo) { continue; } else { return 10; } +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_TWEAKS_SWAPIFBRANCHES_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_TWEAKS_SWAPIFBRANCHES_H + +#include "refactor/Tweak.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace clangd { + +class SwapIfBranches : public Tweak { +public: + TweakID id() override { return llvm::StringLiteral("swap-if-branches"); } + + bool prepare(const Selection &Inputs) override; + Expected apply(const Selection &Inputs) override; + std::string title() const override; + +private: + tooling::Replacements Result; +}; + +} // namespace clangd +} // namespace clang + +#endif \ No newline at end of file Index: clangd/refactor/tweaks/SwapIfBranches.cpp =================================================================== --- /dev/null +++ clangd/refactor/tweaks/SwapIfBranches.cpp @@ -0,0 +1,102 @@ +//===--- SwapIfBranches.cpp --------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "SwapIfBranches.h" +#include "ClangdUnit.h" +#include "Logger.h" +#include "SourceCode.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { + +REGISTER_TWEAK(SwapIfBranches); + +namespace { +class FindIfUnderCursor : public RecursiveASTVisitor { +public: + FindIfUnderCursor(ASTContext &Ctx, SourceLocation CursorLoc, IfStmt *&Result) + : Ctx(Ctx), CursorLoc(CursorLoc), Result(Result) {} + + bool VisitIfStmt(IfStmt *If) { + auto R = toHalfOpenFileRange(Ctx.getSourceManager(), Ctx.getLangOpts(), + SourceRange(If->getIfLoc())); + if (!R) + return true; + if (!halfOpenRangeContains(Ctx.getSourceManager(), *R, CursorLoc)) + return true; + Result = If; + return false; + } + +private: + ASTContext &Ctx; + SourceLocation CursorLoc; + IfStmt *&Result; +}; +} // namespace + +bool SwapIfBranches::prepare(const Selection &Inputs) { + + auto &Ctx = Inputs.AST.getASTContext(); + auto &SrcMgr = Ctx.getSourceManager(); + IfStmt *If = nullptr; + FindIfUnderCursor(Ctx, Inputs.Cursor, If).TraverseAST(Ctx); + if (!If) + return false; + + // avoid dealing with single-statement brances, they require careful handling + // to avoid changing semantics of the code (i.e. dangling else). + if (!llvm::dyn_cast_or_null(If->getThen()) || + !llvm::dyn_cast_or_null(If->getElse())) + return false; + + auto ThenRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + If->getThen()->getSourceRange()); + if (!ThenRng) + return false; + auto ElseRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + If->getElse()->getSourceRange()); + if (!ElseRng) + return false; + + llvm::StringRef ThenCode = toSourceCode(SrcMgr, *ThenRng); + llvm::StringRef ElseCode = toSourceCode(SrcMgr, *ElseRng); + + if (auto Err = Result.add(tooling::Replacement(SrcMgr, ThenRng->getBegin(), + ThenCode.size(), ElseCode))) { + llvm::consumeError(std::move(Err)); + return false; + } + if (auto Err = Result.add(tooling::Replacement(SrcMgr, ElseRng->getBegin(), + ElseCode.size(), ThenCode))) { + llvm::consumeError(std::move(Err)); + return false; + } + return true; +} + +Expected SwapIfBranches::apply(const Selection &Inputs) { + return Result; +} + +std::string SwapIfBranches::title() const { return "Swap if branches"; } +} // namespace clangd +} // namespace clang