Allow users to specify NULL like macros to be replaced
authorTareq A. Siraj <tareq.a.sriaj@intel.com>
Thu, 28 Mar 2013 16:06:59 +0000 (16:06 +0000)
committerTareq A. Siraj <tareq.a.sriaj@intel.com>
Thu, 28 Mar 2013 16:06:59 +0000 (16:06 +0000)
-use-nullptr only replaced macro named NULL and ignored any user defined
macros that behaved like NULL. This patch introduces -user-null-macros
command line option to let users specify their custom NULL like macros.

- Added a -user-null-macros command line option that takes a
  comma-separated list of user-defined macros to be replaced when using
  the -use-nullptr transform.
- Added documentation.
- Updated testcase to reflect current behavior.
- Whitespace fixes.

Reviewers: revane, klimek, gribozavr
llvm-svn: 178243

clang-tools-extra/cpp11-migrate/Transforms.h
clang-tools-extra/cpp11-migrate/UseNullptr/NullptrActions.cpp
clang-tools-extra/cpp11-migrate/UseNullptr/NullptrActions.h
clang-tools-extra/docs/UseNullptrTransform.rst
clang-tools-extra/docs/cpp11-migrate.rst
clang-tools-extra/test/cpp11-migrate/UseNullptr/macros.cpp

index 77fd746..e660ccf 100644 (file)
@@ -37,7 +37,7 @@ public:
   typedef TransformVec::const_iterator const_iterator;
 
 public:
+
   ~Transforms();
 
   /// \brief Create command line options using LLVM's command line library.
index f0ad468..79b8b5f 100644 (file)
@@ -28,6 +28,13 @@ using namespace clang;
 
 namespace {
 
+const char *NullMacroName = "NULL";
+
+static llvm::cl::opt<std::string> UserNullMacroNames(
+    "user-null-macros", llvm::cl::desc("Comma-separated list of user-defined "
+                                       "macro names that behave like NULL"),
+    llvm::cl::init(""));
+
 /// \brief Replaces the provided range with the text "nullptr", but only if
 /// the start and end location are both in main file.
 /// Returns true if and only if a replacement was made.
@@ -41,6 +48,25 @@ bool ReplaceWithNullptr(tooling::Replacements &Replace, SourceManager &SM,
     return false;
 }
 
+/// \brief Returns the name of the outermost macro.
+///
+/// Given
+/// \code
+/// #define MY_NULL NULL
+/// \endcode
+/// If \p Loc points to NULL, this function will return the name MY_NULL.
+llvm::StringRef GetOutermostMacroName(
+    SourceLocation Loc, const SourceManager &SM, const LangOptions &LO) {
+  assert(Loc.isMacroID());
+  SourceLocation OutermostMacroLoc;
+
+  while (Loc.isMacroID()) {
+    OutermostMacroLoc = Loc;
+    Loc = SM.getImmediateMacroCallerLoc(Loc);
+  }
+
+  return clang::Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
+}
 }
 
 /// \brief Looks for a sequences of 0 or more explicit casts with an implicit
@@ -101,6 +127,16 @@ private:
   Expr *FirstSubExpr;
 };
 
+NullptrFixer::NullptrFixer(clang::tooling::Replacements &Replace,
+                           unsigned &AcceptedChanges, RiskLevel)
+    : Replace(Replace), AcceptedChanges(AcceptedChanges) {
+  if (!UserNullMacroNames.empty()) {
+    llvm::StringRef S = UserNullMacroNames;
+    S.split(UserNullMacros, ",");
+  }
+  UserNullMacros.insert(UserNullMacros.begin(), llvm::StringRef(NullMacroName));
+}
+
 void NullptrFixer::run(const ast_matchers::MatchFinder::MatchResult &Result) {
   SourceManager &SM = *Result.SourceManager;
 
@@ -127,20 +163,19 @@ void NullptrFixer::run(const ast_matchers::MatchFinder::MatchResult &Result) {
       EndLoc = SM.getFileLoc(EndLoc);
     } else if (SM.isMacroBodyExpansion(StartLoc) &&
                SM.isMacroBodyExpansion(EndLoc)) {
-      llvm::StringRef ImmediateMacroName = clang::Lexer::getImmediateMacroName(
-          StartLoc, SM, Result.Context->getLangOpts());
-      if (ImmediateMacroName != "NULL")
-        return;
+      llvm::StringRef OutermostMacroName =
+          GetOutermostMacroName(StartLoc, SM, Result.Context->getLangOpts());
 
-      SourceLocation MacroCallerStartLoc =
-          SM.getImmediateMacroCallerLoc(StartLoc);
-      SourceLocation MacroCallerEndLoc = SM.getImmediateMacroCallerLoc(EndLoc);
+      // Check to see if the user wants to replace the macro being expanded.
+      bool ReplaceNullMacro =
+          std::find(UserNullMacros.begin(), UserNullMacros.end(),
+                    OutermostMacroName) != UserNullMacros.end();
 
-      if (MacroCallerStartLoc.isFileID() && MacroCallerEndLoc.isFileID()) {
-        StartLoc = SM.getFileLoc(StartLoc);
-        EndLoc = SM.getFileLoc(EndLoc);
-      } else
+      if (!ReplaceNullMacro)
         return;
+
+      StartLoc = SM.getFileLoc(StartLoc);
+      EndLoc = SM.getFileLoc(EndLoc);
     }
 
     AcceptedChanges +=
index b1086ee..a79cb30 100644 (file)
@@ -25,9 +25,7 @@ class NullptrFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
 public:
   NullptrFixer(clang::tooling::Replacements &Replace,
                unsigned &AcceptedChanges,
-               RiskLevel) :
-  Replace(Replace),
-  AcceptedChanges(AcceptedChanges) { }
+               RiskLevel);
 
   /// \brief Entry point to the callback called when matches are made.
   virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
