arphaman created this revision.
Herald added a subscriber: mgorny.

Symbol occurrences store the results of local rename and will also be used for 
the global, indexed rename results. They can be converted to a set of 
`AtomicChanges` as well. This is a preparation patch for both the support of 
multi-piece local-renames (ObjC selectors) and the migration of clang-rename 
over to clang-refactor.
This is a non functional change, and I just wanted to make sure the general 
direction of this patch is accepted before committing.


Repository:
  rL LLVM

https://reviews.llvm.org/D36156

Files:
  include/clang/Tooling/Refactoring/Rename/RenamingAction.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
@@ -48,8 +48,8 @@
                                    const ASTContext &Context)
       : RecursiveSymbolVisitor(Context.getSourceManager(),
                                Context.getLangOpts()),
-        USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
-  }
+        USRSet(USRs.begin(), USRs.end()), PrevName(PrevName),
+        Occurrences(PrevName), Context(Context) {}
 
   bool visitSymbolOccurrence(const NamedDecl *ND,
                              ArrayRef<SourceRange> NameRanges) {
@@ -68,11 +68,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 &getOccurrences() { return Occurrences; }
 
 private:
   void checkAndAddLocation(SourceLocation Loc) {
@@ -87,12 +85,13 @@
     // 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(SymbolOccurrence::MatchingSymbol,
+                               BeginLoc.getLocWithOffset(Offset));
   }
 
   const std::set<std::string> USRSet;
   const std::string PrevName;
-  std::vector<clang::SourceLocation> LocationsFound;
+  SymbolOccurrences Occurrences;
   const ASTContext &Context;
 };
 
@@ -391,12 +390,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 std::move(Visitor.getOccurrences());
 }
 
 std::vector<tooling::AtomicChange>
Index: lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
@@ -0,0 +1,22 @@
+//===--- 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/AtomicChange.h"
+
+using namespace clang;
+using namespace tooling;
+
+void SymbolOccurrences::emplace_back(SymbolOccurrence::OccurrenceKind Kind,
+                                     ArrayRef<SourceLocation> Locations) {
+  unsigned Offset = this->Locations.size();
+  Occurrences.push_back({Kind, Offset, (unsigned)Locations.size()});
+  for (const auto &Loc : Locations)
+    this->Locations.push_back(Loc);
+}
Index: lib/Tooling/Refactoring/Rename/RenamingAction.cpp
===================================================================
--- lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -32,6 +32,42 @@
 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<SourceLocation> Locs = Occurrence.getNameLocations();
+    assert(NewNameStrings.size() == Locs.size() &&
+           "Mismatching number of locations and name pieces");
+    AtomicChange Change(SM, Locs[0]);
+    for (size_t I = 0, E = Locs.size(); I != E; ++I) {
+      auto Error = Change.replace(SM, Locs[I], Occurrence.getNameLengths()[I],
+                                  NewNameStrings[I]);
+      if (Error)
+        return std::move(Error);
+    }
+    Changes.push_back(std::move(Change));
+  }
+  return Changes;
+}
+
+static void
+applyChanges(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 +88,28 @@
                        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.getNameLocations()[0], 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;
+    }
+    applyChanges(*Change, FileToReplaces);
   }
 
 private:
@@ -103,15 +138,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";
-          }
-        }
-      }
+      applyChanges(AtomicChanges, FileToReplaces);
     }
   }
 
