Revert rL215947: "[clang-rename] revert r215839"
authorManuel Klimek <klimek@google.com>
Wed, 20 Aug 2014 01:39:05 +0000 (01:39 +0000)
committerManuel Klimek <klimek@google.com>
Wed, 20 Aug 2014 01:39:05 +0000 (01:39 +0000)
Make tests not depend on grep supporting -bo.

llvm-svn: 216041

19 files changed:
clang-tools-extra/CMakeLists.txt
clang-tools-extra/Makefile
clang-tools-extra/clang-rename/CMakeLists.txt [new file with mode: 0644]
clang-tools-extra/clang-rename/ClangRename.cpp [new file with mode: 0644]
clang-tools-extra/clang-rename/Makefile [new file with mode: 0644]
clang-tools-extra/clang-rename/RenamingAction.cpp [new file with mode: 0644]
clang-tools-extra/clang-rename/RenamingAction.h [new file with mode: 0644]
clang-tools-extra/clang-rename/USRFinder.cpp [new file with mode: 0644]
clang-tools-extra/clang-rename/USRFinder.h [new file with mode: 0644]
clang-tools-extra/clang-rename/USRFindingAction.cpp [new file with mode: 0644]
clang-tools-extra/clang-rename/USRFindingAction.h [new file with mode: 0644]
clang-tools-extra/clang-rename/USRLocFinder.cpp [new file with mode: 0644]
clang-tools-extra/clang-rename/USRLocFinder.h [new file with mode: 0644]
clang-tools-extra/test/CMakeLists.txt
clang-tools-extra/test/clang-rename/VarTest.cpp [new file with mode: 0644]
clang-tools-extra/unittests/CMakeLists.txt
clang-tools-extra/unittests/clang-rename/CMakeLists.txt [new file with mode: 0644]
clang-tools-extra/unittests/clang-rename/Makefile [new file with mode: 0644]
clang-tools-extra/unittests/clang-rename/USRLocFindingTest.cpp [new file with mode: 0644]

index 1f2132f..0c3a1e1 100644 (file)
@@ -1,5 +1,6 @@
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-modernize)
+add_subdirectory(clang-rename)
 add_subdirectory(modularize)
 add_subdirectory(module-map-checker)
 add_subdirectory(remove-cstr-calls)
index 49bcfc8..aa0830e 100644 (file)
@@ -13,8 +13,8 @@ include $(CLANG_LEVEL)/../../Makefile.config
 
 PARALLEL_DIRS := remove-cstr-calls tool-template modularize \
  module-map-checker pp-trace
