forked from OSchip/llvm-project
Update clang-tidy documentation.
Summary: Update documentation of the modernize module with clang-modernize's documentation. Subscribers: cfe-commits, klimek, alexfh Differential Revision: http://reviews.llvm.org/D12961 llvm-svn: 247987
This commit is contained in:
parent
2d0f38c5fb
commit
b22084e113
|
@ -1,4 +1,253 @@
|
|||
modernize-loop-convert
|
||||
======================
|
||||
|
||||
This check converts ``for(...; ...; ...)`` loops to use the new range-based
|
||||
loops in C++11.
|
||||
|
||||
Three kinds of loops can be converted:
|
||||
|
||||
- Loops over statically allocated arrays.
|
||||
- Loops over containers, using iterators.
|
||||
- Loops over array-like containers, using ``operator[]`` and ``at()``.
|
||||
|
||||
MinConfidence option
|
||||
====================
|
||||
|
||||
risky
|
||||
-----
|
||||
|
||||
In loops where the container expression is more complex than just a
|
||||
reference to a declared expression (a variable, function, enum, etc.),
|
||||
and some part of it appears elsewhere in the loop, we lower our confidence
|
||||
in the transformation due to the increased risk of changing semantics.
|
||||
Transformations for these loops are marked as `risky`, and thus will only
|
||||
be converted if the minimum required confidence level is set to ``risky``.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int arr[10][20];
|
||||
int l = 5;
|
||||
|
||||
for (int j = 0; j < 20; ++j)
|
||||
int k = arr[l][j] + l; // using l outside arr[l] is considered risky
|
||||
|
||||
for (int i = 0; i < obj.getVector().size(); ++i)
|
||||
obj.foo(10); // using 'obj' is considered risky
|
||||
|
||||
See
|
||||
:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
|
||||
for an example of an incorrect transformation when the minimum required confidence
|
||||
level is set to `risky`.
|
||||
|
||||
reasonable (Default)
|
||||
--------------------
|
||||
|
||||
If a loop calls ``.end()`` or ``.size()`` after each iteration, the
|
||||
transformation for that loop is marked as `reasonable`, and thus will
|
||||
be converted if the required confidence level is set to ``reasonable``
|
||||
(default) or lower.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// using size() is considered reasonable
|
||||
for (int i = 0; i < container.size(); ++i)
|
||||
cout << container[i];
|
||||
|
||||
safe
|
||||
----
|
||||
|
||||
Any other loops that do not match the above criteria to be marked as
|
||||
`risky` or `reasonable` are marked `safe`, and thus will be converted
|
||||
if the required confidence level is set to ``safe`` or lower.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int arr[] = {1,2,3};
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
cout << arr[i];
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Original:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
const int N = 5;
|
||||
int arr[] = {1,2,3,4,5};
|
||||
vector<int> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.push_back(3);
|
||||
|
||||
// safe transform
|
||||
for (int i = 0; i < N; ++i)
|
||||
cout << arr[i];
|
||||
|
||||
// reasonable transform
|
||||
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
|
||||
cout << *it;*
|
||||
|
||||
// reasonable transform
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
cout << v[i];
|
||||
|
||||
After transformation with confidence level set to ``reasonable`` (default):
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
const int N = 5;
|
||||
int arr[] = {1,2,3,4,5};
|
||||
vector<int> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.push_back(3);
|
||||
|
||||
// safe transform
|
||||
for (auto & elem : arr)
|
||||
cout << elem;
|
||||
|
||||
// reasonable transform
|
||||
for (auto & elem : v)
|
||||
cout << elem;
|
||||
|
||||
// reasonable transform
|
||||
for (auto & elem : v)
|
||||
cout << elem;
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
There are certain situations where the tool may erroneously perform
|
||||
transformations that remove information and change semantics. Users of the tool
|
||||
should be aware of the behaviour and limitations of the transform outlined by
|
||||
the cases below.
|
||||
|
||||
Comments inside loop headers
|
||||
----------------------------
|
||||
|
||||
Comments inside the original loop header are ignored and deleted when
|
||||
transformed.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
for (int i = 0; i < N; /* This will be deleted */ ++i) { }
|
||||
|
||||
Range-based loops evaluate end() only once
|
||||
------------------------------------------
|
||||
|
||||
The C++11 range-based for loop calls ``.end()`` only once during the
|
||||
initialization of the loop. If in the original loop ``.end()`` is called after
|
||||
each iteration the semantics of the transformed loop may differ.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// The following is semantically equivalent to the C++11 range-based for loop,
|
||||
// therefore the semantics of the header will not change.
|
||||
for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
|
||||
|
||||
// Instead of calling .end() after each iteration, this loop will be
|
||||
// transformed to call .end() only once during the initialization of the loop,
|
||||
// which may affect semantics.
|
||||
for (iterator it = container.begin(); it != container.end(); ++it) { }
|
||||
|
||||
.. _IncorrectRiskyTransformation:
|
||||
|
||||
As explained above, calling member functions of the container in the body
|
||||
of the loop is considered `risky`. If the called member function modifies the
|
||||
container the semantics of the converted loop will differ due to ``.end()``
|
||||
being called only once.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
bool flag = false;
|
||||
for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
|
||||
// Add a copy of the first element to the end of the vector.
|
||||
if (!flag) {
|
||||
// This line makes this transformation 'risky'.
|
||||
vec.push_back(*it);
|
||||
flag = true;
|
||||
}
|
||||
cout << *it;
|
||||
}
|
||||
|
||||
The original code above prints out the contents of the container including the
|
||||
newly added element while the converted loop, shown below, will only print the
|
||||
original contents and not the newly added element.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
bool flag = false;
|
||||
for (auto & elem : vec) {
|
||||
// Add a copy of the first element to the end of the vector.
|
||||
if (!flag) {
|
||||
// This line makes this transformation 'risky'
|
||||
vec.push_back(elem);
|
||||
flag = true;
|
||||
}
|
||||
cout << elem;
|
||||
}
|
||||
|
||||
Semantics will also be affected if ``.end()`` has side effects. For example, in
|
||||
the case where calls to ``.end()`` are logged the semantics will change in the
|
||||
transformed loop if ``.end()`` was originally called after each iteration.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
iterator end() {
|
||||
num_of_end_calls++;
|
||||
return container.end();
|
||||
}
|
||||
|
||||
Overloaded operator->() with side effects
|
||||
-----------------------------------------
|
||||
|
||||
Similarly, if ``operator->()`` was overloaded to have side effects, such as
|
||||
logging, the semantics will change. If the iterator's ``operator->()`` was used
|
||||
in the original loop it will be replaced with ``<container element>.<member>``
|
||||
instead due to the implicit dereference as part of the range-based for loop.
|
||||
Therefore any side effect of the overloaded ``operator->()`` will no longer be
|
||||
performed.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
for (iterator it = c.begin(); it != c.end(); ++it) {
|
||||
it->func(); // Using operator->()
|
||||
}
|
||||
// Will be transformed to:
|
||||
for (auto & elem : c) {
|
||||
elem.func(); // No longer using operator->()
|
||||
}
|
||||
|
||||
Pointers and references to containers
|
||||
-------------------------------------
|
||||
|
||||
While most of the transform's risk analysis is dedicated to determining whether
|
||||
the iterator or container was modified within the loop, it is possible to
|
||||
circumvent the analysis by accessing and modifying the container through a
|
||||
pointer or reference.
|
||||
|
||||
If the container were directly used instead of using the pointer or reference
|
||||
the following transformation would have only been applied at the ``risky``
|
||||
level since calling a member function of the container is considered `risky`.
|
||||
The transform cannot identify expressions associated with the container that are
|
||||
different than the one used in the loop header, therefore the transformation
|
||||
below ends up being performed at the ``safe`` level.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
vector<int> vec;
|
||||
|
||||
vector<int> *ptr = &vec;
|
||||
vector<int> &ref = vec;
|
||||
|
||||
for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
|
||||
if (!flag) {
|
||||
// Accessing and modifying the container is considered risky, but the risk
|
||||
// level is not raised here.
|
||||
ptr->push_back(*it);
|
||||
ref.push_back(*it);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,151 @@
|
|||
modernize-pass-by-value
|
||||
=======================
|
||||
|
||||
With move semantics added to the language and the standard library updated with
|
||||
move constructors added for many types it is now interesting to take an argument
|
||||
directly by value, instead of by const-reference, and then copy. This
|
||||
transformation allows the compiler to take care of choosing the best way to
|
||||
construct the copy.
|
||||
|
||||
The transformation is usually beneficial when the calling code passes an
|
||||
*rvalue* and assumes the move construction is a cheap operation. This short
|
||||
example illustrates how the construction of the value happens:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void foo(std::string s);
|
||||
std::string get_str();
|
||||
|
||||
void f(const std::string &str) {
|
||||
foo(str); // lvalue -> copy construction
|
||||
foo(get_str()); // prvalue -> move construction
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Currently, only constructors are transformed to make use of pass-by-value.
|
||||
Contributions that handle other situations are welcome!
|
||||
|
||||
|
||||
Pass-by-value in constructors
|
||||
-----------------------------
|
||||
|
||||
Replaces the uses of const-references constructor parameters that are copied
|
||||
into class fields. The parameter is then moved with `std::move()`.
|
||||
|
||||
Since `std::move()` is a library function declared in `<utility>` it may be
|
||||
necessary to add this include. The transform will add the include directive when
|
||||
necessary.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <string>
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
- Foo(const std::string &Copied, const std::string &ReadOnly)
|
||||
- : Copied(Copied), ReadOnly(ReadOnly)
|
||||
+ Foo(std::string Copied, const std::string &ReadOnly)
|
||||
+ : Copied(std::move(Copied)), ReadOnly(ReadOnly)
|
||||
{}
|
||||
|
||||
private:
|
||||
std::string Copied;
|
||||
const std::string &ReadOnly;
|
||||
};
|
||||
|
||||
std::string get_cwd();
|
||||
|
||||
void f(const std::string &Path) {
|
||||
// The parameter corresponding to 'get_cwd()' is move-constructed. By
|
||||
// using pass-by-value in the Foo constructor we managed to avoid a
|
||||
// copy-construction.
|
||||
Foo foo(get_cwd(), Path);
|
||||
}
|
||||
|
||||
|
||||
If the parameter is used more than once no transformation is performed since
|
||||
moved objects have an undefined state. It means the following code will be left
|
||||
untouched:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <string>
|
||||
|
||||
void pass(const std::string &S);
|
||||
|
||||
struct Foo {
|
||||
Foo(const std::string &S) : Str(S) {
|
||||
pass(S);
|
||||
}
|
||||
|
||||
std::string Str;
|
||||
};
|
||||
|
||||
|
||||
Known limitations
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
A situation where the generated code can be wrong is when the object referenced
|
||||
is modified before the assignment in the init-list through a "hidden" reference.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
std::string s("foo");
|
||||
|
||||
struct Base {
|
||||
Base() {
|
||||
s = "bar";
|
||||
}
|
||||
};
|
||||
|
||||
struct Derived : Base {
|
||||
- Derived(const std::string &S) : Field(S)
|
||||
+ Derived(std::string S) : Field(std::move(S))
|
||||
{ }
|
||||
|
||||
std::string Field;
|
||||
};
|
||||
|
||||
void f() {
|
||||
- Derived d(s); // d.Field holds "bar"
|
||||
+ Derived d(s); // d.Field holds "foo"
|
||||
}
|
||||
|
||||
|
||||
Note about delayed template parsing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When delayed template parsing is enabled, constructors part of templated
|
||||
contexts; templated constructors, constructors in class templates, constructors
|
||||
of inner classes of template classes, etc., are not transformed. Delayed
|
||||
template parsing is enabled by default on Windows as a Microsoft extension:
|
||||
`Clang Compiler User’s Manual - Microsoft extensions`_.
|
||||
|
||||
Delayed template parsing can be enabled using the `-fdelayed-template-parsing`
|
||||
flag and disabled using `-fno-delayed-template-parsing`.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template <typename T> class C {
|
||||
std::string S;
|
||||
|
||||
public:
|
||||
= // using -fdelayed-template-parsing (default on Windows)
|
||||
= C(const std::string &S) : S(S) {}
|
||||
|
||||
+ // using -fno-delayed-template-parsing (default on non-Windows systems)
|
||||
+ C(std::string S) : S(std::move(S)) {}
|
||||
};
|
||||
|
||||
.. _Clang Compiler User’s Manual - Microsoft extensions: http://clang.llvm.org/docs/UsersManual.html#microsoft-extensions
|
||||
|
||||
.. seealso::
|
||||
|
||||
For more information about the pass-by-value idiom, read: `Want Speed? Pass by Value`_.
|
||||
|
||||
.. _Want Speed? Pass by Value: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
|
||||
|
|
|
@ -1,29 +1,71 @@
|
|||
modernize-replace-auto-ptr
|
||||
==========================
|
||||
|
||||
This check replaces the uses of the deprecated class ``std::auto_ptr`` by
|
||||
``std::unique_ptr`` (introduced in C++11). The transfer of ownership, done
|
||||
by the copy-constructor and the assignment operator, is changed to match
|
||||
``std::unique_ptr`` usage by using explicit calls to ``std::move()``.
|
||||
|
||||
Transforms the deprecated ``std::auto_ptr`` into the C++11 ``std::unique_ptr``.
|
||||
Migration example:
|
||||
|
||||
Note that both the ``std::auto_ptr`` type and the transfer of ownership are
|
||||
transformed. ``std::auto_ptr`` provides two ways to transfer the ownership,
|
||||
the copy-constructor and the assignment operator. Unlike most classes these
|
||||
operations do not 'copy' the resource but they 'steal' it.
|
||||
``std::unique_ptr`` uses move semantics instead, which makes the intent of
|
||||
transferring the resource explicit. This difference between the two smart
|
||||
pointers requeres to wrap the copy-ctor and assign-operator with
|
||||
``std::move()``.
|
||||
.. code-block:: c++
|
||||
|
||||
For example, given:
|
||||
-void take_ownership_fn(std::auto_ptr<int> int_ptr);
|
||||
+void take_ownership_fn(std::unique_ptr<int> int_ptr);
|
||||
|
||||
.. code:: c++
|
||||
void f(int x) {
|
||||
- std::auto_ptr<int> a(new int(x));
|
||||
- std::auto_ptr<int> b;
|
||||
+ std::unique_ptr<int> a(new int(x));
|
||||
+ std::unique_ptr<int> b;
|
||||
|
||||
std::auto_ptr<int> i, j;
|
||||
i = j;
|
||||
- b = a;
|
||||
- take_ownership_fn(b);
|
||||
+ b = std::move(a);
|
||||
+ take_ownership_fn(std::move(b));
|
||||
}
|
||||
|
||||
This code is transformed to:
|
||||
Since `std::move()` is a library function declared in `<utility>` it may be
|
||||
necessary to add this include. The transform will add the include directive when
|
||||
necessary.
|
||||
|
||||
.. code:: c++
|
||||
Known Limitations
|
||||
=================
|
||||
* If headers modification is not activated or if a header is not allowed to be
|
||||
changed this transform will produce broken code (compilation error), where the
|
||||
the headers' code will stay unchanged while the code using them will be
|
||||
changed.
|
||||
|
||||
std::unique_ptr<in> i, j;
|
||||
i = std::move(j);
|
||||
* Client code that declares a reference to an ``std::auto_ptr`` coming from code
|
||||
that can't be migrated (such as a header coming from a 3\ :sup:`rd` party
|
||||
library) will produce a compilation error after migration. This is because the
|
||||
type of the reference will be changed to ``std::unique_ptr`` but the type
|
||||
returned by the library won't change, binding a reference to
|
||||
``std::unique_ptr`` from an ``std::auto_ptr``. This pattern doesn't make much
|
||||
sense and usually ``std::auto_ptr`` are stored by value (otherwise what is the
|
||||
point in using them instead of a reference or a pointer?).
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// <3rd-party header...>
|
||||
std::auto_ptr<int> get_value();
|
||||
const std::auto_ptr<int> & get_ref();
|
||||
|
||||
// <calling code (with migration)...>
|
||||
-std::auto_ptr<int> a(get_value());
|
||||
+std::unique_ptr<int> a(get_value()); // ok, unique_ptr constructed from auto_ptr
|
||||
|
||||
-const std::auto_ptr<int> & p = get_ptr();
|
||||
+const std::unique_ptr<int> & p = get_ptr(); // won't compile
|
||||
|
||||
* Non-instantiated templates aren't modified.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template <typename X>
|
||||
void f() {
|
||||
std::auto_ptr<X> p;
|
||||
}
|
||||
|
||||
// only 'f<int>()' (or similar) will trigger the replacement.
|
||||
|
||||
|
|
|
@ -1,4 +1,134 @@
|
|||
modernize-use-auto
|
||||
==================
|
||||
|
||||
This check is responsible for using the ``auto`` type specifier for
|
||||
variable declarations to *improve code readability and maintainability*.
|
||||
For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
std::vector<int>::iterator I = my_container.begin();
|
||||
|
||||
// transforms to:
|
||||
|
||||
auto I = my_container.begin();
|
||||
|
||||
The ``auto`` type specifier will only be introduced in situations where the
|
||||
variable type matches the type of the initializer expression. In other words
|
||||
``auto`` should deduce the same type that was originally spelled in the source.
|
||||
However, not every situation should be transformed:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int val = 42;
|
||||
InfoStruct &I = SomeObject.getInfo();
|
||||
|
||||
// Should not become:
|
||||
|
||||
auto val = 42;
|
||||
auto &I = SomeObject.getInfo();
|
||||
|
||||
In this example using ``auto`` for builtins doesn't improve readability. In
|
||||
other situations it makes the code less self-documenting impairing readability
|
||||
and maintainability. As a result, ``auto`` is used only introduced in specific
|
||||
situations described below.
|
||||
|
||||
Iterators
|
||||
=========
|
||||
|
||||
Iterator type specifiers tend to be long and used frequently, especially in
|
||||
loop constructs. Since the functions generating iterators have a common format,
|
||||
the type specifier can be replaced without obscuring the meaning of code while
|
||||
improving readability and maintainability.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
for (std::vector<int>::iterator I = my_container.begin(),
|
||||
E = my_container.end();
|
||||
I != E; ++I) {
|
||||
}
|
||||
|
||||
// becomes
|
||||
|
||||
for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
|
||||
}
|
||||
|
||||
The transform will only replace iterator type-specifiers when all of the
|
||||
following conditions are satisfied:
|
||||
* The iterator is for one of the standard container in ``std`` namespace:
|
||||
|
||||
* ``array``
|
||||
|
||||
* ``deque``
|
||||
|
||||
* ``forward_list``
|
||||
|
||||
* ``list``
|
||||
|
||||
* ``vector``
|
||||
|
||||
* ``map``
|
||||
|
||||
* ``multimap``
|
||||
|
||||
* ``set``
|
||||
|
||||
* ``multiset``
|
||||
|
||||
* ``unordered_map``
|
||||
|
||||
* ``unordered_multimap``
|
||||
|
||||
* ``unordered_set``
|
||||
|
||||
* ``unordered_multiset``
|
||||
|
||||
* ``queue``
|
||||
|
||||
* ``priority_queue``
|
||||
|
||||
* ``stack``
|
||||
|
||||
* The iterator is one of the possible iterator types for standard containers:
|
||||
|
||||
* ``iterator``
|
||||
|
||||
* ``reverse_iterator``
|
||||
|
||||
* ``const_iterator``
|
||||
|
||||
* ``const_reverse_iterator``
|
||||
|
||||
* In addition to using iterator types directly, typedefs or other ways of
|
||||
referring to those types are also allowed. However, implementation-specific
|
||||
types for which a type like ``std::vector<int>::iterator`` is itself a
|
||||
typedef will not be transformed. Consider the following examples:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// The following direct uses of iterator types will be transformed.
|
||||
std::vector<int>::iterator I = MyVec.begin();
|
||||
{
|
||||
using namespace std;
|
||||
list<int>::iterator I = MyList.begin();
|
||||
}
|
||||
|
||||
// The type specifier for J would transform to auto since it's a typedef
|
||||
// to a standard iterator type.
|
||||
typedef std::map<int, std::string>::const_iterator map_iterator;
|
||||
map_iterator J = MyMap.begin();
|
||||
|
||||
// The following implementation-specific iterator type for which
|
||||
// std::vector<int>::iterator could be a typedef would not be transformed.
|
||||
__gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
|
||||
|
||||
* The initializer for the variable being declared is not a braced initializer
|
||||
list. Otherwise, use of ``auto`` would cause the type of the variable to be
|
||||
deduced as``std::initializer_list``.
|
||||
|
||||
Known Limitations
|
||||
=================
|
||||
* If the initializer is an explicit conversion constructor, the transform will
|
||||
not replace the type specifier even though it would be safe to do so.
|
||||
* User-defined iterators are not handled at this time.
|
||||
|
||||
|
|
|
@ -1,4 +1,65 @@
|
|||
modernize-use-nullptr
|
||||
=====================
|
||||
|
||||
The check converts the usage of null pointer constants (eg. ``NULL``, ``0``)
|
||||
to use the new C++11 ``nullptr`` keyword.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void assignment() {
|
||||
char *a = NULL;
|
||||
char *b = 0;
|
||||
char c = 0;
|
||||
}
|
||||
|
||||
int *ret_ptr() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
transforms to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void assignment() {
|
||||
char *a = nullptr;
|
||||
char *b = nullptr;
|
||||
char c = 0;
|
||||
}
|
||||
|
||||
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:``UserNullMacros`` 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;
|
||||
}
|
||||
|
||||
transforms to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#define MY_NULL NULL
|
||||
void assignment() {
|
||||
int *p = nullptr;
|
||||
}
|
||||
|
||||
if the ``UserNullMacros`` option is set to ``MY_NULL``.
|
||||
|
|
Loading…
Reference in New Issue