[rename] Introduce symbol occurrences

Symbol occurrences store the results of local rename and will also be used for
the global, indexed rename results. Their kind is used to determine whether they
should be renamed automatically or not. They can be converted to a set of
AtomicChanges as well.

Differential Revision: https://reviews.llvm.org/D36156

llvm-svn: 310853
diff --git a/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
index de6aba9..f4f4bef 100644
--- a/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -24,6 +24,7 @@
 #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>
 
@@ -32,6 +33,45 @@
 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);
     }
   }
 
diff --git a/clang/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp b/clang/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
new file mode 100644
index 0000000..ea64b2c
--- /dev/null
+++ b/clang/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()));
+  }
+}
diff --git a/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp b/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
index dc21a94..43b03ca 100644
--- a/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
+++ b/clang/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>