r313244 - [refactor] add clang-refactor tool with initial testing support and
Alex Lorenz via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 14 03:06:52 PDT 2017
Author: arphaman
Date: Thu Sep 14 03:06:52 2017
New Revision: 313244
URL: http://llvm.org/viewvc/llvm-project?rev=313244&view=rev
Log:
[refactor] add clang-refactor tool with initial testing support and
local-rename action
This commit introduces the clang-refactor tool alongside the local-rename action
which uses the existing renaming engine used by clang-rename. The tool
doesn't actually perform the source transformations yet, it just provides
testing support. This commit also moves only one test from clang-rename over to
test/Refactor. I will continue to move the other tests throughout
development of clang-refactor.
The following options are supported by clang-refactor:
-v: use verbose output
-selection: The source range that corresponds to the portion of the source
that's selected (currently only special command test:<file> is supported).
Please note that a follow-up commit will migrate clang-refactor to
libTooling's common option parser, so clang-refactor will be able to use
the common interface with compilation database and options like -p, -extra-arg,
etc.
The testing support provided by clang-refactor is described below:
When -selection=test:<file> is given, clang-refactor will parse the selection
commands from that file. The selection commands are grouped and the specified
refactoring action invoked by the tool. Each command in a group is expected to
produce an identical result. The precise syntax for the selection commands is
described in a comment in TestSupport.h.
Differential Revision: https://reviews.llvm.org/D36574
Added:
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringAction.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRegistry.def
cfe/trunk/lib/Tooling/Refactoring/RefactoringActions.cpp
cfe/trunk/test/Refactor/
cfe/trunk/test/Refactor/LocalRename/
cfe/trunk/test/Refactor/LocalRename/Field.cpp
cfe/trunk/test/Refactor/tool-common-options.c
cfe/trunk/test/Refactor/tool-test-support.c
cfe/trunk/tools/clang-refactor/
cfe/trunk/tools/clang-refactor/CMakeLists.txt
cfe/trunk/tools/clang-refactor/ClangRefactor.cpp
cfe/trunk/tools/clang-refactor/TestSupport.cpp
cfe/trunk/tools/clang-refactor/TestSupport.h
Removed:
cfe/trunk/test/clang-rename/Field.cpp
Modified:
cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
cfe/trunk/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h
cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h
cfe/trunk/include/clang/module.modulemap
cfe/trunk/lib/Tooling/Refactoring/AtomicChange.cpp
cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
cfe/trunk/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
cfe/trunk/test/CMakeLists.txt
cfe/trunk/tools/CMakeLists.txt
cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp
Modified: cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h Thu Sep 14 03:06:52 2017
@@ -52,6 +52,8 @@ public:
AtomicChange &operator=(AtomicChange &&) = default;
AtomicChange &operator=(const AtomicChange &) = default;
+ bool operator==(const AtomicChange &Other) const;
+
/// \brief Returns the atomic change as a YAML string.
std::string toYAMLString();
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringAction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringAction.h?rev=313244&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringAction.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringAction.h Thu Sep 14 03:06:52 2017
@@ -0,0 +1,64 @@
+//===--- RefactoringAction.h - Clang refactoring library ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+/// A refactoring action is a class that defines a set of related refactoring
+/// action rules. These rules get grouped under a common umbrella - a single
+/// clang-refactor subcommand.
+///
+/// A subclass of \c RefactoringAction is responsible for creating the set of
+/// grouped refactoring action rules that represent one refactoring operation.
+/// Although the rules in one action may have a number of different
+/// implementations, they should strive to produce a similar result. It should
+/// be easy for users to identify which refactoring action produced the result
+/// regardless of which refactoring action rule was used.
+///
+/// The distinction between actions and rules enables the creation of action
+/// that uses very different rules, for example:
+/// - local vs global: a refactoring operation like
+/// "add missing switch cases" can be applied to one switch when it's
+/// selected in an editor, or to all switches in a project when an enum
+/// constant is added to an enum.
+/// - tool vs editor: some refactoring operation can be initiated in the
+/// editor when a declaration is selected, or in a tool when the name of
+/// the declaration is passed using a command-line argument.
+class RefactoringAction {
+public:
+ virtual ~RefactoringAction() {}
+
+ /// Returns the name of the subcommand that's used by clang-refactor for this
+ /// action.
+ virtual StringRef getCommand() const = 0;
+
+ virtual StringRef getDescription() const = 0;
+
+ RefactoringActionRules createActiveActionRules();
+
+protected:
+ /// Returns a set of refactoring actions rules that are defined by this
+ /// action.
+ virtual RefactoringActionRules createActionRules() const = 0;
+};
+
+/// Returns the list of all the available refactoring actions.
+std::vector<std::unique_ptr<RefactoringAction>> createRefactoringActions();
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRegistry.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRegistry.def?rev=313244&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRegistry.def (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRegistry.def Thu Sep 14 03:06:52 2017
@@ -0,0 +1,7 @@
+#ifndef REFACTORING_ACTION
+#define REFACTORING_ACTION(Name)
+#endif
+
+REFACTORING_ACTION(LocalRename)
+
+#undef REFACTORING_ACTION
Modified: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h Thu Sep 14 03:06:52 2017
@@ -30,6 +30,10 @@ public:
/// consumer to propagate the result of the refactoring action.
virtual void invoke(RefactoringResultConsumer &Consumer,
RefactoringRuleContext &Context) = 0;
+
+ /// Returns true when the rule has a source selection requirement that has
+ /// to be fullfilled before refactoring can be performed.
+ virtual bool hasSelectionRequirement() = 0;
};
/// A set of refactoring action rules that should have unique initiation
Modified: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h Thu Sep 14 03:06:52 2017
@@ -58,7 +58,7 @@ struct SourceSelectionRequirement
Optional<InputT> Value = InputT::evaluate(Context);
if (!Value)
return None;
- return std::move(Requirement.evaluateSelection(*Value));
+ return std::move(Requirement.evaluateSelection(Context, *Value));
}
private:
@@ -82,6 +82,20 @@ struct IsRequirement<T>
: std::conditional<std::is_base_of<internal::RequirementBase, T>::value,
std::true_type, std::false_type>::type {};
+/// A type trait that returns true when the given type has at least one source
+/// selection requirement.
+template <typename First, typename... Rest>
+struct HasSelectionRequirement
+ : std::conditional<HasSelectionRequirement<First>::value ||
+ HasSelectionRequirement<Rest...>::value,
+ std::true_type, std::false_type>::type {};
+
+template <typename I, typename O, typename R>
+struct HasSelectionRequirement<internal::SourceSelectionRequirement<I, O, R>>
+ : std::true_type {};
+
+template <typename T> struct HasSelectionRequirement<T> : std::false_type {};
+
} // end namespace traits
} // end namespace refactoring_action_rules
} // end namespace tooling
Modified: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h Thu Sep 14 03:06:52 2017
@@ -15,6 +15,9 @@
namespace clang {
namespace tooling {
+
+class RefactoringRuleContext;
+
namespace refactoring_action_rules {
/// Creates a new refactoring action rule that invokes the given function once
@@ -40,6 +43,7 @@ namespace refactoring_action_rules {
template <typename ResultType, typename... RequirementTypes>
std::unique_ptr<RefactoringActionRule>
createRefactoringRule(Expected<ResultType> (*RefactoringFunction)(
+ const RefactoringRuleContext &,
typename RequirementTypes::OutputType...),
const RequirementTypes &... Requirements) {
static_assert(tooling::traits::IsValidRefactoringResult<ResultType>::value,
@@ -56,13 +60,12 @@ template <
typename Fn = decltype(&Callable::operator()),
typename ResultType = typename internal::LambdaDeducer<Fn>::ReturnType,
bool IsNonCapturingLambda = std::is_convertible<
- Callable,
- ResultType (*)(typename RequirementTypes::OutputType...)>::value,
+ Callable, typename internal::LambdaDeducer<Fn>::FunctionType>::value,
typename = typename std::enable_if<IsNonCapturingLambda>::type>
std::unique_ptr<RefactoringActionRule>
createRefactoringRule(const Callable &C,
const RequirementTypes &... Requirements) {
- ResultType (*Func)(typename RequirementTypes::OutputType...) = C;
+ typename internal::LambdaDeducer<Fn>::FunctionType Func = C;
return createRefactoringRule(Func, Requirements...);
}
Modified: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h Thu Sep 14 03:06:52 2017
@@ -39,6 +39,10 @@ public:
llvm::index_sequence_for<RequirementTypes...>());
}
+ bool hasSelectionRequirement() override {
+ return traits::HasSelectionRequirement<RequirementTypes...>::value;
+ }
+
private:
/// Returns \c T when given \c Expected<Optional<T>>, or \c T otherwise.
template <typename T>
@@ -79,7 +83,7 @@ private:
template <size_t... Is>
void invokeImpl(RefactoringResultConsumer &Consumer,
RefactoringRuleContext &Context,
- llvm::index_sequence<Is...>) {
+ llvm::index_sequence<Is...> Seq) {
// Initiate the operation.
auto Values =
std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...);
@@ -96,8 +100,8 @@ private:
return Consumer.handleError(std::move(Error));
}
// Perform the operation.
- auto Result =
- Function(unwrapRequirementResult(std::move(std::get<Is>(Values)))...);
+ auto Result = Function(
+ Context, unwrapRequirementResult(std::move(std::get<Is>(Values)))...);
if (!Result)
return Consumer.handleError(Result.takeError());
Consumer.handle(std::move(*Result));
@@ -111,8 +115,9 @@ private:
/// createRefactoringRule.
template <typename T> struct LambdaDeducer;
template <typename T, typename R, typename... Args>
-struct LambdaDeducer<R (T::*)(Args...) const> {
+struct LambdaDeducer<R (T::*)(const RefactoringRuleContext &, Args...) const> {
using ReturnType = R;
+ using FunctionType = R (*)(const RefactoringRuleContext &, Args...);
};
} // end namespace internal
Modified: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h Thu Sep 14 03:06:52 2017
@@ -13,6 +13,9 @@
#include "clang/Basic/SourceManager.h"
namespace clang {
+
+class ASTContext;
+
namespace tooling {
/// The refactoring rule context stores all of the inputs that might be needed
@@ -38,6 +41,15 @@ public:
void setSelectionRange(SourceRange R) { SelectionRange = R; }
+ bool hasASTContext() const { return AST; }
+
+ ASTContext &getASTContext() const {
+ assert(AST && "no AST!");
+ return *AST;
+ }
+
+ void setASTContext(ASTContext &Context) { AST = &Context; }
+
private:
/// The source manager for the translation unit / file on which a refactoring
/// action might operate on.
@@ -45,6 +57,9 @@ private:
/// An optional source selection range that's commonly used to represent
/// a selection in an editor.
SourceRange SelectionRange;
+ /// An optional AST for the translation unit on which a refactoring action
+ /// might operate on.
+ ASTContext *AST = nullptr;
};
} // end namespace tooling
Modified: cfe/trunk/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h Thu Sep 14 03:06:52 2017
@@ -23,6 +23,7 @@
namespace clang {
class ASTConsumer;
+class ASTContext;
class CompilerInstance;
class NamedDecl;
@@ -37,6 +38,10 @@ namespace tooling {
/// - A destructor is canonicalized to its class.
const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl);
+/// Returns the set of USRs that correspond to the given declaration.
+std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
+ ASTContext &Context);
+
struct USRFindingAction {
USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
ArrayRef<std::string> QualifiedNames, bool Force)
Modified: cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h Thu Sep 14 03:06:52 2017
@@ -12,6 +12,7 @@
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
#include <type_traits>
namespace clang {
@@ -40,9 +41,10 @@ private:
/// A custom selection requirement.
class Requirement {
- /// Subclasses must implement 'T evaluateSelection(SelectionConstraint) const'
- /// member function. \c T is used to determine the return type that is
- /// passed to the refactoring rule's function.
+ /// Subclasses must implement
+ /// 'T evaluateSelection(const RefactoringRuleContext &,
+ /// SelectionConstraint) const' member function. \c T is used to determine
+ /// the return type that is passed to the refactoring rule's function.
/// If T is \c DiagnosticOr<S> , then \c S is passed to the rule's function
/// using move semantics.
/// Otherwise, T is passed to the function directly using move semantics.
@@ -64,14 +66,17 @@ namespace internal {
template <typename T> struct EvaluateSelectionChecker : std::false_type {};
template <typename T, typename R, typename A>
-struct EvaluateSelectionChecker<R (T::*)(A) const> : std::true_type {
+struct EvaluateSelectionChecker<R (T::*)(const RefactoringRuleContext &, A)
+ const> : std::true_type {
using ReturnType = R;
using ArgType = A;
};
template <typename T> class Identity : public Requirement {
public:
- T evaluateSelection(T Value) const { return std::move(Value); }
+ T evaluateSelection(const RefactoringRuleContext &, T Value) const {
+ return std::move(Value);
+ }
};
} // end namespace internal
Modified: cfe/trunk/include/clang/module.modulemap
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/module.modulemap?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/include/clang/module.modulemap (original)
+++ cfe/trunk/include/clang/module.modulemap Thu Sep 14 03:06:52 2017
@@ -138,6 +138,8 @@ module Clang_Tooling {
// importing the AST matchers library gives a link dependency on the AST
// matchers (and thus the AST), which clang-format should not have.
exclude header "Tooling/RefactoringCallbacks.h"
+
+ textual header "Tooling/Refactoring/RefactoringActionRegistry.def"
}
module Clang_ToolingCore {
Modified: cfe/trunk/lib/Tooling/Refactoring/AtomicChange.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/AtomicChange.cpp?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/AtomicChange.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/AtomicChange.cpp Thu Sep 14 03:06:52 2017
@@ -215,6 +215,15 @@ AtomicChange::AtomicChange(std::string K
RemovedHeaders(std::move(RemovedHeaders)), Replaces(std::move(Replaces)) {
}
+bool AtomicChange::operator==(const AtomicChange &Other) const {
+ if (Key != Other.Key || FilePath != Other.FilePath || Error != Other.Error)
+ return false;
+ if (!(Replaces == Other.Replaces))
+ return false;
+ // FXIME: Compare header insertions/removals.
+ return true;
+}
+
std::string AtomicChange::toYAMLString() {
std::string YamlContent;
llvm::raw_string_ostream YamlContentStream(YamlContent);
Modified: cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt (original)
+++ cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt Thu Sep 14 03:06:52 2017
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS Support)
add_clang_library(clangToolingRefactor
ASTSelection.cpp
AtomicChange.cpp
+ RefactoringActions.cpp
Rename/RenamingAction.cpp
Rename/SymbolOccurrences.cpp
Rename/USRFinder.cpp
Added: cfe/trunk/lib/Tooling/Refactoring/RefactoringActions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/RefactoringActions.cpp?rev=313244&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/RefactoringActions.cpp (added)
+++ cfe/trunk/lib/Tooling/Refactoring/RefactoringActions.cpp Thu Sep 14 03:06:52 2017
@@ -0,0 +1,35 @@
+//===--- RefactoringActions.cpp - Constructs refactoring actions ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/RefactoringAction.h"
+
+namespace clang {
+namespace tooling {
+
+// Forward declare the individual create*Action functions.
+#define REFACTORING_ACTION(Name) \
+ std::unique_ptr<RefactoringAction> create##Name##Action();
+#include "clang/Tooling/Refactoring/RefactoringActionRegistry.def"
+
+std::vector<std::unique_ptr<RefactoringAction>> createRefactoringActions() {
+ std::vector<std::unique_ptr<RefactoringAction>> Actions;
+
+#define REFACTORING_ACTION(Name) Actions.push_back(create##Name##Action());
+#include "clang/Tooling/Refactoring/RefactoringActionRegistry.def"
+
+ return Actions;
+}
+
+RefactoringActionRules RefactoringAction::createActiveActionRules() {
+ // FIXME: Filter out rules that are not supported by a particular client.
+ return createActionRules();
+}
+
+} // end namespace tooling
+} // end namespace clang
Modified: cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp Thu Sep 14 03:06:52 2017
@@ -22,6 +22,10 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/RefactoringAction.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
+#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
+#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
@@ -33,6 +37,63 @@ using namespace llvm;
namespace clang {
namespace tooling {
+namespace {
+
+class LocalRename : public RefactoringAction {
+public:
+ StringRef getCommand() const override { return "local-rename"; }
+
+ StringRef getDescription() const override {
+ return "Finds and renames symbols in code with no indexer support";
+ }
+
+ /// Returns a set of refactoring actions rules that are defined by this
+ /// action.
+ RefactoringActionRules createActionRules() const override {
+ using namespace refactoring_action_rules;
+ RefactoringActionRules Rules;
+ Rules.push_back(createRefactoringRule(
+ renameOccurrences, requiredSelection(SymbolSelectionRequirement())));
+ return Rules;
+ }
+
+private:
+ static Expected<AtomicChanges>
+ renameOccurrences(const RefactoringRuleContext &Context,
+ const NamedDecl *ND) {
+ std::vector<std::string> USRs =
+ getUSRsForDeclaration(ND, Context.getASTContext());
+ std::string PrevName = ND->getNameAsString();
+ auto Occurrences = getOccurrencesOfUSRs(
+ USRs, PrevName, Context.getASTContext().getTranslationUnitDecl());
+
+ // FIXME: This is a temporary workaround that's needed until the refactoring
+ // options are implemented.
+ StringRef NewName = "Bar";
+ return createRenameReplacements(
+ Occurrences, Context.getASTContext().getSourceManager(), NewName);
+ }
+
+ class SymbolSelectionRequirement : public selection::Requirement {
+ public:
+ Expected<Optional<const NamedDecl *>>
+ evaluateSelection(const RefactoringRuleContext &Context,
+ selection::SourceSelectionRange Selection) const {
+ const NamedDecl *ND = getNamedDeclAt(Context.getASTContext(),
+ Selection.getRange().getBegin());
+ if (!ND)
+ return None;
+ return getCanonicalSymbolDeclaration(ND);
+ }
+ };
+};
+
+} // end anonymous namespace
+
+std::unique_ptr<RefactoringAction> createLocalRenameAction() {
+ return llvm::make_unique<LocalRename>();
+}
+
Expected<std::vector<AtomicChange>>
createRenameReplacements(const SymbolOccurrences &Occurrences,
const SourceManager &SM,
Modified: cfe/trunk/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp Thu Sep 14 03:06:52 2017
@@ -154,6 +154,12 @@ private:
};
} // namespace
+std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
+ ASTContext &Context) {
+ AdditionalUSRFinder Finder(ND, Context);
+ return Finder.Find();
+}
+
class NamedDeclFindingConsumer : public ASTConsumer {
public:
NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
Modified: cfe/trunk/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CMakeLists.txt?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/test/CMakeLists.txt (original)
+++ cfe/trunk/test/CMakeLists.txt Thu Sep 14 03:06:52 2017
@@ -48,6 +48,7 @@ list(APPEND CLANG_TEST_DEPS
clang-offload-bundler
clang-import-test
clang-rename
+ clang-refactor
clang-diff
)
Added: cfe/trunk/test/Refactor/LocalRename/Field.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/LocalRename/Field.cpp?rev=313244&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/LocalRename/Field.cpp (added)
+++ cfe/trunk/test/Refactor/LocalRename/Field.cpp Thu Sep 14 03:06:52 2017
@@ -0,0 +1,9 @@
+// RUN: clang-refactor local-rename -selection=test:%s -no-dbs %s | FileCheck %s
+
+class Baz {
+ int /*range=*/Foo; // CHECK: int /*range=*/Bar;
+public:
+ Baz();
+};
+
+Baz::Baz() : /*range=*/Foo(0) {} // CHECK: Baz::Baz() : /*range=*/Bar(0) {};
Added: cfe/trunk/test/Refactor/tool-common-options.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/tool-common-options.c?rev=313244&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/tool-common-options.c (added)
+++ cfe/trunk/test/Refactor/tool-common-options.c Thu Sep 14 03:06:52 2017
@@ -0,0 +1,6 @@
+// RUN: not clang-refactor 2>&1 | FileCheck --check-prefix=MISSING_ACTION %s
+// MISSING_ACTION: error: no refactoring action given
+// MISSING_ACTION-NEXT: note: the following actions are supported:
+
+// RUN: not clang-refactor local-rename -no-dbs 2>&1 | FileCheck --check-prefix=MISSING_SOURCES %s
+// MISSING_SOURCES: error: must provide paths to the source files when '-no-dbs' is used
Added: cfe/trunk/test/Refactor/tool-test-support.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/tool-test-support.c?rev=313244&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/tool-test-support.c (added)
+++ cfe/trunk/test/Refactor/tool-test-support.c Thu Sep 14 03:06:52 2017
@@ -0,0 +1,41 @@
+// RUN: clang-refactor local-rename -selection=test:%s -no-dbs -v %s 2>&1 | FileCheck %s
+
+/*range=*/int test;
+
+/*range named=*/int test2;
+
+/*range= +1*/int test3;
+
+/* range = +100 */int test4;
+
+/*range named =+0*/int test5;
+
+// CHECK: Test selection group '':
+// CHECK-NEXT: 100-100
+// CHECK-NEXT: 153-153
+// CHECK-NEXT: 192-192
+// CHECK-NEXT: Test selection group 'named':
+// CHECK-NEXT: 127-127
+// CHECK-NEXT: 213-213
+
+// The following invocations are in the default group:
+
+// CHECK: invoking action 'local-rename':
+// CHECK-NEXT: -selection={{.*}}tool-test-support.c:3:11
+
+// CHECK: invoking action 'local-rename':
+// CHECK-NEXT: -selection={{.*}}tool-test-support.c:7:15
+
+// CHECK: invoking action 'local-rename':
+// CHECK-NEXT: -selection={{.*}}tool-test-support.c:9:29
+
+
+// The following invocations are in the 'named' group, and they follow
+// the default invocation even if some of their ranges occur prior to the
+// ranges from the default group because the groups are tested one-by-one:
+
+// CHECK: invoking action 'local-rename':
+// CHECK-NEXT: -selection={{.*}}tool-test-support.c:5:17
+
+// CHECK: invoking action 'local-rename':
+// CHECK-NEXT: -selection={{.*}}tool-test-support.c:11:20
Removed: cfe/trunk/test/clang-rename/Field.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/clang-rename/Field.cpp?rev=313243&view=auto
==============================================================================
--- cfe/trunk/test/clang-rename/Field.cpp (original)
+++ cfe/trunk/test/clang-rename/Field.cpp (removed)
@@ -1,15 +0,0 @@
-class Baz {
- int Foo; /* Test 1 */ // CHECK: int Bar;
-public:
- Baz();
-};
-
-Baz::Baz() : Foo(0) /* Test 2 */ {} // CHECK: Baz::Baz() : Bar(0)
-
-// Test 1.
-// RUN: clang-rename -offset=18 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
-// Test 2.
-// RUN: clang-rename -offset=89 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
-
-// To find offsets after modifying the file, use:
-// grep -Ubo 'Foo.*' <file>
Modified: cfe/trunk/tools/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/CMakeLists.txt?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/tools/CMakeLists.txt (original)
+++ cfe/trunk/tools/CMakeLists.txt Thu Sep 14 03:06:52 2017
@@ -12,6 +12,7 @@ add_clang_subdirectory(clang-offload-bun
add_clang_subdirectory(c-index-test)
add_clang_subdirectory(clang-rename)
+add_clang_subdirectory(clang-refactor)
if(CLANG_ENABLE_ARCMT)
add_clang_subdirectory(arcmt-test)
Added: cfe/trunk/tools/clang-refactor/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-refactor/CMakeLists.txt?rev=313244&view=auto
==============================================================================
--- cfe/trunk/tools/clang-refactor/CMakeLists.txt (added)
+++ cfe/trunk/tools/clang-refactor/CMakeLists.txt Thu Sep 14 03:06:52 2017
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+add_clang_executable(clang-refactor
+ ClangRefactor.cpp
+ TestSupport.cpp
+ )
+
+target_link_libraries(clang-refactor
+ clangBasic
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
+
+install(TARGETS clang-refactor RUNTIME DESTINATION bin)
Added: cfe/trunk/tools/clang-refactor/ClangRefactor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-refactor/ClangRefactor.cpp?rev=313244&view=auto
==============================================================================
--- cfe/trunk/tools/clang-refactor/ClangRefactor.cpp (added)
+++ cfe/trunk/tools/clang-refactor/ClangRefactor.cpp Thu Sep 14 03:06:52 2017
@@ -0,0 +1,391 @@
+//===--- ClangRefactor.cpp - Clang-based refactoring tool -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements a clang-refactor tool that performs various
+/// source transformations.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TestSupport.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/RefactoringAction.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace clang;
+using namespace tooling;
+using namespace refactor;
+namespace cl = llvm::cl;
+
+namespace opts {
+
+static cl::OptionCategory CommonRefactorOptions("Common refactoring options");
+
+static cl::opt<bool>
+ NoDatabases("no-dbs",
+ cl::desc("Ignore external databases including Clang's "
+ "compilation database and indexer stores"),
+ cl::cat(CommonRefactorOptions), cl::sub(*cl::AllSubCommands));
+
+static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"),
+ cl::cat(CommonRefactorOptions),
+ cl::sub(*cl::AllSubCommands));
+} // end namespace opts
+
+namespace {
+
+/// Stores the parsed `-selection` argument.
+class SourceSelectionArgument {
+public:
+ virtual ~SourceSelectionArgument() {}
+
+ /// Parse the `-selection` argument.
+ ///
+ /// \returns A valid argument when the parse succedeed, null otherwise.
+ static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value);
+
+ /// Prints any additional state associated with the selection argument to
+ /// the given output stream.
+ virtual void print(raw_ostream &OS) = 0;
+
+ /// Returns a replacement refactoring result consumer (if any) that should
+ /// consume the results of a refactoring operation.
+ ///
+ /// The replacement refactoring result consumer is used by \c
+ /// TestSourceSelectionArgument to inject a test-specific result handling
+ /// logic into the refactoring operation. The test-specific consumer
+ /// ensures that the individual results in a particular test group are
+ /// identical.
+ virtual std::unique_ptr<RefactoringResultConsumer> createCustomConsumer() {
+ return nullptr;
+ }
+
+ /// Runs the give refactoring function for each specified selection.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ virtual bool
+ forAllRanges(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange R)> Callback) = 0;
+};
+
+/// Stores the parsed -selection=test:<filename> option.
+class TestSourceSelectionArgument final : public SourceSelectionArgument {
+public:
+ TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)
+ : TestSelections(std::move(TestSelections)) {}
+
+ void print(raw_ostream &OS) override { TestSelections.dump(OS); }
+
+ std::unique_ptr<RefactoringResultConsumer> createCustomConsumer() override {
+ return TestSelections.createConsumer();
+ }
+
+ /// Testing support: invokes the selection action for each selection range in
+ /// the test file.
+ bool forAllRanges(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange R)> Callback) override {
+ return TestSelections.foreachRange(SM, Callback);
+ }
+
+private:
+ TestSelectionRangesInFile TestSelections;
+};
+
+std::unique_ptr<SourceSelectionArgument>
+SourceSelectionArgument::fromString(StringRef Value) {
+ if (Value.startswith("test:")) {
+ StringRef Filename = Value.drop_front(strlen("test:"));
+ Optional<TestSelectionRangesInFile> ParsedTestSelection =
+ findTestSelectionRanges(Filename);
+ if (!ParsedTestSelection)
+ return nullptr; // A parsing error was already reported.
+ return llvm::make_unique<TestSourceSelectionArgument>(
+ std::move(*ParsedTestSelection));
+ }
+ // FIXME: Support true selection ranges.
+ llvm::errs() << "error: '-selection' option must be specified using "
+ "<file>:<line>:<column> or "
+ "<file>:<line>:<column>-<line>:<column> format";
+ return nullptr;
+}
+
+/// A subcommand that corresponds to individual refactoring action.
+class RefactoringActionSubcommand : public cl::SubCommand {
+public:
+ RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,
+ RefactoringActionRules ActionRules,
+ cl::OptionCategory &Category)
+ : SubCommand(Action->getCommand(), Action->getDescription()),
+ Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
+ Sources = llvm::make_unique<cl::list<std::string>>(
+ cl::Positional, cl::ZeroOrMore, cl::desc("<source0> [... <sourceN>]"),
+ cl::cat(Category), cl::sub(*this));
+
+ // Check if the selection option is supported.
+ bool HasSelection = false;
+ for (const auto &Rule : this->ActionRules) {
+ if ((HasSelection = Rule->hasSelectionRequirement()))
+ break;
+ }
+ if (HasSelection) {
+ Selection = llvm::make_unique<cl::opt<std::string>>(
+ "selection",
+ cl::desc("The selected source range in which the refactoring should "
+ "be initiated (<file>:<line>:<column>-<line>:<column> or "
+ "<file>:<line>:<column>)"),
+ cl::cat(Category), cl::sub(*this));
+ }
+ }
+
+ ~RefactoringActionSubcommand() { unregisterSubCommand(); }
+
+ const RefactoringActionRules &getActionRules() const { return ActionRules; }
+
+ /// Parses the command-line arguments that are specific to this rule.
+ ///
+ /// \returns true on error, false otherwise.
+ bool parseArguments() {
+ if (Selection) {
+ ParsedSelection = SourceSelectionArgument::fromString(*Selection);
+ if (!ParsedSelection)
+ return true;
+ }
+ return false;
+ }
+
+ SourceSelectionArgument *getSelection() const {
+ assert(Selection && "selection not supported!");
+ return ParsedSelection.get();
+ }
+
+ ArrayRef<std::string> getSources() const { return *Sources; }
+
+private:
+ std::unique_ptr<RefactoringAction> Action;
+ RefactoringActionRules ActionRules;
+ std::unique_ptr<cl::list<std::string>> Sources;
+ std::unique_ptr<cl::opt<std::string>> Selection;
+ std::unique_ptr<SourceSelectionArgument> ParsedSelection;
+};
+
+class ClangRefactorConsumer : public RefactoringResultConsumer {
+public:
+ void handleError(llvm::Error Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ }
+
+ // FIXME: Consume atomic changes and apply them to files.
+};
+
+class ClangRefactorTool {
+public:
+ std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands;
+
+ ClangRefactorTool() {
+ std::vector<std::unique_ptr<RefactoringAction>> Actions =
+ createRefactoringActions();
+
+ // Actions must have unique command names so that we can map them to one
+ // subcommand.
+ llvm::StringSet<> CommandNames;
+ for (const auto &Action : Actions) {
+ if (!CommandNames.insert(Action->getCommand()).second) {
+ llvm::errs() << "duplicate refactoring action command '"
+ << Action->getCommand() << "'!";
+ exit(1);
+ }
+ }
+
+ // Create subcommands and command-line options.
+ for (auto &Action : Actions) {
+ SubCommands.push_back(llvm::make_unique<RefactoringActionSubcommand>(
+ std::move(Action), Action->createActiveActionRules(),
+ opts::CommonRefactorOptions));
+ }
+ }
+
+ using TUCallbackType = llvm::function_ref<void(ASTContext &)>;
+
+ /// Parses the translation units that were given to the subcommand using
+ /// the 'sources' option and invokes the callback for each parsed
+ /// translation unit.
+ bool foreachTranslationUnit(RefactoringActionSubcommand &Subcommand,
+ TUCallbackType Callback) {
+ std::unique_ptr<CompilationDatabase> Compilations;
+ if (opts::NoDatabases) {
+ // FIXME (Alex L): Support compilation options.
+ Compilations =
+ llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
+ ".", std::vector<std::string>());
+ } else {
+ // FIXME (Alex L): Support compilation database.
+ llvm::errs() << "compilation databases are not supported yet!\n";
+ return true;
+ }
+
+ class ToolASTConsumer : public ASTConsumer {
+ public:
+ TUCallbackType Callback;
+ ToolASTConsumer(TUCallbackType Callback) : Callback(Callback) {}
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ Callback(Context);
+ }
+ };
+ class ActionWrapper {
+ public:
+ TUCallbackType Callback;
+ ActionWrapper(TUCallbackType Callback) : Callback(Callback) {}
+
+ std::unique_ptr<ASTConsumer> newASTConsumer() {
+ return llvm::make_unique<ToolASTConsumer>(std::move(Callback));
+ }
+ };
+
+ ClangTool Tool(*Compilations, Subcommand.getSources());
+ ActionWrapper ToolAction(std::move(Callback));
+ std::unique_ptr<tooling::FrontendActionFactory> Factory =
+ tooling::newFrontendActionFactory(&ToolAction);
+ return Tool.run(Factory.get());
+ }
+
+ /// Logs an individual refactoring action invocation to STDOUT.
+ void logInvocation(RefactoringActionSubcommand &Subcommand,
+ const RefactoringRuleContext &Context) {
+ if (!opts::Verbose)
+ return;
+ llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
+ if (Context.getSelectionRange().isValid()) {
+ SourceRange R = Context.getSelectionRange();
+ llvm::outs() << " -selection=";
+ R.getBegin().print(llvm::outs(), Context.getSources());
+ llvm::outs() << " -> ";
+ R.getEnd().print(llvm::outs(), Context.getSources());
+ llvm::outs() << "\n";
+ }
+ }
+
+ bool invokeAction(RefactoringActionSubcommand &Subcommand) {
+ // Find a set of matching rules.
+ SmallVector<RefactoringActionRule *, 4> MatchingRules;
+ llvm::StringSet<> MissingOptions;
+
+ bool HasSelection = false;
+ for (const auto &Rule : Subcommand.getActionRules()) {
+ if (Rule->hasSelectionRequirement()) {
+ HasSelection = true;
+ if (Subcommand.getSelection())
+ MatchingRules.push_back(Rule.get());
+ else
+ MissingOptions.insert("selection");
+ }
+ // FIXME (Alex L): Support custom options.
+ }
+ if (MatchingRules.empty()) {
+ llvm::errs() << "error: '" << Subcommand.getName()
+ << "' can't be invoked with the given arguments:\n";
+ for (const auto &Opt : MissingOptions)
+ llvm::errs() << " missing '-" << Opt.getKey() << "' option\n";
+ return true;
+ }
+
+ bool HasFailed = false;
+ ClangRefactorConsumer Consumer;
+ if (foreachTranslationUnit(Subcommand, [&](ASTContext &AST) {
+ RefactoringRuleContext Context(AST.getSourceManager());
+ Context.setASTContext(AST);
+
+ auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
+ logInvocation(Subcommand, Context);
+ for (RefactoringActionRule *Rule : MatchingRules) {
+ if (!Rule->hasSelectionRequirement())
+ continue;
+ Rule->invoke(Consumer, Context);
+ return;
+ }
+ // FIXME (Alex L): If more than one initiation succeeded, then the
+ // rules are ambiguous.
+ llvm_unreachable(
+ "The action must have at least one selection rule");
+ };
+
+ if (HasSelection) {
+ assert(Subcommand.getSelection() && "Missing selection argument?");
+ if (opts::Verbose)
+ Subcommand.getSelection()->print(llvm::outs());
+ auto CustomConsumer =
+ Subcommand.getSelection()->createCustomConsumer();
+ if (Subcommand.getSelection()->forAllRanges(
+ Context.getSources(), [&](SourceRange R) {
+ Context.setSelectionRange(R);
+ InvokeRule(CustomConsumer ? *CustomConsumer : Consumer);
+ }))
+ HasFailed = true;
+ return;
+ }
+ // FIXME (Alex L): Implement non-selection based invocation path.
+ }))
+ return true;
+ return HasFailed;
+ }
+};
+
+} // end anonymous namespace
+
+int main(int argc, const char **argv) {
+ ClangRefactorTool Tool;
+
+ // FIXME: Use LibTooling's CommonOptions parser when subcommands are supported
+ // by it.
+ cl::HideUnrelatedOptions(opts::CommonRefactorOptions);
+ cl::ParseCommandLineOptions(
+ argc, argv, "Clang-based refactoring tool for C, C++ and Objective-C");
+ cl::PrintOptionValues();
+
+ // Figure out which action is specified by the user. The user must specify
+ // the action using a command-line subcommand, e.g. the invocation
+ // `clang-refactor local-rename` corresponds to the `LocalRename` refactoring
+ // action. All subcommands must have a unique names. This allows us to figure
+ // out which refactoring action should be invoked by looking at the first
+ // subcommand that's enabled by LLVM's command-line parser.
+ auto It = llvm::find_if(
+ Tool.SubCommands,
+ [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
+ return !!(*SubCommand);
+ });
+ if (It == Tool.SubCommands.end()) {
+ llvm::errs() << "error: no refactoring action given\n";
+ llvm::errs() << "note: the following actions are supported:\n";
+ for (const auto &Subcommand : Tool.SubCommands)
+ llvm::errs().indent(2) << Subcommand->getName() << "\n";
+ return 1;
+ }
+ RefactoringActionSubcommand &ActionCommand = **It;
+
+ ArrayRef<std::string> Sources = ActionCommand.getSources();
+ // When -no-dbs is used, at least one file (TU) must be given to any
+ // subcommand.
+ if (opts::NoDatabases && Sources.empty()) {
+ llvm::errs() << "error: must provide paths to the source files when "
+ "'-no-dbs' is used\n";
+ return 1;
+ }
+ if (ActionCommand.parseArguments())
+ return 1;
+ if (Tool.invokeAction(ActionCommand))
+ return 1;
+
+ return 0;
+}
Added: cfe/trunk/tools/clang-refactor/TestSupport.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-refactor/TestSupport.cpp?rev=313244&view=auto
==============================================================================
--- cfe/trunk/tools/clang-refactor/TestSupport.cpp (added)
+++ cfe/trunk/tools/clang-refactor/TestSupport.cpp Thu Sep 14 03:06:52 2017
@@ -0,0 +1,343 @@
+//===--- TestSupport.cpp - Clang-based refactoring tool -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements routines that provide refactoring testing
+/// utilities.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TestSupport.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace refactor {
+
+void TestSelectionRangesInFile::dump(raw_ostream &OS) const {
+ for (const auto &Group : GroupedRanges) {
+ OS << "Test selection group '" << Group.Name << "':\n";
+ for (const auto &Range : Group.Ranges) {
+ OS << " " << Range.Begin << "-" << Range.End << "\n";
+ }
+ }
+}
+
+bool TestSelectionRangesInFile::foreachRange(
+ const SourceManager &SM,
+ llvm::function_ref<void(SourceRange)> Callback) const {
+ const FileEntry *FE = SM.getFileManager().getFile(Filename);
+ FileID FID = FE ? SM.translateFile(FE) : FileID();
+ if (!FE || FID.isInvalid()) {
+ llvm::errs() << "error: -selection=test:" << Filename
+ << " : given file is not in the target TU";
+ return true;
+ }
+ SourceLocation FileLoc = SM.getLocForStartOfFile(FID);
+ for (const auto &Group : GroupedRanges) {
+ for (const TestSelectionRange &Range : Group.Ranges) {
+ // Translate the offset pair to a true source range.
+ SourceLocation Start =
+ SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Range.Begin));
+ SourceLocation End =
+ SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Range.End));
+ assert(Start.isValid() && End.isValid() && "unexpected invalid range");
+ Callback(SourceRange(Start, End));
+ }
+ }
+ return false;
+}
+
+namespace {
+
+void dumpChanges(const tooling::AtomicChanges &Changes, raw_ostream &OS) {
+ for (const auto &Change : Changes)
+ OS << const_cast<tooling::AtomicChange &>(Change).toYAMLString() << "\n";
+}
+
+bool areChangesSame(const tooling::AtomicChanges &LHS,
+ const tooling::AtomicChanges &RHS) {
+ if (LHS.size() != RHS.size())
+ return false;
+ for (const auto &I : llvm::zip(LHS, RHS)) {
+ if (!(std::get<0>(I) == std::get<1>(I)))
+ return false;
+ }
+ return true;
+}
+
+bool printRewrittenSources(const tooling::AtomicChanges &Changes,
+ raw_ostream &OS) {
+ std::set<std::string> Files;
+ for (const auto &Change : Changes)
+ Files.insert(Change.getFilePath());
+ tooling::ApplyChangesSpec Spec;
+ Spec.Cleanup = false;
+ for (const auto &File : Files) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
+ llvm::MemoryBuffer::getFile(File);
+ if (!BufferErr) {
+ llvm::errs() << "failed to open" << File << "\n";
+ return true;
+ }
+ auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
+ Changes, Spec);
+ if (!Result) {
+ llvm::errs() << toString(Result.takeError());
+ return true;
+ }
+ OS << *Result;
+ }
+ return false;
+}
+
+class TestRefactoringResultConsumer final
+ : public tooling::RefactoringResultConsumer {
+public:
+ TestRefactoringResultConsumer(const TestSelectionRangesInFile &TestRanges)
+ : TestRanges(TestRanges) {
+ Results.push_back({});
+ }
+
+ ~TestRefactoringResultConsumer() {
+ // Ensure all results are checked.
+ for (auto &Group : Results) {
+ for (auto &Result : Group) {
+ if (!Result) {
+ (void)llvm::toString(Result.takeError());
+ }
+ }
+ }
+ }
+
+ void handleError(llvm::Error Err) override { handleResult(std::move(Err)); }
+
+ void handle(tooling::AtomicChanges Changes) override {
+ handleResult(std::move(Changes));
+ }
+
+ void handle(tooling::SymbolOccurrences Occurrences) override {
+ tooling::RefactoringResultConsumer::handle(std::move(Occurrences));
+ }
+
+private:
+ bool handleAllResults();
+
+ void handleResult(Expected<tooling::AtomicChanges> Result) {
+ Results.back().push_back(std::move(Result));
+ size_t GroupIndex = Results.size() - 1;
+ if (Results.back().size() >=
+ TestRanges.GroupedRanges[GroupIndex].Ranges.size()) {
+ ++GroupIndex;
+ if (GroupIndex >= TestRanges.GroupedRanges.size()) {
+ if (handleAllResults())
+ exit(1); // error has occurred.
+ return;
+ }
+ Results.push_back({});
+ }
+ }
+
+ const TestSelectionRangesInFile &TestRanges;
+ std::vector<std::vector<Expected<tooling::AtomicChanges>>> Results;
+};
+
+std::pair<unsigned, unsigned> getLineColumn(StringRef Filename,
+ unsigned Offset) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile =
+ MemoryBuffer::getFile(Filename);
+ if (!ErrOrFile)
+ return {0, 0};
+ StringRef Source = ErrOrFile.get()->getBuffer();
+ Source = Source.take_front(Offset);
+ size_t LastLine = Source.find_last_of("\r\n");
+ return {Source.count('\n') + 1,
+ (LastLine == StringRef::npos ? Offset : Offset - LastLine) + 1};
+}
+
+} // end anonymous namespace
+
+bool TestRefactoringResultConsumer::handleAllResults() {
+ bool Failed = false;
+ for (auto &Group : llvm::enumerate(Results)) {
+ // All ranges in the group must produce the same result.
+ Optional<tooling::AtomicChanges> CanonicalResult;
+ Optional<std::string> CanonicalErrorMessage;
+ for (auto &I : llvm::enumerate(Group.value())) {
+ Expected<tooling::AtomicChanges> &Result = I.value();
+ std::string ErrorMessage;
+ bool HasResult = !!Result;
+ if (!HasResult) {
+ // FIXME: Handle diagnostic error as well.
+ handleAllErrors(Result.takeError(), [&](StringError &Err) {
+ ErrorMessage = Err.getMessage();
+ });
+ }
+ if (!CanonicalResult && !CanonicalErrorMessage) {
+ if (HasResult)
+ CanonicalResult = std::move(*Result);
+ else
+ CanonicalErrorMessage = std::move(ErrorMessage);
+ continue;
+ }
+
+ // Verify that this result corresponds to the canonical result.
+ if (CanonicalErrorMessage) {
+ // The error messages must match.
+ if (!HasResult && ErrorMessage == *CanonicalErrorMessage)
+ continue;
+ } else {
+ assert(CanonicalResult && "missing canonical result");
+ // The results must match.
+ if (HasResult && areChangesSame(*Result, *CanonicalResult))
+ continue;
+ }
+ Failed = true;
+ // Report the mismatch.
+ std::pair<unsigned, unsigned> LineColumn = getLineColumn(
+ TestRanges.Filename,
+ TestRanges.GroupedRanges[Group.index()].Ranges[I.index()].Begin);
+ llvm::errs()
+ << "error: unexpected refactoring result for range starting at "
+ << LineColumn.first << ':' << LineColumn.second << " in group '"
+ << TestRanges.GroupedRanges[Group.index()].Name << "':\n ";
+ if (HasResult)
+ llvm::errs() << "valid result";
+ else
+ llvm::errs() << "error '" << ErrorMessage << "'";
+ llvm::errs() << " does not match initial ";
+ if (CanonicalErrorMessage)
+ llvm::errs() << "error '" << *CanonicalErrorMessage << "'\n";
+ else
+ llvm::errs() << "valid result\n";
+ if (HasResult && !CanonicalErrorMessage) {
+ llvm::errs() << " Expected to Produce:\n";
+ dumpChanges(*CanonicalResult, llvm::errs());
+ llvm::errs() << " Produced:\n";
+ dumpChanges(*Result, llvm::errs());
+ }
+ }
+
+ // Dump the results:
+ const auto &TestGroup = TestRanges.GroupedRanges[Group.index()];
+ if (!CanonicalResult) {
+ llvm::errs() << TestGroup.Ranges.size() << " '" << TestGroup.Name
+ << "' results:\n";
+ llvm::errs() << *CanonicalErrorMessage << "\n";
+ } else {
+ llvm::outs() << TestGroup.Ranges.size() << " '" << TestGroup.Name
+ << "' results:\n";
+ if (printRewrittenSources(*CanonicalResult, llvm::outs()))
+ return true;
+ }
+ }
+ return Failed;
+}
+
+std::unique_ptr<tooling::RefactoringResultConsumer>
+TestSelectionRangesInFile::createConsumer() const {
+ return llvm::make_unique<TestRefactoringResultConsumer>(*this);
+}
+
+/// Adds the \p ColumnOffset to file offset \p Offset, without going past a
+/// newline.
+static unsigned addColumnOffset(StringRef Source, unsigned Offset,
+ unsigned ColumnOffset) {
+ if (!ColumnOffset)
+ return Offset;
+ StringRef Substr = Source.drop_front(Offset).take_front(ColumnOffset);
+ size_t NewlinePos = Substr.find_first_of("\r\n");
+ return Offset +
+ (NewlinePos == StringRef::npos ? ColumnOffset : (unsigned)NewlinePos);
+}
+
+Optional<TestSelectionRangesInFile>
+findTestSelectionRanges(StringRef Filename) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile =
+ MemoryBuffer::getFile(Filename);
+ if (!ErrOrFile) {
+ llvm::errs() << "error: -selection=test:" << Filename
+ << " : could not open the given file";
+ return None;
+ }
+ StringRef Source = ErrOrFile.get()->getBuffer();
+
+ // FIXME (Alex L): 3rd capture groups for +line:column.
+ // See the doc comment for this function for the explanation of this
+ // syntax.
+ static Regex RangeRegex("range[[:blank:]]*([[:alpha:]_]*)?[[:blank:]]*=[[:"
+ "blank:]]*(\\+[[:digit:]]+)?");
+
+ std::map<std::string, SmallVector<TestSelectionRange, 8>> GroupedRanges;
+
+ LangOptions LangOpts;
+ LangOpts.CPlusPlus = 1;
+ LangOpts.CPlusPlus11 = 1;
+ Lexer Lex(SourceLocation::getFromRawEncoding(0), LangOpts, Source.begin(),
+ Source.begin(), Source.end());
+ Lex.SetCommentRetentionState(true);
+ Token Tok;
+ for (Lex.LexFromRawLexer(Tok); Tok.isNot(tok::eof);
+ Lex.LexFromRawLexer(Tok)) {
+ if (Tok.isNot(tok::comment))
+ continue;
+ StringRef Comment =
+ Source.substr(Tok.getLocation().getRawEncoding(), Tok.getLength());
+ SmallVector<StringRef, 4> Matches;
+ // Allow CHECK: comments to contain range= commands.
+ if (!RangeRegex.match(Comment, &Matches) || Comment.contains("CHECK")) {
+ // Try to detect mistyped 'range:' comments to ensure tests don't miss
+ // anything.
+ if (Comment.contains_lower("range") && Comment.contains("=") &&
+ !Comment.contains_lower("run") && !Comment.contains("CHECK")) {
+ llvm::errs() << "error: suspicious comment '" << Comment
+ << "' that "
+ "resembles the range command found\n";
+ llvm::errs() << "note: please reword if this isn't a range command\n";
+ return None;
+ }
+ continue;
+ }
+ unsigned Offset = Tok.getEndLoc().getRawEncoding();
+ unsigned ColumnOffset = 0;
+ if (!Matches[2].empty()) {
+ // Don't forget to drop the '+'!
+ if (Matches[2].drop_front().getAsInteger(10, ColumnOffset))
+ assert(false && "regex should have produced a number");
+ }
+ // FIXME (Alex L): Support true ranges.
+ Offset = addColumnOffset(Source, Offset, ColumnOffset);
+ TestSelectionRange Range = {Offset, Offset};
+ auto It = GroupedRanges.insert(std::make_pair(
+ Matches[1].str(), SmallVector<TestSelectionRange, 8>{Range}));
+ if (!It.second)
+ It.first->second.push_back(Range);
+ }
+ if (GroupedRanges.empty()) {
+ llvm::errs() << "error: -selection=test:" << Filename
+ << ": no 'range' commands";
+ return None;
+ }
+
+ TestSelectionRangesInFile TestRanges = {Filename.str(), {}};
+ for (auto &Group : GroupedRanges)
+ TestRanges.GroupedRanges.push_back({Group.first, std::move(Group.second)});
+ return std::move(TestRanges);
+}
+
+} // end namespace refactor
+} // end namespace clang
Added: cfe/trunk/tools/clang-refactor/TestSupport.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-refactor/TestSupport.h?rev=313244&view=auto
==============================================================================
--- cfe/trunk/tools/clang-refactor/TestSupport.h (added)
+++ cfe/trunk/tools/clang-refactor/TestSupport.h Thu Sep 14 03:06:52 2017
@@ -0,0 +1,107 @@
+//===--- TestSupport.h - Clang-based refactoring tool -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Declares datatypes and routines that are used by test-specific code
+/// in clang-refactor.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H
+#define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <string>
+
+namespace clang {
+
+class SourceManager;
+
+namespace refactor {
+
+/// A source selection range that's specified in a test file using an inline
+/// command in the comment. These commands can take the following forms:
+///
+/// - /*range=*/ will create an empty selection range in the default group
+/// right after the comment.
+/// - /*range a=*/ will create an empty selection range in the 'a' group right
+/// after the comment.
+/// - /*range = +1*/ will create an empty selection range at a location that's
+/// right after the comment with one offset to the column.
+/// - /*range= -> +2:3*/ will create a selection range that starts at the
+/// location right after the comment, and ends at column 3 of the 2nd line
+/// after the line of the starting location.
+///
+/// Clang-refactor will expected all ranges in one test group to produce
+/// identical results.
+struct TestSelectionRange {
+ unsigned Begin, End;
+};
+
+/// A set of test selection ranges specified in one file.
+struct TestSelectionRangesInFile {
+ std::string Filename;
+ struct RangeGroup {
+ std::string Name;
+ SmallVector<TestSelectionRange, 8> Ranges;
+ };
+ std::vector<RangeGroup> GroupedRanges;
+
+ TestSelectionRangesInFile(TestSelectionRangesInFile &&) = default;
+ TestSelectionRangesInFile &operator=(TestSelectionRangesInFile &&) = default;
+
+ bool foreachRange(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange)> Callback) const;
+
+ std::unique_ptr<tooling::RefactoringResultConsumer> createConsumer() const;
+
+ void dump(llvm::raw_ostream &OS) const;
+};
+
+/// Extracts the grouped selection ranges from the file that's specified in
+/// the -selection=test:<filename> option.
+///
+/// The grouped ranges are specified in comments using the following syntax:
+/// "range" [ group-name ] "=" [ "+" starting-column-offset ] [ "->"
+/// "+" ending-line-offset ":"
+/// ending-column-position ]
+///
+/// The selection range is then computed from this command by taking the ending
+/// location of the comment, and adding 'starting-column-offset' to the column
+/// for that location. That location in turns becomes the whole selection range,
+/// unless 'ending-line-offset' and 'ending-column-position' are specified. If
+/// they are specified, then the ending location of the selection range is
+/// the starting location's line + 'ending-line-offset' and the
+/// 'ending-column-position' column.
+///
+/// All selection ranges in one group are expected to produce the same
+/// refactoring result.
+///
+/// When testing, zero is returned from clang-refactor even when a group
+/// produces an initiation error, which is different from normal invocation
+/// that returns a non-zero value. This is done on purpose, to ensure that group
+/// consistency checks can return non-zero, but still print the output of
+/// the group. So even if a test matches the output of group, it will still fail
+/// because clang-refactor should return zero on exit when the group results are
+/// consistent.
+///
+/// \returns None on failure (errors are emitted to stderr), or a set of
+/// grouped source ranges in the given file otherwise.
+Optional<TestSelectionRangesInFile> findTestSelectionRanges(StringRef Filename);
+
+} // end namespace refactor
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H
Modified: cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp?rev=313244&r1=313243&r2=313244&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp Thu Sep 14 03:06:52 2017
@@ -57,7 +57,8 @@ createReplacements(const std::unique_ptr
TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
auto ReplaceAWithB =
- [](std::pair<selection::SourceSelectionRange, int> Selection)
+ [](const RefactoringRuleContext &,
+ std::pair<selection::SourceSelectionRange, int> Selection)
-> Expected<AtomicChanges> {
const SourceManager &SM = Selection.first.getSources();
SourceLocation Loc = Selection.first.getRange().getBegin().getLocWithOffset(
@@ -71,7 +72,8 @@ TEST_F(RefactoringActionRulesTest, MyFir
class SelectionRequirement : public selection::Requirement {
public:
std::pair<selection::SourceSelectionRange, int>
- evaluateSelection(selection::SourceSelectionRange Selection) const {
+ evaluateSelection(const RefactoringRuleContext &,
+ selection::SourceSelectionRange Selection) const {
return std::make_pair(Selection, 20);
}
};
@@ -127,8 +129,10 @@ TEST_F(RefactoringActionRulesTest, MyFir
}
TEST_F(RefactoringActionRulesTest, ReturnError) {
- Expected<AtomicChanges> (*Func)(selection::SourceSelectionRange) =
- [](selection::SourceSelectionRange) -> Expected<AtomicChanges> {
+ Expected<AtomicChanges> (*Func)(const RefactoringRuleContext &,
+ selection::SourceSelectionRange) =
+ [](const RefactoringRuleContext &,
+ selection::SourceSelectionRange) -> Expected<AtomicChanges> {
return llvm::make_error<llvm::StringError>(
"Error", llvm::make_error_code(llvm::errc::invalid_argument));
};
@@ -155,13 +159,14 @@ TEST_F(RefactoringActionRulesTest, Retur
class SelectionRequirement : public selection::Requirement {
public:
Expected<Optional<int>>
- evaluateSelection(selection::SourceSelectionRange Selection) const {
+ evaluateSelection(const RefactoringRuleContext &,
+ selection::SourceSelectionRange Selection) const {
return llvm::make_error<llvm::StringError>(
"bad selection", llvm::make_error_code(llvm::errc::invalid_argument));
}
};
auto Rule = createRefactoringRule(
- [](int) -> Expected<AtomicChanges> {
+ [](const RefactoringRuleContext &, int) -> Expected<AtomicChanges> {
llvm::report_fatal_error("Should not run!");
},
requiredSelection(SelectionRequirement()));
@@ -201,7 +206,8 @@ Optional<SymbolOccurrences> findOccurren
TEST_F(RefactoringActionRulesTest, ReturnSymbolOccurrences) {
auto Rule = createRefactoringRule(
- [](selection::SourceSelectionRange Selection)
+ [](const RefactoringRuleContext &,
+ selection::SourceSelectionRange Selection)
-> Expected<SymbolOccurrences> {
SymbolOccurrences Occurrences;
Occurrences.push_back(SymbolOccurrence(
More information about the cfe-commits
mailing list