#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
#include <string>
#include <vector>
*Occurrences, Context.getASTContext().getSourceManager(), Name);
}
+Expected<QualifiedRenameRule>
+QualifiedRenameRule::initiate(RefactoringRuleContext &Context,
+ std::string OldQualifiedName,
+ std::string NewQualifiedName) {
+ const NamedDecl *ND =
+ getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
+ if (!ND)
+ return llvm::make_error<llvm::StringError>("Could not find symbol " +
+ OldQualifiedName,
+ llvm::errc::invalid_argument);
+ return QualifiedRenameRule(ND, std::move(NewQualifiedName));
+}
+
+const RefactoringDescriptor &QualifiedRenameRule::describe() {
+ static const RefactoringDescriptor Descriptor = {
+ /*Name=*/"local-qualified-rename",
+ /*Title=*/"Qualified Rename",
+ /*Description=*/
+ R"(Finds and renames qualified symbols in code within a translation unit.
+It is used to move/rename a symbol to a new namespace/name:
+ * Supported symbols: classes, class members, functions, enums, and type alias.
+ * Renames all symbol occurrences from the old qualified name to the new
+ qualified name. All symbol references will be correctly qualified; For
+ symbol definitions, only name will be changed.
+For example, rename "A::Foo" to "B::Bar":
+ Old code:
+ namespace foo {
+ class A {};
+ }
+
+ namespace bar {
+ void f(foo::A a) {}
+ }
+
+ New code after rename:
+ namespace foo {
+ class B {};
+ }
+
+ namespace bar {
+ void f(B b) {}
+ })"
+ };
+ return Descriptor;
+}
+
+Expected<AtomicChanges>
+QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {
+ auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());
+ assert(!USRs.empty());
+ return tooling::createRenameAtomicChanges(
+ USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());
+}
+
Expected<std::vector<AtomicChange>>
createRenameReplacements(const SymbolOccurrences &Occurrences,
const SourceManager &SM, const SymbolName &NewName) {
RefactoringActionRules ActionRules,
cl::OptionCategory &Category)
: SubCommand(Action->getCommand(), Action->getDescription()),
- Action(std::move(Action)), ActionRules(std::move(ActionRules)),
- HasSelection(false) {
+ Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
// Check if the selection option is supported.
for (const auto &Rule : this->ActionRules) {
- if ((HasSelection = Rule->hasSelectionRequirement()))
+ if (Rule->hasSelectionRequirement()) {
+ 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));
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));
+ }
}
// Create the refactoring options.
for (const auto &Rule : this->ActionRules) {
const RefactoringActionRules &getActionRules() const { return ActionRules; }
- /// Parses the command-line arguments that are specific to this rule.
+ /// Parses the "-selection" command-line argument.
///
/// \returns true on error, false otherwise.
- bool parseArguments() {
+ bool parseSelectionArgument() {
if (Selection) {
ParsedSelection = SourceSelectionArgument::fromString(*Selection);
if (!ParsedSelection)
return false;
}
- // Whether the selection is supported by any rule in the subcommand.
- bool hasSelection() const { return HasSelection; }
-
SourceSelectionArgument *getSelection() const {
assert(Selection && "selection not supported!");
return ParsedSelection.get();
std::unique_ptr<cl::opt<std::string>> Selection;
std::unique_ptr<SourceSelectionArgument> ParsedSelection;
RefactoringActionCommandLineOptions Options;
- // Whether the selection is supported by any rule in the subcommand.
- bool HasSelection;
};
class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface {
// If the selection option is test specific, we use a test-specific
// consumer.
std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
- if (SelectedSubcommand->hasSelection())
+ bool HasSelection = MatchingRule->hasSelectionRequirement();
+ if (HasSelection)
TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
ClangRefactorToolConsumerInterface *ActiveConsumer =
TestConsumer ? TestConsumer.get() : Consumer.get();
ActiveConsumer->beginTU(AST);
- // FIXME (Alex L): Implement non-selection based invocation path.
- if (SelectedSubcommand->hasSelection()) {
+
+ auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
+ if (opts::Verbose)
+ logInvocation(*SelectedSubcommand, Context);
+ MatchingRule->invoke(*ActiveConsumer, Context);
+ };
+ if (HasSelection) {
assert(SelectedSubcommand->getSelection() &&
"Missing selection argument?");
if (opts::Verbose)
if (SelectedSubcommand->getSelection()->forAllRanges(
Context.getSources(), [&](SourceRange R) {
Context.setSelectionRange(R);
- if (opts::Verbose)
- logInvocation(*SelectedSubcommand, Context);
- MatchingRule->invoke(*ActiveConsumer, Context);
+ InvokeRule(*ActiveConsumer);
}))
HasFailed = true;
ActiveConsumer->endTU();
return;
}
+ InvokeRule(*ActiveConsumer);
ActiveConsumer->endTU();
}
}
llvm::Expected<RefactoringActionRule *>
- getMatchingRule(const RefactoringActionSubcommand &Subcommand) {
+ getMatchingRule(RefactoringActionSubcommand &Subcommand) {
SmallVector<RefactoringActionRule *, 4> MatchingRules;
llvm::StringSet<> MissingOptions;
for (const auto &Rule : Subcommand.getActionRules()) {
- bool SelectionMatches = true;
- if (Rule->hasSelectionRequirement()) {
- if (!Subcommand.getSelection()) {
- MissingOptions.insert("selection");
- SelectionMatches = false;
- }
- }
CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
Rule->visitRefactoringOptions(Visitor);
- if (SelectionMatches && Visitor.getMissingRequiredOptions().empty()) {
- MatchingRules.push_back(Rule.get());
- continue;
+ if (Visitor.getMissingRequiredOptions().empty()) {
+ if (!Rule->hasSelectionRequirement()) {
+ MatchingRules.push_back(Rule.get());
+ } else {
+ Subcommand.parseSelectionArgument();
+ if (Subcommand.getSelection()) {
+ MatchingRules.push_back(Rule.get());
+ } else {
+ MissingOptions.insert("selection");
+ }
+ }
}
for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
MissingOptions.insert(Opt->getName());
Error, llvm::inconvertibleErrorCode());
}
RefactoringActionSubcommand *Subcommand = &(**It);
- if (Subcommand->parseArguments())
- return llvm::make_error<llvm::StringError>(
- llvm::Twine("Failed to parse arguments for subcommand ") +
- Subcommand->getName(),
- llvm::inconvertibleErrorCode());
return Subcommand;
}