Index: lib/Tooling/Refactoring/CMakeLists.txt
===================================================================
--- lib/Tooling/Refactoring/CMakeLists.txt
+++ lib/Tooling/Refactoring/CMakeLists.txt
@@ -6,6 +6,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,148 @@
+//===--- 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 {
+
+/// 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 to
+/// account for things like Objective-C selectors. As well as that, occurrences
+/// for multi-string names might have just one location if they correspond to
+/// a macro expansion.
+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 fixup their code manually after performing such a
+    /// rename.
+    /// FIXME: The rename verifier should notify user about this issue.
+    MatchingSymbol
+  };
+
+  SymbolOccurrence(OccurrenceKind Kind, ArrayRef<SourceLocation> Locations,
+                   ArrayRef<unsigned> Lengths)
+      : Kind(Kind), Locations(Locations), NameLengths(Lengths.data()) {
+    assert(Locations.size() == Lengths.size() &&
+           "mismatching number of locations and lengths");
+  }
+
+  OccurrenceKind getKind() const { return Kind; }
+
+  ArrayRef<SourceLocation> getNameLocations() const { return Locations; }
+  ArrayRef<unsigned> getNameLengths() const {
+    return llvm::makeArrayRef(NameLengths, Locations.size());
+  }
+
+private:
+  OccurrenceKind Kind;
+  ArrayRef<SourceLocation> Locations;
+  const unsigned *NameLengths;
+};
+
+/// A set of source symbol occurrences for one symbol in a single translation
+/// unit.
+class SymbolOccurrences {
+public:
+  /// Creates a set of empty symbol occurrences for the symbol of a given name.
+  SymbolOccurrences(StringRef SymbolName) {
+    // 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(!SymbolName.empty() && "Invalid symbol name!");
+    NamePieceSize.push_back(SymbolName.size());
+  }
+
+  SymbolOccurrences(SymbolOccurrences &&) = default;
+  SymbolOccurrences &operator=(SymbolOccurrences &&) = default;
+
+  void emplace_back(SymbolOccurrence::OccurrenceKind Kind,
+                    ArrayRef<SourceLocation> Locations);
+
+  size_t size() const { return Occurrences.size(); }
+  bool empty() const { return Occurrences.empty(); }
+
+private:
+  /// Iterates over the symbol occurrences.
+  class Iterator
+      : public llvm::iterator_facade_base<Iterator, std::forward_iterator_tag,
+                                          SymbolOccurrence> {
+  public:
+    Iterator(const SymbolOccurrences *Owner, size_t Index)
+        : Owner(Owner), Index(Index) {}
+
+    bool operator==(const Iterator &Other) const {
+      return Index == Other.Index;
+    }
+
+    SymbolOccurrence operator*() const {
+      const OccurrenceData &Data = Owner->Occurrences[Index];
+      ArrayRef<SourceLocation> Locs =
+          llvm::makeArrayRef(Owner->Locations)
+              .slice(Data.LocationsOffset, Data.NumLocations);
+      return SymbolOccurrence(Data.Kind, Locs, Owner->NamePieceSize);
+    }
+
+    Iterator &operator++() {
+      ++Index;
+      return *this;
+    }
+
+  private:
+    const SymbolOccurrences *Owner;
+    size_t Index;
+  };
+
+  typedef Iterator const_iterator;
+
+public:
+  const_iterator begin() const { return Iterator(this, 0); }
+  const_iterator end() const { return Iterator(this, Occurrences.size()); }
+
+private:
+  struct OccurrenceData {
+    SymbolOccurrence::OccurrenceKind Kind;
+    unsigned LocationsOffset, NumLocations;
+  };
+  SmallVector<unsigned, 1> NamePieceSize;
+  std::vector<OccurrenceData> Occurrences;
+  std::vector<SourceLocation> Locations;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_SYMBOL_OCCURRENCES_H
Index: include/clang/Tooling/Refactoring/Rename/RenamingAction.h
===================================================================
--- include/clang/Tooling/Refactoring/Rename/RenamingAction.h
+++ include/clang/Tooling/Refactoring/Rename/RenamingAction.h
@@ -16,13 +16,17 @@
 #define LLVM_CLANG_TOOLING_REFACTOR_RENAME_RENAMING_ACTION_H
 
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "llvm/Support/Error.h"
 
 namespace clang {
 class ASTConsumer;
 class CompilerInstance;
 
 namespace tooling {
 
+class SymbolOccurrences;
+
 class RenamingAction {
 public:
   RenamingAction(const std::vector<std::string> &NewNames,
@@ -42,6 +46,13 @@
   bool PrintLocations;
 };
 
+/// Returns source replacements that correspond to the rename of the given
+/// symbol occurrences.
+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