Allow users to specify NULL like macros to be replaced

-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
This commit is contained in:
Tareq A. Siraj 2013-03-28 16:06:59 +00:00
parent 0118635979
commit c2aa348dd0
6 changed files with 103 additions and 19 deletions

View 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.

View 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")
llvm::StringRef OutermostMacroName =
GetOutermostMacroName(StartLoc, SM, Result.Context->getLangOpts());
// 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 (!ReplaceNullMacro)
return;
SourceLocation MacroCallerStartLoc =
SM.getImmediateMacroCallerLoc(StartLoc);
SourceLocation MacroCallerEndLoc = SM.getImmediateMacroCallerLoc(EndLoc);
if (MacroCallerStartLoc.isFileID() && MacroCallerEndLoc.isFileID()) {
StartLoc = SM.getFileLoc(StartLoc);
EndLoc = SM.getFileLoc(EndLoc);
} else
return;
StartLoc = SM.getFileLoc(StartLoc);
EndLoc = SM.getFileLoc(EndLoc);
}
AcceptedChanges +=

View 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

View 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.

View 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

View 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
}