[cfe-commits] r160255 - in /cfe/trunk: include/clang/ASTMatchers/RefactoringCallbacks.h include/clang/Tooling/Tooling.h lib/ASTMatchers/CMakeLists.txt lib/ASTMatchers/RefactoringCallbacks.cpp unittests/ASTMatchers/CMakeLists.txt unittests/ASTMatchers/RefactoringCallbacksTest.cpp
Daniel Jasper
djasper at google.com
Mon Jul 16 02:18:17 PDT 2012
Author: djasper
Date: Mon Jul 16 04:18:17 2012
New Revision: 160255
URL: http://llvm.org/viewvc/llvm-project?rev=160255&view=rev
Log:
Add refactoring callbacks to make common kinds of refactorings easy.
Added:
cfe/trunk/include/clang/ASTMatchers/RefactoringCallbacks.h
cfe/trunk/lib/ASTMatchers/RefactoringCallbacks.cpp
cfe/trunk/unittests/ASTMatchers/RefactoringCallbacksTest.cpp
Modified:
cfe/trunk/include/clang/Tooling/Tooling.h
cfe/trunk/lib/ASTMatchers/CMakeLists.txt
cfe/trunk/unittests/ASTMatchers/CMakeLists.txt
Added: cfe/trunk/include/clang/ASTMatchers/RefactoringCallbacks.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/RefactoringCallbacks.h?rev=160255&view=auto
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/RefactoringCallbacks.h (added)
+++ cfe/trunk/include/clang/ASTMatchers/RefactoringCallbacks.h Mon Jul 16 04:18:17 2012
@@ -0,0 +1,90 @@
+//===--- RefactoringCallbacks.h - Structural query framework ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides callbacks to make common kinds of refactorings easy.
+//
+// The general idea is to construct a matcher expression that describes a
+// subtree match on the AST and then replace the corresponding source code
+// either by some specific text or some other AST node.
+//
+// Example:
+// int main(int argc, char **argv) {
+// ClangTool Tool(argc, argv);
+// MatchFinder Finder;
+// ReplaceStmtWithText Callback("integer", "42");
+// Finder.AddMatcher(id("integer", expression(integerLiteral())), Callback);
+// return Tool.run(newFrontendActionFactory(&Finder));
+// }
+//
+// This will replace all integer literals with "42".
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_REFACTORING_CALLBACKS_H
+#define LLVM_CLANG_AST_MATCHERS_REFACTORING_CALLBACKS_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+namespace ast_matchers {
+
+/// \brief Base class for RefactoringCallbacks.
+///
+/// Collects \c tooling::Replacements while running.
+class RefactoringCallback : public MatchFinder::MatchCallback {
+public:
+ RefactoringCallback();
+ tooling::Replacements &getReplacements();
+
+protected:
+ tooling::Replacements Replace;
+};
+
+/// \brief Replace the text of the statement bound to \c FromId with the text in
+/// \c ToText.
+class ReplaceStmtWithText : public RefactoringCallback {
+public:
+ ReplaceStmtWithText(StringRef FromId, StringRef ToText);
+ virtual void run(const MatchFinder::MatchResult &Result);
+
+private:
+ std::string FromId;
+ std::string ToText;
+};
+
+/// \brief Replace the text of the statement bound to \c FromId with the text of
+/// the statement bound to \c ToId.
+class ReplaceStmtWithStmt : public RefactoringCallback {
+public:
+ ReplaceStmtWithStmt(StringRef FromId, StringRef ToId);
+ virtual void run(const MatchFinder::MatchResult &Result);
+
+private:
+ std::string FromId;
+ std::string ToId;
+};
+
+/// \brief Replace an if-statement bound to \c Id with the outdented text of its
+/// body, choosing the consequent or the alternative based on whether
+/// \c PickTrueBranch is true.
+class ReplaceIfStmtWithItsBody : public RefactoringCallback {
+public:
+ ReplaceIfStmtWithItsBody(StringRef Id, bool PickTrueBranch);
+ virtual void run(const MatchFinder::MatchResult &Result);
+
+private:
+ std::string Id;
+ const bool PickTrueBranch;
+};
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_REFACTORING_CALLBACKS_H
Modified: cfe/trunk/include/clang/Tooling/Tooling.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Tooling.h?rev=160255&r1=160254&r2=160255&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Tooling.h (original)
+++ cfe/trunk/include/clang/Tooling/Tooling.h Mon Jul 16 04:18:17 2012
@@ -86,7 +86,8 @@
/// FrontendActionFactory *FactoryAdapter =
/// newFrontendActionFactory(&Factory);
template <typename FactoryT>
-FrontendActionFactory *newFrontendActionFactory(FactoryT *ConsumerFactory);
+inline FrontendActionFactory *newFrontendActionFactory(
+ FactoryT *ConsumerFactory);
/// \brief Runs (and deletes) the tool on 'Code' with the -fsyntax-only flag.
///
@@ -202,7 +203,8 @@
}
template <typename FactoryT>
-FrontendActionFactory *newFrontendActionFactory(FactoryT *ConsumerFactory) {
+inline FrontendActionFactory *newFrontendActionFactory(
+ FactoryT *ConsumerFactory) {
class FrontendActionFactoryAdapter : public FrontendActionFactory {
public:
explicit FrontendActionFactoryAdapter(FactoryT *ConsumerFactory)
Modified: cfe/trunk/lib/ASTMatchers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/CMakeLists.txt?rev=160255&r1=160254&r2=160255&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/CMakeLists.txt (original)
+++ cfe/trunk/lib/ASTMatchers/CMakeLists.txt Mon Jul 16 04:18:17 2012
@@ -4,6 +4,7 @@
add_clang_library(clangASTMatchers
ASTMatchFinder.cpp
ASTMatchersInternal.cpp
+ RefactoringCallbacks.cpp
)
add_dependencies(clangASTMatchers
Added: cfe/trunk/lib/ASTMatchers/RefactoringCallbacks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/RefactoringCallbacks.cpp?rev=160255&view=auto
==============================================================================
--- cfe/trunk/lib/ASTMatchers/RefactoringCallbacks.cpp (added)
+++ cfe/trunk/lib/ASTMatchers/RefactoringCallbacks.cpp Mon Jul 16 04:18:17 2012
@@ -0,0 +1,78 @@
+//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Lex/Lexer.h"
+#include "clang/ASTMatchers/RefactoringCallbacks.h"
+
+namespace clang {
+namespace ast_matchers {
+
+RefactoringCallback::RefactoringCallback() {}
+tooling::Replacements &RefactoringCallback::getReplacements() {
+ return Replace;
+}
+
+static tooling::Replacement replaceStmtWithText(SourceManager &Sources,
+ const Stmt &From,
+ StringRef Text) {
+ return tooling::Replacement(Sources, CharSourceRange::getTokenRange(
+ From.getSourceRange()), Text);
+}
+static tooling::Replacement replaceStmtWithStmt(SourceManager &Sources,
+ const Stmt &From,
+ const Stmt &To) {
+ return replaceStmtWithText(Sources, From, Lexer::getSourceText(
+ CharSourceRange::getTokenRange(To.getSourceRange()),
+ Sources, LangOptions()));
+}
+
+ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
+ : FromId(FromId), ToText(ToText) {}
+
+void ReplaceStmtWithText::run(const MatchFinder::MatchResult &Result) {
+ if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
+ Replace.insert(tooling::Replacement(
+ *Result.SourceManager,
+ CharSourceRange::getTokenRange(FromMatch->getSourceRange()),
+ ToText));
+ }
+}
+
+ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
+ : FromId(FromId), ToId(ToId) {}
+
+void ReplaceStmtWithStmt::run(const MatchFinder::MatchResult &Result) {
+ const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
+ const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
+ if (FromMatch && ToMatch)
+ Replace.insert(replaceStmtWithStmt(
+ *Result.SourceManager, *FromMatch, *ToMatch));
+}
+
+ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
+ bool PickTrueBranch)
+ : Id(Id), PickTrueBranch(PickTrueBranch) {}
+
+void ReplaceIfStmtWithItsBody::run(const MatchFinder::MatchResult &Result) {
+ if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
+ const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
+ if (Body) {
+ Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
+ } else if (!PickTrueBranch) {
+ // If we want to use the 'else'-branch, but it doesn't exist, delete
+ // the whole 'if'.
+ Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, ""));
+ }
+ }
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
Modified: cfe/trunk/unittests/ASTMatchers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/CMakeLists.txt?rev=160255&r1=160254&r2=160255&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/CMakeLists.txt (original)
+++ cfe/trunk/unittests/ASTMatchers/CMakeLists.txt Mon Jul 16 04:18:17 2012
@@ -1,5 +1,6 @@
add_clang_unittest(ASTMatchersTests
- ASTMatchersTest.cpp)
+ ASTMatchersTest.cpp
+ RefactoringCallbacksTest.cpp)
target_link_libraries(ASTMatchersTests
gtest gtest_main clangASTMatchers clangTooling)
Added: cfe/trunk/unittests/ASTMatchers/RefactoringCallbacksTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/RefactoringCallbacksTest.cpp?rev=160255&view=auto
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/RefactoringCallbacksTest.cpp (added)
+++ cfe/trunk/unittests/ASTMatchers/RefactoringCallbacksTest.cpp Mon Jul 16 04:18:17 2012
@@ -0,0 +1,98 @@
+//===- unittest/ASTMatchers/RefactoringCallbacksTest.cpp ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/RefactoringCallbacks.h"
+#include "../Tooling/RewriterTestContext.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+template <typename T>
+void expectRewritten(const std::string &Code,
+ const std::string &Expected,
+ const T &AMatcher,
+ RefactoringCallback &Callback) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher, &Callback);
+ OwningPtr<tooling::FrontendActionFactory> Factory(
+ tooling::newFrontendActionFactory(&Finder));
+ ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
+ << "Parsing error in \"" << Code << "\"";
+ RewriterTestContext Context;
+ FileID ID = Context.createInMemoryFile("input.cc", Code);
+ EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+ Context.Rewrite));
+ EXPECT_EQ(Expected, Context.getRewrittenText(ID));
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) {
+ std::string Code = "void f() { int i = 1; }";
+ std::string Expected = "void f() { ; }";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) {
+ std::string Code = "#define A void f() { int i = 1; }\nA";
+ std::string Expected = "#define A void f() { ; }\nA";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) {
+ std::string Code = "#define A void f() { int i = 1; }";
+ std::string Expected = "#define A void f() { int i = 1; }";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesInteger) {
+ std::string Code = "void f() { int i = 1; }";
+ std::string Expected = "void f() { int i = 2; }";
+ ReplaceStmtWithText Callback("id", "2");
+ expectRewritten(Code, Expected, id("id", expression(integerLiteral())),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
+ std::string Code = "void f() { int i = false ? 1 : i * 2; }";
+ std::string Expected = "void f() { int i = i * 2; }";
+ ReplaceStmtWithStmt Callback("always-false", "should-be");
+ expectRewritten(Code, Expected,
+ id("always-false", conditionalOperator(
+ hasCondition(boolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expression())))),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
+ std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
+ std::string Expected = "bool a; void f() { f(); }";
+ ReplaceIfStmtWithItsBody Callback("id", true);
+ expectRewritten(Code, Expected,
+ id("id", ifStmt(
+ hasCondition(implicitCast(hasSourceExpression(
+ declarationReference(to(variable(hasName("a"))))))))),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
+ std::string Code = "void f() { if (false) int i = 0; }";
+ std::string Expected = "void f() { }";
+ ReplaceIfStmtWithItsBody Callback("id", false);
+ expectRewritten(Code, Expected,
+ id("id", ifStmt(hasCondition(boolLiteral(equals(false))))),
+ Callback);
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
More information about the cfe-commits
mailing list