-DIRS := clang-apply-replacements clang-modernize clang-tidy clang-query \
-       unittests
+DIRS := clang-apply-replacements clang-modernize clang-rename clang-tidy \
+       clang-query unittests
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/clang-tools-extra/clang-rename/CMakeLists.txt b/clang-tools-extra/clang-rename/CMakeLists.txt
new file mode 100644 (file)
index 0000000..69962a7
--- /dev/null
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_executable(clang-rename
+  ClangRename.cpp
+  USRFinder.cpp
+  USRFindingAction.cpp
+  USRLocFinder.cpp
+  RenamingAction.cpp
+  )
+
+target_link_libraries(clang-rename
+  clangAST
+  clangBasic
+  clangFrontend
+  clangIndex
+  clangRewrite
+  clangTooling
+  )
+
+install(TARGETS clang-rename RUNTIME DESTINATION bin)
diff --git a/clang-tools-extra/clang-rename/ClangRename.cpp b/clang-tools-extra/clang-rename/ClangRename.cpp
new file mode 100644 (file)
index 0000000..077c591
--- /dev/null
@@ -0,0 +1,151 @@
+//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements a clang-rename tool that automatically finds and
+/// renames symbols in C++ code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRFindingAction.h"
+#include "RenamingAction.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/CommandLineSourceLoc.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/Host.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+cl::OptionCategory ClangRenameCategory("Clang-rename options");
+
+static cl::opt<std::string>
+NewName(
+    "new-name",
+    cl::desc("The new name to change the symbol to."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<unsigned>
+SymbolOffset(
+    "offset",
+    cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<bool>
+Inplace(
+    "i",
+    cl::desc("Overwrite edited <file>s."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<bool>
+PrintName(
+    "pn",
+    cl::desc("Print the found symbol's name prior to renaming to stderr."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<bool>
+PrintLocations(
+    "pl",
+    cl::desc("Print the locations affected by renaming to stderr."),
+    cl::cat(ClangRenameCategory));
+
+#define CLANG_RENAME_VERSION "0.0.1"
+
+static void PrintVersion() {
+  outs() << "clang-rename version " << CLANG_RENAME_VERSION << "\n";
+}
+
+using namespace clang;
+
+const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\
+clang-rename renames every occurrence of a symbol found at <offset> in\n\
+<source0>. If -i is specified, the edited files are overwritten to disk.\n\
+Otherwise, the results are written to stdout.\n";
+
+int main(int argc, const char **argv) {
+  cl::SetVersionPrinter(PrintVersion);
+  tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
+
+  // Check the arguments for correctness.
+
+  if (NewName.empty()) {
+    errs() << "clang-rename: no new name provided.\n\n";
+    cl::PrintHelpMessage();
+    exit(1);
+  }
+
+  // Get the USRs.
+  auto Files = OP.getSourcePathList();
+  tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+  rename::USRFindingAction USRAction(SymbolOffset);
+
+  // Find the USRs.
+  Tool.run(tooling::newFrontendActionFactory(&USRAction).get());
+  const auto &USRs = USRAction.getUSRs();
+  const auto &PrevName = USRAction.getUSRSpelling();
+
+  if (PrevName.empty())
+    // An error should have already been printed.
+    exit(1);
+
+  if (PrintName)
+    errs() << "clang-rename: found name: " << PrevName;
+
+  // Perform the renaming.
+  rename::RenamingAction RenameAction(NewName, PrevName, USRs,
+                                      Tool.getReplacements(), PrintLocations);
+  auto Factory = tooling::newFrontendActionFactory(&RenameAction);
+  int res;
+
+  if (Inplace) {
+    res = Tool.runAndSave(Factory.get());
+  } else {
+    res = Tool.run(Factory.get());
+
+    // Write every file to stdout. Right now we just barf the files without any
+    // indication of which files start where, other than that we print the files
+    // in the same order we see them.
+    LangOptions DefaultLangOptions;
+    IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
+        new DiagnosticOptions();
+    TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+    DiagnosticsEngine Diagnostics(
+        IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
+        &*DiagOpts, &DiagnosticPrinter, false);
+    auto &FileMgr = Tool.getFiles();
+    SourceManager Sources(Diagnostics, FileMgr);
+    Rewriter Rewrite(Sources, DefaultLangOptions);
+
+    Tool.applyAllReplacements(Rewrite);
+    for (const auto &File : Files) {
+      const auto *Entry = FileMgr.getFile(File);
+      auto ID = Sources.translateFile(Entry);
+      Rewrite.getEditBuffer(ID).write(outs());
+    }
+  }
+
+  exit(res);
+}
diff --git a/clang-tools-extra/clang-rename/Makefile b/clang-tools-extra/clang-rename/Makefile
new file mode 100644 (file)
index 0000000..8efcc45
--- /dev/null
@@ -0,0 +1,20 @@
+##===- tools/extra/clang-rename/Makefile -------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../../..
+TOOLNAME = clang-rename
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
+USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \
+           clangTooling.a clangParse.a clangSema.a clangIndex.a \
+           clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \
+           clangStaticAnalyzerCore.a clangAnalysis.a clangRewriteFrontend.a \
+           clangRewrite.a clangEdit.a clangAST.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
diff --git a/clang-tools-extra/clang-rename/RenamingAction.cpp b/clang-tools-extra/clang-rename/RenamingAction.cpp
new file mode 100644 (file)
index 0000000..61a4e80
--- /dev/null
@@ -0,0 +1,90 @@
+//===--- tools/extra/clang-rename/RenamingAction.cpp - Clang rename tool --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RenamingAction.h"
+#include "USRLocFinder.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+class RenamingASTConsumer : public ASTConsumer {
+public:
+  RenamingASTConsumer(const std::string &NewName,
+                      const std::string &PrevName,
+                      const std::vector<std::string> &USRs,
+                      tooling::Replacements &Replaces,
+                      bool PrintLocations)
+      : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
+        PrintLocations(PrintLocations) {
+  }
+
+  void HandleTranslationUnit(ASTContext &Context) override {
+    const auto &SourceMgr = Context.getSourceManager();
+    std::vector<SourceLocation> RenamingCandidates;
+    std::vector<SourceLocation> NewCandidates;
+
+    for (const auto &USR : USRs) {
+      NewCandidates = getLocationsOfUSR(USR, Context.getTranslationUnitDecl());
+      RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
+                                NewCandidates.end());
+      NewCandidates.clear();
+    }
+
+    auto PrevNameLen = PrevName.length();
+    if (PrintLocations)
+      for (const auto &Loc : RenamingCandidates) {
+        FullSourceLoc FullLoc(Loc, SourceMgr);
+        errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
+               << ":" << FullLoc.getSpellingLineNumber() << ":"
+               << FullLoc.getSpellingColumnNumber() << "\n";
+        Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
+                                             NewName));
+      }
+    else
+      for (const auto &Loc : RenamingCandidates)
+        Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
+                                             NewName));
+  }
+
+private:
+  const std::string &NewName, &PrevName;
+  const std::vector<std::string> &USRs;
+  tooling::Replacements &Replaces;
+  bool PrintLocations;
+};
+
+std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
+  return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
+                                                Replaces, PrintLocations);
+}
+
+}
+}
diff --git a/clang-tools-extra/clang-rename/RenamingAction.h b/clang-tools-extra/clang-rename/RenamingAction.h
new file mode 100644 (file)
index 0000000..d52f21d
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- tools/extra/clang-rename/RenamingAction.h - Clang rename tool ----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
+
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+
+namespace rename {
+
+class RenamingAction {
+public:
+  RenamingAction(const std::string &NewName, const std::string &PrevName,
+                 const std::vector<std::string> &USRs,
+                 tooling::Replacements &Replaces, bool PrintLocations = false)
+      : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
+        PrintLocations(PrintLocations) {
+  }
+
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+
+private:
+  const std::string &NewName, &PrevName;
+  const std::vector<std::string> &USRs;
+  tooling::Replacements &Replaces;
+  bool PrintLocations;
+};
+
+}
+}
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
diff --git a/clang-tools-extra/clang-rename/USRFinder.cpp b/clang-tools-extra/clang-rename/USRFinder.cpp
new file mode 100644 (file)
index 0000000..16a3cce
--- /dev/null
@@ -0,0 +1,162 @@
+//===--- tools/extra/clang-rename/USRFinder.cpp - Clang rename tool -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
+/// point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRFinder.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+// NamedDeclFindingASTVisitor recursively visits each AST node to find the
+// symbol underneath the cursor.
+// FIXME: move to seperate .h/.cc file if this gets too large.
+namespace {
+class NamedDeclFindingASTVisitor
+    : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
+public:
+  // \brief Finds the NamedDecl at a point in the source.
+  // \param Point the location in the source to search for the NamedDecl.
+  explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
+                                      const SourceLocation Point)
+      : Result(nullptr), SourceMgr(SourceMgr),
+        Point(Point) {
+  }
+
+  // Declaration visitors:
+
+  // \brief Checks if the point falls within the NameDecl. This covers every
+  // declaration of a named entity that we may come across. Usually, just
+  // checking if the point lies within the length of the name of the declaration
+  // and the start location is sufficient.
+  bool VisitNamedDecl(const NamedDecl *Decl) {
+    return setResult(Decl, Decl->getLocation(),
+                     Decl->getNameAsString().length());
+  }
+
+  // Expression visitors:
+
+  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+    // Check the namespace specifier first.
+    if (!checkNestedNameSpecifierLoc(Expr->getQualifierLoc()))
+      return false;
+
+    const auto *Decl = Expr->getFoundDecl();
+    return setResult(Decl, Expr->getLocation(),
+                     Decl->getNameAsString().length());
+  }
+
+  bool VisitMemberExpr(const MemberExpr *Expr) {
+    const auto *Decl = Expr->getFoundDecl().getDecl();
+    return setResult(Decl, Expr->getMemberLoc(),
+                     Decl->getNameAsString().length());
+  }
+
+  // Other:
+
+  const NamedDecl *getNamedDecl() {
+    return Result;
+  }
+
+private:
+  // \brief Determines if a namespace qualifier contains the point.
+  // \returns false on success and sets Result.
+  bool checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+    while (NameLoc) {
+      const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
+      if (Decl && !setResult(Decl, NameLoc.getLocalBeginLoc(),
+                             Decl->getNameAsString().length()))
+        return false;
+      NameLoc = NameLoc.getPrefix();
+    }
+    return true;
+  }
+
+  // \brief Sets Result to Decl if the Point is within Start and End.
+  // \returns false on success.
+  bool setResult(const NamedDecl *Decl, SourceLocation Start,
+                 SourceLocation End) {
+    if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
+        !End.isFileID() || !isPointWithin(Start, End)) {
+      return true;
+    }
+    Result = Decl;
+    return false;
+  }
+
+  // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
+  // \returns false on success.
+  bool setResult(const NamedDecl *Decl, SourceLocation Loc,
+                 unsigned Offset) {
+    // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
+    return Offset == 0 ||
+           setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
+  }
+
+  // \brief Determines if the Point is within Start and End.
+  bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
+    // FIXME: Add tests for Point == End.
+    return Point == Start || Point == End ||
+           (SourceMgr.isBeforeInTranslationUnit(Start, Point) &&
+            SourceMgr.isBeforeInTranslationUnit(Point, End));
+  }
+
+  const NamedDecl *Result;
+  const SourceManager &SourceMgr;
+  const SourceLocation Point; // The location to find the NamedDecl.
+};
+}
+
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+                                const SourceLocation Point) {
+  const auto &SourceMgr = Context.getSourceManager();
+  const auto SearchFile = SourceMgr.getFilename(Point);
+
+  NamedDeclFindingASTVisitor Visitor(SourceMgr, Point);
+
+  // We only want to search the decls that exist in the same file as the point.
+  auto Decls = Context.getTranslationUnitDecl()->decls();
+  for (auto &CurrDecl : Decls) {
+    const auto FileLoc = CurrDecl->getLocStart();
+    const auto FileName = SourceMgr.getFilename(FileLoc);
+    // FIXME: Add test.
+    if (FileName == SearchFile) {
+      Visitor.TraverseDecl(CurrDecl);
+      if (const NamedDecl *Result = Visitor.getNamedDecl()) {
+        return Result;
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+std::string getUSRForDecl(const Decl *Decl) {
+  llvm::SmallVector<char, 128> Buff;
+
+  // FIXME: Add test for the nullptr case.
+  if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
+    return "";
+
+  return std::string(Buff.data(), Buff.size());
+}
+
+} // namespace clang
+} // namespace rename
diff --git a/clang-tools-extra/clang-rename/USRFinder.h b/clang-tools-extra/clang-rename/USRFinder.h
new file mode 100644 (file)
index 0000000..2de6f42
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- tools/extra/clang-rename/USRFinder.h - Clang rename tool ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for determining the USR of a symbol at a location in source
+/// code.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
+
+#include <string>
+
+namespace clang {
+class ASTContext;
+class Decl;
+class SourceLocation;
+class NamedDecl;
+
+namespace rename {
+
+// Given an AST context and a point, returns a NamedDecl identifying the symbol
+// at the point. Returns null if nothing is found at the point.
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+                                const SourceLocation Point);
+
+// Converts a Decl into a USR.
+std::string getUSRForDecl(const Decl *Decl);
+
+}
+}
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
diff --git a/clang-tools-extra/clang-rename/USRFindingAction.cpp b/clang-tools-extra/clang-rename/USRFindingAction.cpp
new file mode 100644 (file)
index 0000000..74352fd
--- /dev/null
@@ -0,0 +1,118 @@
+//===--- tools/extra/clang-rename/USRFindingAction.cpp - Clang rename tool ===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRFindingAction.h"
+#include "USRFinder.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+// Get the USRs for the constructors of the class.
+static std::vector<std::string> getAllConstructorUSRs(
+    const CXXRecordDecl *Decl) {
+  std::vector<std::string> USRs;
+
+  // We need to get the definition of the record (as opposed to any forward
+  // declarations) in order to find the constructor and destructor.
+  const auto *RecordDecl = Decl->getDefinition();
+
+  // Iterate over all the constructors and add their USRs.
+  for (const auto &CtorDecl : RecordDecl->ctors())
+    USRs.push_back(getUSRForDecl(CtorDecl));
+
+  // Ignore destructors. GetLocationsOfUSR will find the declaration of and
+  // explicit calls to a destructor through TagTypeLoc (and it is better for the
+  // purpose of renaming).
+  //
+  // For example, in the following code segment,
+  //  1  class C {
+  //  2    ~C();
+  //  3  };
+  // At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc starting
+  // from 'C'.
+
+  return USRs;
+}
+
+struct NamedDeclFindingConsumer : public ASTConsumer {
+  void HandleTranslationUnit(ASTContext &Context) override {
+    const auto &SourceMgr = Context.getSourceManager();
+    // The file we look for the USR in will always be the main source file.
+    const auto Point = SourceMgr.getLocForStartOfFile(
+        SourceMgr.getMainFileID()).getLocWithOffset(SymbolOffset);
+    if (!Point.isValid())
+      return;
+    const NamedDecl *FoundDecl = getNamedDeclAt(Context, Point);
+    if (FoundDecl == nullptr) {
+      FullSourceLoc FullLoc(Point, SourceMgr);
+      errs() << "clang-rename: could not find symbol at "
+             << SourceMgr.getFilename(Point) << ":"
+             << FullLoc.getSpellingLineNumber() << ":"
+             << FullLoc.getSpellingColumnNumber() << " (offset " << SymbolOffset
+             << ").\n";
+      return;
+    }
+
+    // If the decl is a constructor or destructor, we want to instead take the
+    // decl of the parent record.
+    if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
+      FoundDecl = CtorDecl->getParent();
+    else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
+      FoundDecl = DtorDecl->getParent();
+
+    // If the decl is in any way relatedpp to a class, we want to make sure we
+    // search for the constructor and destructor as well as everything else.
+    if (const auto *Record = dyn_cast<CXXRecordDecl>(FoundDecl))
+      *USRs = getAllConstructorUSRs(Record);
+
+    USRs->push_back(getUSRForDecl(FoundDecl));
+    *SpellingName = FoundDecl->getNameAsString();
+  }
+
+  unsigned SymbolOffset;
+  std::string *SpellingName;
+  std::vector<std::string> *USRs;
+};
+
+std::unique_ptr<ASTConsumer>
+USRFindingAction::newASTConsumer() {
+  std::unique_ptr<NamedDeclFindingConsumer> Consumer(
+      new NamedDeclFindingConsumer);
+  SpellingName = "";
+  Consumer->SymbolOffset = SymbolOffset;
+  Consumer->USRs = &USRs;
+  Consumer->SpellingName = &SpellingName;
+  return std::move(Consumer);
+}
+
+} // namespace rename
+} // namespace clang
diff --git a/clang-tools-extra/clang-rename/USRFindingAction.h b/clang-tools-extra/clang-rename/USRFindingAction.h
new file mode 100644 (file)
index 0000000..4c059a5
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- tools/extra/clang-rename/USRFindingAction.h - Clang rename tool --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to find all relevent USRs at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
+
+#include "clang/Frontend/FrontendAction.h"
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+class NamedDecl;
+
+namespace rename {
+
+struct USRFindingAction {
+  USRFindingAction(unsigned Offset) : SymbolOffset(Offset) {
+  }
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+
+  // \brief get the spelling of the USR(s) as it would appear in source files.
+  const std::string &getUSRSpelling() {
+    return SpellingName;
+  }
+
+  const std::vector<std::string> &getUSRs() {
+    return USRs;
+  }
+
+private:
+  unsigned SymbolOffset;
+  std::string SpellingName;
+  std::vector<std::string> USRs;
+};
+
+}
+}
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
diff --git a/clang-tools-extra/clang-rename/USRLocFinder.cpp b/clang-tools-extra/clang-rename/USRLocFinder.cpp
new file mode 100644 (file)
index 0000000..fc8dfca
--- /dev/null
@@ -0,0 +1,103 @@
+//===--- tools/extra/clang-rename/USRLocFinder.cpp - Clang rename tool ----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Mehtods for finding all instances of a USR. Our strategy is very
+/// simple; we just compare the USR at every relevant AST node with the one
+/// provided.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRLocFinder.h"
+#include "USRFinder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+namespace {
+// \brief This visitor recursively searches for all instances of a USR in a
+// translation unit and stores them for later usage.
+class USRLocFindingASTVisitor
+    : public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
+public:
+  explicit USRLocFindingASTVisitor(const std::string USR) : USR(USR) {
+  }
+
+  // Declaration visitors:
+
+  bool VisitNamedDecl(const NamedDecl *Decl) {
+    if (getUSRForDecl(Decl) == USR) {
+      LocationsFound.push_back(Decl->getLocation());
+    }
+    return true;
+  }
+
+  // Expression visitors:
+
+  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+    const auto *Decl = Expr->getFoundDecl();
+
+    checkNestedNameSpecifierLoc(Expr->getQualifierLoc());
+    if (getUSRForDecl(Decl) == USR) {
+      LocationsFound.push_back(Expr->getLocation());
+    }
+
+    return true;
+  }
+
+  bool VisitMemberExpr(const MemberExpr *Expr) {
+    const auto *Decl = Expr->getFoundDecl().getDecl();
+    if (getUSRForDecl(Decl) == USR) {
+      LocationsFound.push_back(Expr->getMemberLoc());
+    }
+    return true;
+  }
+
+  // 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;
+  }
+
+private:
+  // Namespace traversal:
+  void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+    while (NameLoc) {
+      const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
+      if (Decl && getUSRForDecl(Decl) == USR)
+        LocationsFound.push_back(NameLoc.getLocalBeginLoc());
+      NameLoc = NameLoc.getPrefix();
+    }
+  }
+
+  // All the locations of the USR were found.
+  const std::string USR;
+  std::vector<clang::SourceLocation> LocationsFound;
+};
+} // namespace
+
+std::vector<SourceLocation> getLocationsOfUSR(const std::string USR,
+                                              Decl *Decl) {
+  USRLocFindingASTVisitor visitor(USR);
+
+  visitor.TraverseDecl(Decl);
+  return visitor.getLocationsFound();
+}
+
+} // namespace rename
+} // namespace clang
diff --git a/clang-tools-extra/clang-rename/USRLocFinder.h b/clang-tools-extra/clang-rename/USRLocFinder.h
new file mode 100644 (file)
index 0000000..7ed0e0f
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- tools/extra/clang-rename/USRLocFinder.h - Clang rename tool ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides functionality for finding all instances of a USR in a given
+/// AST.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
+
+#include <string>
+#include <vector>
+
+namespace clang {
+
+class Decl;
+class SourceLocation;
+
+namespace rename {
+
+// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
+std::vector<SourceLocation> getLocationsOfUSR(const std::string usr,
+                                              Decl *decl);
+}
+}
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
index 6a98cdd..253f302 100644 (file)
@@ -37,6 +37,7 @@ set(CLANG_TOOLS_TEST_DEPS
   # Individual tools we test.
   clang-apply-replacements
   clang-modernize
+  clang-rename
   clang-query
   clang-tidy
   modularize
diff --git a/clang-tools-extra/test/clang-rename/VarTest.cpp b/clang-tools-extra/test/clang-rename/VarTest.cpp
new file mode 100644 (file)
index 0000000..a038544
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=170 -new-name=hector %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+// REQUIRES: shell
+namespace A {
+int foo;  // CHECK: int hector;
+}
+int foo;  // CHECK: int foo;
+int bar = foo; // CHECK: bar = foo;
+int baz = A::foo; // CHECK: baz = A::hector;
+void fun1() {
+  struct {
+    int foo; // CHECK: int foo;
+  } b = { 100 };
+  int foo = 100; // CHECK: int foo
+  baz = foo; // CHECK: baz = foo;
+  {
+    extern int foo; // CHECK: int foo;
+    baz = foo; // CHECK: baz = foo;
+    foo = A::foo + baz; // CHECK: foo = A::hector + baz;
+    A::foo = b.foo; // CHECK: A::hector = b.foo;
+  }
+  foo = b.foo; // CHECK: foo = b.foo;
+}
+// Use grep -FUbo 'foo;' <file> to get the correct offset of foo when changing
+// this file.
index 34d7ecc..10bfc5e 100644 (file)
@@ -7,5 +7,6 @@ endfunction()
 
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-modernize)
+add_subdirectory(clang-rename)
 add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
