ilya-biryukov updated this revision to Diff 182546.
ilya-biryukov added a comment.
- Move source code helpers to this patch
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D56611/new/
https://reviews.llvm.org/D56611
Files:
clang-tools-extra/clangd/SourceCode.cpp
clang-tools-extra/clangd/SourceCode.h
clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp
Index: clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp
@@ -0,0 +1,119 @@
+//===--- 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 "ClangdUnit.h"
+#include "Logger.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.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 {
+namespace {
+/// Swaps the 'then' and 'else' branch of the if statement.
+/// Before:
+/// if (foo) { return 10; } else { continue; }
+/// After:
+/// if (foo) { continue; } else { return 10; }
+class SwapIfBranches : public Tweak {
+public:
+ TweakID id() const override {
+ return llvm::StringLiteral("swap-if-branches");
+ }
+
+ bool prepare(const Selection &Inputs) override;
+ Expected<tooling::Replacements> apply(const Selection &Inputs) override;
+ std::string title() const override;
+
+private:
+ tooling::Replacements Result;
+};
+REGISTER_TWEAK(SwapIfBranches);
+
+class FindIfUnderCursor : public RecursiveASTVisitor<FindIfUnderCursor> {
+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<CompoundStmt>(If->getThen()) ||
+ !llvm::dyn_cast_or_null<CompoundStmt>(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<tooling::Replacements> SwapIfBranches::apply(const Selection &Inputs) {
+ return Result;
+}
+
+std::string SwapIfBranches::title() const { return "Swap if branches"; }
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -9,4 +9,5 @@
# clangd/tool/CMakeLists.txt for an example.
add_clang_library(clangDaemonTweaks OBJECT
QualifyName.cpp
+ SwapIfBranches.cpp
)
Index: clang-tools-extra/clangd/SourceCode.h
===================================================================
--- clang-tools-extra/clangd/SourceCode.h
+++ clang-tools-extra/clangd/SourceCode.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H
#include "Protocol.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Core/Replacement.h"
@@ -60,6 +61,30 @@
llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,
Position P);
+/// Turns a token range into a half-open range and checks its correctness.
+/// The resulting range will have only valid source location on both sides, both
+/// of which are file locations.
+llvm::Optional<SourceRange> toHalfOpenFileRange(const SourceManager &Mgr,
+ const LangOptions &LangOpts,
+ SourceRange R);
+
+/// Returns true iff all of the following conditions hold:
+/// - start and end locations are valid,
+/// - start and end locations are file locations from the same file
+/// (i.e. expansion locations are not taken into account).
+/// - start offset <= end offset.
+/// FIXME: introduce a type for source range with this invariant.
+bool isValidFileRange(const SourceManager &Mgr, SourceRange R);
+
+/// Returns true iff \p L is contained in \p R.
+/// EXPECTS: isValidFileRange(R) == true, L is a file location.
+bool halfOpenRangeContains(const SourceManager &Mgr, SourceRange R,
+ SourceLocation L);
+
+/// Returns the source code covered by the source range.
+/// EXPECTS: isValidRange(R) == true.
+llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R);
+
// Converts a half-open clang source range to an LSP range.
// Note that clang also uses closed source ranges, which this can't handle!
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R);
Index: clang-tools-extra/clangd/SourceCode.cpp
===================================================================
--- clang-tools-extra/clangd/SourceCode.cpp
+++ clang-tools-extra/clangd/SourceCode.cpp
@@ -12,6 +12,8 @@
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
@@ -142,6 +144,64 @@
return P;
}
+bool isValidFileRange(const SourceManager &Mgr, SourceRange R) {
+ if (!R.getBegin().isValid() || !R.getEnd().isValid())
+ return false;
+
+ FileID BeginFID;
+ size_t BeginOffset = 0;
+ std::tie(BeginFID, BeginOffset) = Mgr.getDecomposedLoc(R.getBegin());
+
+ FileID EndFID;
+ size_t EndOffset = 0;
+ std::tie(EndFID, EndOffset) = Mgr.getDecomposedLoc(R.getEnd());
+
+ return BeginFID.isValid() && BeginFID == EndFID && BeginOffset <= EndOffset;
+}
+
+bool halfOpenRangeContains(const SourceManager &Mgr, SourceRange R,
+ SourceLocation L) {
+ assert(isValidFileRange(Mgr, R));
+
+ FileID BeginFID;
+ size_t BeginOffset = 0;
+ std::tie(BeginFID, BeginOffset) = Mgr.getDecomposedLoc(R.getBegin());
+ size_t EndOffset = Mgr.getFileOffset(R.getEnd());
+
+ FileID LFid;
+ size_t LOffset;
+ std::tie(LFid, LOffset) = Mgr.getDecomposedLoc(L);
+ return BeginFID == LFid && BeginOffset <= LOffset && LOffset < EndOffset;
+}
+
+llvm::Optional<SourceRange> toHalfOpenFileRange(const SourceManager &Mgr,
+ const LangOptions &LangOpts,
+ SourceRange R) {
+ auto Begin = Mgr.getFileLoc(R.getBegin());
+ if (Begin.isInvalid())
+ return llvm::None;
+ auto End = Mgr.getFileLoc(R.getEnd());
+ if (End.isInvalid())
+ return llvm::None;
+ End = Lexer::getLocForEndOfToken(End, 0, Mgr, LangOpts);
+
+ SourceRange Result(Begin, End);
+ if (!isValidFileRange(Mgr, Result))
+ return llvm::None;
+ return Result;
+}
+
+llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R) {
+ assert(isValidFileRange(SM, R));
+ bool Invalid = false;
+ auto *Buf = SM.getBuffer(SM.getFileID(R.getBegin()), &Invalid);
+ assert(!Invalid);
+
+ size_t BeginOffset = SM.getFileOffset(R.getBegin());
+ size_t EndOffset = SM.getFileOffset(R.getEnd());
+ return Buf->getBuffer().substr(BeginOffset, EndOffset - BeginOffset);
+}
+
llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,
Position P) {
llvm::StringRef Code = SM.getBuffer(SM.getMainFileID())->getBuffer();
@@ -170,8 +230,7 @@
return {Lines + 1, Offset - StartOfLine + 1};
}
-std::pair<llvm::StringRef, llvm::StringRef>
-splitQualifiedName(llvm::StringRef QName) {
+std::pair<StringRef, StringRef> splitQualifiedName(StringRef QName) {
size_t Pos = QName.rfind("::");
if (Pos == llvm::StringRef::npos)
return {llvm::StringRef(), QName};
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits