Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -65,6 +65,7 @@ refactor/ActionProvider.cpp refactor/actions/QualifyName.cpp + refactor/actions/SwapIfBranches.cpp LINK_LIBS clangAST Index: clangd/CodeActions.cpp =================================================================== --- clangd/CodeActions.cpp +++ clangd/CodeActions.cpp @@ -9,6 +9,7 @@ #include "CodeActions.h" #include "Logger.h" #include "refactor/actions/QualifyName.h" +#include "refactor/actions/SwapIfBranches.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" @@ -22,6 +23,7 @@ CodeActions::CodeActions() { // FIXME: provide a registry of the providers. Actions.push_back(llvm::make_unique()); + Actions.push_back(llvm::make_unique()); } std::vector Index: clangd/refactor/actions/SwapIfBranches.h =================================================================== --- /dev/null +++ clangd/refactor/actions/SwapIfBranches.h @@ -0,0 +1,31 @@ +//===--- SwapIfBrances.h -----------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// A code action that 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_ACTIONS_SWAPIFBRANCHES_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_ACTIONS_SWAPIFBRANCHES_H + +#include "refactor/ActionProvider.h" + +namespace clang { +namespace clangd { + +class SwapIfBranches : public ActionProvider { + llvm::Optional prepare(const ActionInputs &Inputs) override; +}; + +} // namespace clangd +} // namespace clang + +#endif \ No newline at end of file Index: clangd/refactor/actions/SwapIfBranches.cpp =================================================================== --- /dev/null +++ clangd/refactor/actions/SwapIfBranches.cpp @@ -0,0 +1,94 @@ +//===--- 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 "CodeActions.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 { + +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; +}; + +llvm::Optional +SwapIfBranches::prepare(const ActionInputs &Inputs) { + auto &Ctx = Inputs.AST.getASTContext(); + auto &SrcMgr = Ctx.getSourceManager(); + IfStmt *If = nullptr; + FindIfUnderCursor(Ctx, Inputs.Cursor, If).TraverseAST(Ctx); + if (!If) + return llvm::None; + + // 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 llvm::None; + + auto ThenRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + If->getThen()->getSourceRange()); + if (!ThenRng) + return llvm::None; + auto ElseRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + If->getElse()->getSourceRange()); + if (!ElseRng) + return llvm::None; + + llvm::StringRef ThenCode = toSourceCode(SrcMgr, *ThenRng); + llvm::StringRef ElseCode = toSourceCode(SrcMgr, *ElseRng); + + tooling::Replacements R; + if (auto Err = R.add(tooling::Replacement(SrcMgr, ThenRng->getBegin(), + ThenCode.size(), ElseCode))) { + llvm::consumeError(std::move(Err)); + return llvm::None; + } + if (auto Err = R.add(tooling::Replacement(SrcMgr, ElseRng->getBegin(), + ElseCode.size(), ThenCode))) { + llvm::consumeError(std::move(Err)); + return llvm::None; + } + return PreparedAction("Swap if branches", std::move(R)); +} +} // namespace clangd +} // namespace clang