diff --git a/clang-tools-extra/unittests/clang-rename/CMakeLists.txt b/clang-tools-extra/unittests/clang-rename/CMakeLists.txt
new file mode 100644 (file)
index 0000000..dca9a80
--- /dev/null
@@ -0,0 +1,33 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANG_RENAME_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-rename REALPATH)
+include_directories(
+  ${CLANG_RENAME_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangRenameTests
+  USRLocFindingTest.cpp
+  ${CLANG_RENAME_SOURCE_DIR}/USRFinder.cpp
+  ${CLANG_RENAME_SOURCE_DIR}/USRFindingAction.cpp
+  )
+
+target_link_libraries(ClangRenameTests
+  clangAnalysis
+  clangAST
+  clangBasic
+  clangDriver
+  clangEdit
+  clangFrontend
+  clangFrontendTool
+  clangIndex
+  clangLex
+  clangParse
+  clangRewrite
+  clangRewriteFrontend
+  clangSerialization
+  clangSema
+  clangTooling
+  )
\ No newline at end of file
diff --git a/clang-tools-extra/unittests/clang-rename/Makefile b/clang-tools-extra/unittests/clang-rename/Makefile
new file mode 100644 (file)
index 0000000..c86d132
--- /dev/null
@@ -0,0 +1,24 @@
+##===- unittests/clang-rename/Makefile ---------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../../../..
+include $(CLANG_LEVEL)/../../Makefile.config
+
+TESTNAME = ClangRenameTests
+LINK_COMPONENTS := asmparser bitreader support MC MCParser option \
+                TransformUtils
+USEDLIBS = clangAnalysis.a clangAST.a clangBasic.a clangDriver.a clangEdit.a \
+          clangFrontend.a clangFrontendTool.a clangIndex.a clangLex.a \
+          clangParse.a clangRewrite.a clangRewriteFrontend.a \
+          clangSerialization.a clangSema.a clangTooling.a
+
+include $(CLANG_LEVEL)/Makefile
+MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1
+CPP.Flags += -I(PROJ_SRC_DIR)/../../clang-rename
+include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/clang-tools-extra/unittests/clang-rename/USRLocFindingTest.cpp b/clang-tools-extra/unittests/clang-rename/USRLocFindingTest.cpp
new file mode 100644 (file)
index 0000000..4f497c6
--- /dev/null
@@ -0,0 +1,84 @@
+#include "USRFindingAction.h"
+#include "gtest/gtest.h"
+#include "clang/Tooling/Tooling.h"
+#include <stdio.h>
+#include <set>
+#include <map>
+#include <vector>
+
+namespace clang {
+namespace rename {
+namespace test {
+
+// Determines if the symbol group invariants hold. To recap, those invariants
+// are:
+//  (1) All symbols in the same symbol group share the same USR.
+//  (2) Two symbols from two different groups do not share the same USR.
+static void testOffsetGroups(const char *Code,
+                             const std::vector<std::vector<unsigned>> Groups) {
+  std::set<std::string> AllUSRs, CurrUSR;
+
+  for (const auto &Group : Groups) {
+    // Groups the invariants do not hold then the value of USR is also invalid,
+    // but at that point the test has already failed and USR ceases to be
+    // useful.
+    std::string USR;
+    for (const auto &Offset : Group) {
+      USRFindingAction Action(Offset);
+      auto Factory = tooling::newFrontendActionFactory(&Action);
+      EXPECT_TRUE(tooling::runToolOnCode(Factory->create(), Code));
+      const auto &USRs = Action.getUSRs();
+      EXPECT_EQ(1u, USRs.size());
+      USR = USRs[0];
+      CurrUSR.insert(USR);
+    }
+    EXPECT_EQ(1u, CurrUSR.size());
+    CurrUSR.clear();
+    AllUSRs.insert(USR);
+  }
+
+  EXPECT_EQ(Groups.size(), AllUSRs.size());
+}
+
+
+TEST(USRLocFinding, FindsVarUSR) {
+  const char VarTest[] = "\n\
+namespace A {\n\
+int foo;\n\
+}\n\
+int foo;\n\
+int bar = foo;\n\
+int baz = A::foo;\n\
+void fun1() {\n\
+  struct {\n\
+    int foo;\n\
+  } b = { 100 };\n\
+  int foo = 100;\n\
+  baz = foo;\n\
+  {\n\
+    extern int foo;\n\
+    baz = foo;\n\
+    foo = A::foo + baz;\n\
+    A::foo = b.foo;\n\
+  }\n\
+ foo = b.foo;\n\
+}\n";
+  std::vector<std::vector<unsigned>> VarTestOffsets(3);
+  VarTestOffsets[0].push_back(19);
+  VarTestOffsets[0].push_back(63);
+  VarTestOffsets[0].push_back(205);
+  VarTestOffsets[0].push_back(223);
+  VarTestOffsets[1].push_back(30);
+  VarTestOffsets[1].push_back(45);
+  VarTestOffsets[1].push_back(172);
+  VarTestOffsets[1].push_back(187);
+  VarTestOffsets[2].push_back(129);
+  VarTestOffsets[2].push_back(148);
+  VarTestOffsets[2].push_back(242);
+
+  testOffsetGroups(VarTest, VarTestOffsets);
+}
+
+}
+}
+}