arphaman updated this revision to Diff 110964.
arphaman marked an inline comment as done.
arphaman added a comment.

Use `takeOccurrences`


Repository:
  rL LLVM

https://reviews.llvm.org/D36156

Files:
  include/clang/Tooling/Refactoring/Rename/RenamingAction.h
  include/clang/Tooling/Refactoring/Rename/SymbolName.h
  include/clang/Tooling/Refactoring/Rename/SymbolOccurrences.h
  include/clang/Tooling/Refactoring/Rename/USRLocFinder.h
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/Rename/RenamingAction.cpp
  lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
  lib/Tooling/Refactoring/Rename/USRLocFinder.cpp

Index: lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
===================================================================
--- lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
+++ lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
@@ -23,6 +23,7 @@
 #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"
@@ -68,11 +69,9 @@
 
   // 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) {
@@ -82,17 +81,18 @@
     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;
 };
 
@@ -391,12 +391,11 @@
 
 } // 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>
Index: lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
@@ -0,0 +1,37 @@
+//===--- 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()));
+  }
+}
Index: lib/Tooling/Refactoring/Rename/RenamingAction.cpp
===================================================================
--- lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -24,14 +24,54 @@
 #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>
 
 using namespace llvm;
 
 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(
@@ -52,29 +92,29 @@
                        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:
@@ -103,15 +143,7 @@
       // 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);
     }
   }
 
Index: lib/Tooling/Refactoring/CMakeLists.txt
===================================================================
--- lib/Tooling/Refactoring/CMakeLists.txt
+++ lib/Tooling/Refactoring/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangToolingRefactor
   AtomicChange.cpp
   Rename/RenamingAction.cpp
+  Rename/SymbolOccurrences.cpp
   Rename/USRFinder.cpp
   Rename/USRFindingAction.cpp
   Rename/USRLocFinder.cpp
Index: include/clang/Tooling/Refactoring/Rename/USRLocFinder.h
===================================================================
--- include/clang/Tooling/Refactoring/Rename/USRLocFinder.h
+++ include/clang/Tooling/Refactoring/Rename/USRLocFinder.h
@@ -19,6 +19,7 @@
 #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>
@@ -38,10 +39,13 @@
 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
Index: include/clang/Tooling/Refactoring/Rename/SymbolOccurrences.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/Rename/SymbolOccurrences.h
@@ -0,0 +1,91 @@
+//===--- 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
Index: include/clang/Tooling/Refactoring/Rename/SymbolName.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/Rename/SymbolName.h
@@ -0,0 +1,49 @@
+//===--- 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
Index: include/clang/Tooling/Refactoring/Rename/RenamingAction.h
===================================================================
--- include/clang/Tooling/Refactoring/Rename/RenamingAction.h
+++ include/clang/Tooling/Refactoring/Rename/RenamingAction.h
@@ -16,6 +16,9 @@
 #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;
@@ -42,6 +45,13 @@
   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:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to