#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_RENAMING_ACTION_H
#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "clang/Tooling/Refactoring/Rename/SymbolOccurrences.h"
+#include "llvm/Support/Error.h"
namespace clang {
class ASTConsumer;
bool PrintLocations;
};
+/// Returns source replacements that correspond to the rename of the given
+/// symbol occurrences.
+llvm::Expected<std::vector<AtomicChange>>
+createRenameReplacements(const SymbolOccurrences &Occurrences,
+ const SourceManager &SM,
+ ArrayRef<StringRef> NewNameStrings);
+
/// Rename all symbols identified by the given USRs.
class QualifiedRenamingAction {
public:
--- /dev/null
+//===--- SymbolName.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_RENAME_SYMBOL_NAME_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_SYMBOL_NAME_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tooling {
+
+/// A name of a symbol.
+///
+/// Symbol's name can be composed of multiple strings. For example, Objective-C
+/// methods can contain multiple argument lables:
+///
+/// \code
+/// - (void) myMethodNamePiece: (int)x anotherNamePieces:(int)y;
+/// // ^~ string 0 ~~~~~ ^~ string 1 ~~~~~
+/// \endcode
+class SymbolName {
+public:
+ SymbolName(StringRef Name) {
+ // While empty symbol names are valid (Objective-C selectors can have empty
+ // name pieces), occurrences Objective-C selectors are created using an
+ // array of strings instead of just one string.
+ assert(!Name.empty() && "Invalid symbol name!");
+ this->Name.push_back(Name.str());
+ }
+
+ ArrayRef<std::string> getNamePieces() const { return Name; }
+
+private:
+ llvm::SmallVector<std::string, 1> Name;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_SYMBOL_NAME_H
--- /dev/null
+//===--- SymbolOccurrences.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_RENAME_SYMBOL_OCCURRENCES_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_SYMBOL_OCCURRENCES_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+class SymbolName;
+
+/// An occurrence of a symbol in the source.
+///
+/// Occurrences can have difference kinds, that describe whether this occurrence
+/// is an exact semantic match, or whether this is a weaker textual match that's
+/// not guaranteed to represent the exact declaration.
+///
+/// A single occurrence of a symbol can span more than one source range. For
+/// example, Objective-C selectors can contain multiple argument labels:
+///
+/// \code
+/// [object selectorPiece1: ... selectorPiece2: ...];
+/// // ^~~ range 0 ~~ ^~~ range 1 ~~
+/// \endcode
+///
+/// We have to replace the text in both range 0 and range 1 when renaming the
+/// Objective-C method 'selectorPiece1:selectorPiece2'.
+class SymbolOccurrence {
+public:
+ enum OccurrenceKind {
+ /// This occurrence is an exact match and can be renamed automatically.
+ ///
+ /// Note:
+ /// Symbol occurrences in macro arguments that expand to different
+ /// declarations get marked as exact matches, and thus the renaming engine
+ /// will rename them e.g.:
+ ///
+ /// \code
+ /// #define MACRO(x) x + ns::x
+ /// int foo(int var) {
+ /// return MACRO(var); // var is renamed automatically here when
+ /// // either var or ns::var is renamed.
+ /// };
+ /// \endcode
+ ///
+ /// The user will have to fix their code manually after performing such a
+ /// rename.
+ /// FIXME: The rename verifier should notify user about this issue.
+ MatchingSymbol
+ };
+
+ SymbolOccurrence(const SymbolName &Name, OccurrenceKind Kind,
+ ArrayRef<SourceLocation> Locations);
+
+ SymbolOccurrence(SymbolOccurrence &&) = default;
+ SymbolOccurrence &operator=(SymbolOccurrence &&) = default;
+
+ OccurrenceKind getKind() const { return Kind; }
+
+ ArrayRef<SourceRange> getNameRanges() const {
+ if (MultipleRanges) {
+ return llvm::makeArrayRef(MultipleRanges.get(),
+ RangeOrNumRanges.getBegin().getRawEncoding());
+ }
+ return RangeOrNumRanges;
+ }
+
+private:
+ OccurrenceKind Kind;
+ std::unique_ptr<SourceRange[]> MultipleRanges;
+ SourceRange RangeOrNumRanges;
+};
+
+using SymbolOccurrences = std::vector<SymbolOccurrence>;
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_SYMBOL_OCCURRENCES_H
#include "clang/AST/AST.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "clang/Tooling/Refactoring/Rename/SymbolOccurrences.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <vector>
createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
llvm::StringRef NewName, Decl *TranslationUnitDecl);
-// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
-std::vector<SourceLocation>
-getLocationsOfUSRs(const std::vector<std::string> &USRs,
- llvm::StringRef PrevName, Decl *Decl);
+/// Finds the symbol occurrences for the symbol that's identified by the given
+/// USR set.
+///
+/// \return SymbolOccurrences that can be converted to AtomicChanges when
+/// renaming.
+SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs,
+ StringRef PrevName, Decl *Decl);
} // end namespace tooling
} // end namespace clang
add_clang_library(clangToolingRefactor
AtomicChange.cpp
Rename/RenamingAction.cpp
+ Rename/SymbolOccurrences.cpp
Rename/USRFinder.cpp
Rename/USRFindingAction.cpp
Rename/USRLocFinder.cpp
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
#include <string>
#include <vector>
namespace clang {
namespace tooling {
+Expected<std::vector<AtomicChange>>
+createRenameReplacements(const SymbolOccurrences &Occurrences,
+ const SourceManager &SM,
+ ArrayRef<StringRef> NewNameStrings) {
+ // FIXME: A true local rename can use just one AtomicChange.
+ std::vector<AtomicChange> Changes;
+ for (const auto &Occurrence : Occurrences) {
+ ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
+ assert(NewNameStrings.size() == Ranges.size() &&
+ "Mismatching number of ranges and name pieces");
+ AtomicChange Change(SM, Ranges[0].getBegin());
+ for (const auto &Range : llvm::enumerate(Ranges)) {
+ auto Error =
+ Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
+ NewNameStrings[Range.index()]);
+ if (Error)
+ return std::move(Error);
+ }
+ Changes.push_back(std::move(Change));
+ }
+ return Changes;
+}
+
+/// Takes each atomic change and inserts its replacements into the set of
+/// replacements that belong to the appropriate file.
+static void convertChangesToFileReplacements(
+ ArrayRef<AtomicChange> AtomicChanges,
+ std::map<std::string, tooling::Replacements> *FileToReplaces) {
+ for (const auto &AtomicChange : AtomicChanges) {
+ for (const auto &Replace : AtomicChange.getReplacements()) {
+ llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
+ if (Err) {
+ llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
+ << llvm::toString(std::move(Err)) << "\n";
+ }
+ }
+ }
+}
+
class RenamingASTConsumer : public ASTConsumer {
public:
RenamingASTConsumer(
const std::string &PrevName,
const std::vector<std::string> &USRs) {
const SourceManager &SourceMgr = Context.getSourceManager();
- std::vector<SourceLocation> RenamingCandidates;
- std::vector<SourceLocation> NewCandidates;
- NewCandidates = tooling::getLocationsOfUSRs(
+ SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
USRs, PrevName, Context.getTranslationUnitDecl());
- RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
- NewCandidates.end());
-
- unsigned PrevNameLen = PrevName.length();
- for (const auto &Loc : RenamingCandidates) {
- if (PrintLocations) {
- FullSourceLoc FullLoc(Loc, SourceMgr);
- errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
+ if (PrintLocations) {
+ for (const auto &Occurrence : Occurrences) {
+ FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
+ SourceMgr);
+ errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
<< ":" << FullLoc.getSpellingLineNumber() << ":"
<< FullLoc.getSpellingColumnNumber() << "\n";
}
- // FIXME: better error handling.
- tooling::Replacement Replace(SourceMgr, Loc, PrevNameLen, NewName);
- llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
- if (Err)
- llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
- << llvm::toString(std::move(Err)) << "\n";
}
+ // FIXME: Support multi-piece names.
+ // FIXME: better error handling (propagate error out).
+ StringRef NewNameRef = NewName;
+ Expected<std::vector<AtomicChange>> Change =
+ createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
+ if (!Change) {
+ llvm::errs() << "Failed to create renaming replacements for '" << PrevName
+ << "'! " << llvm::toString(Change.takeError()) << "\n";
+ return;
+ }
+ convertChangesToFileReplacements(*Change, &FileToReplaces);
}
private:
// ready.
auto AtomicChanges = tooling::createRenameAtomicChanges(
USRList[I], NewNames[I], Context.getTranslationUnitDecl());
- for (const auto AtomicChange : AtomicChanges) {
- for (const auto &Replace : AtomicChange.getReplacements()) {
- llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
- if (Err) {
- llvm::errs() << "Renaming failed in " << Replace.getFilePath()
- << "! " << llvm::toString(std::move(Err)) << "\n";
- }
- }
- }
+ convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
}
}
--- /dev/null
+//===--- SymbolOccurrences.cpp - Clang refactoring library ----------------===//
+//
+// 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/Rename/SymbolOccurrences.h"
+#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+using namespace tooling;
+
+SymbolOccurrence::SymbolOccurrence(const SymbolName &Name, OccurrenceKind Kind,
+ ArrayRef<SourceLocation> Locations)
+ : Kind(Kind) {
+ ArrayRef<std::string> NamePieces = Name.getNamePieces();
+ assert(Locations.size() == NamePieces.size() &&
+ "mismatching number of locations and lengths");
+ assert(!Locations.empty() && "no locations");
+ if (Locations.size() == 1) {
+ RangeOrNumRanges = SourceRange(
+ Locations[0], Locations[0].getLocWithOffset(NamePieces[0].size()));
+ return;
+ }
+ MultipleRanges = llvm::make_unique<SourceRange[]>(Locations.size());
+ RangeOrNumRanges.setBegin(
+ SourceLocation::getFromRawEncoding(Locations.size()));
+ for (const auto &Loc : llvm::enumerate(Locations)) {
+ MultipleRanges[Loc.index()] = SourceRange(
+ Loc.value(),
+ Loc.value().getLocWithOffset(NamePieces[Loc.index()].size()));
+ }
+}
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Core/Lookup.h"
#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
+#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
// Non-visitors:
- // \brief Returns a list of unique locations. Duplicate or overlapping
- // locations are erroneous and should be reported!
- const std::vector<clang::SourceLocation> &getLocationsFound() const {
- return LocationsFound;
- }
+ /// \brief Returns a set of unique symbol occurrences. Duplicate or
+ /// overlapping occurrences are erroneous and should be reported!
+ SymbolOccurrences takeOccurrences() { return std::move(Occurrences); }
private:
void checkAndAddLocation(SourceLocation Loc) {
StringRef TokenName =
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
Context.getSourceManager(), Context.getLangOpts());
- size_t Offset = TokenName.find(PrevName);
+ size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
// The token of the source location we find actually has the old
// name.
if (Offset != StringRef::npos)
- LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
+ Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol,
+ BeginLoc.getLocWithOffset(Offset));
}
const std::set<std::string> USRSet;
- const std::string PrevName;
- std::vector<clang::SourceLocation> LocationsFound;
+ const SymbolName PrevName;
+ SymbolOccurrences Occurrences;
const ASTContext &Context;
};
} // namespace
-std::vector<SourceLocation>
-getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
- Decl *Decl) {
+SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs,
+ StringRef PrevName, Decl *Decl) {
USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
Visitor.TraverseDecl(Decl);
- return Visitor.getLocationsFound();
+ return Visitor.takeOccurrences();
}
std::vector<tooling::AtomicChange>