@@ -35,6 +33,7 @@ public:
 private:
   clang::tooling::Replacements &Replace;
   unsigned &AcceptedChanges;
+  llvm::SmallVector<llvm::StringRef, 1> UserNullMacros;
 };
 
 #endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_NULLPTR_ACTIONS_H
index 300caed..bc6289c 100644 (file)
@@ -38,3 +38,45 @@ transforms to:
   int *ret_ptr() {
     return nullptr;
   }
+
+
+User defined macros
+===================
+
+By default this transform will only replace the ``NULL`` macro and will skip any
+user-defined macros that behaves like ``NULL``. The user can use the
+:option:`-user-null-macros` option to specify a comma-separated list of macro
+names that will be transformed along with ``NULL``.
+
+Example
+-------
+
+.. code-block:: c++
+
+  #define MY_NULL (void*)0
+  void assignment() {
+    void *p = MY_NULL;
+  }
+
+
+using the command-line
+
+.. code-block:: bash
+
+  cpp11-migrate -use-nullptr -user-null-macros=MY_NULL foo.cpp
+
+
+transforms to:
+
+.. code-block:: c++
+
+  #define MY_NULL NULL
+  void assignment() {
+    int *p = nullptr;
+  }
+
+
+Risk
+====
+
+:option:`-risk` has no effect in this transform.
index 9d11f12..6e787f2 100644 (file)
@@ -40,6 +40,12 @@ Command Line Options
   Makes use of the new C++11 keyword ``nullptr`` where possible.
   See :doc:`UseNullptrTransform`.
 
+.. option:: -user-null-macros=<string>
+
+  ``<string>`` is a comma-separated list of user-defined macros that behave like
+  the ``NULL`` macro. The :option:`-use-nullptr` transform will replace these
+  macros along with ``NULL``. See :doc:`UseNullptrTransform`.
+
 .. option:: -use-auto
 
   Replace the type specifier of variable declarations with the ``auto`` type
@@ -76,7 +82,7 @@ Command Line Options
   :ref:`transform documentation <transforms>` for details.
 
 .. option:: -final-syntax-check
+
   After applying the final transform to a file, parse the file to ensure the
   last transform did not introduce syntax errors. Syntax errors introduced by
   earlier transforms are already caught when subsequent transforms parse the
index 7b3c2f0..28de745 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
 // RUN: cpp11-migrate -use-nullptr %t.cpp -- -I %S
 // RUN: FileCheck -input-file=%t.cpp %s
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t2.cpp
+// RUN: cpp11-migrate -use-nullptr -user-null-macros=MY_NULL %t2.cpp -- -I %S
+// RUN: FileCheck -check-prefix=USER-SUPPLIED-NULL -input-file=%t2.cpp %s
 
 #define NULL 0
 // CHECK: #define NULL 0
@@ -56,10 +59,9 @@ void test_macro_expansion2() {
 
 void test_macro_expansion3() {
 #define MY_NULL NULL
-  // TODO: Eventually we should fix the transform to detect cases like this so
-  // that we can replace MY_NULL with nullptr.
   int *p = MY_NULL;
   // CHECK: int *p = MY_NULL;
+  // USER-SUPPLIED-NULL: int *p = nullptr;
 #undef MY_NULL
 }