Skip to content
314 changes: 314 additions & 0 deletions clang-tools-extra/docs/clang-change-namespace.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
======================
Clang-Change-Namespace
======================

.. contents::

.. toctree::
:maxdepth: 1

:program:`clang-change-namespace` can be used to change the surrounding
namespaces of class/function definitions.

Classes/functions in the moved namespace will have new namespaces while
references to symbols (e.g. types, functions) which are not defined in the
changed namespace will be correctly qualified by prepending namespace specifiers
before them. This will try to add shortest namespace specifiers possible.

When a symbol reference needs to be fully-qualified, this adds a `::` prefix to
the namespace specifiers unless the new namespace is the global namespace. For
Comment on lines +18 to +19
Copy link
Collaborator

@AaronBallman AaronBallman Jul 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain I understand this. Is this saying:

$ cat > test.cc << EOF
  namespace a {
  class A {
  };    
  } // namespace a

  a::A thing;
EOF

with

  clang-change-namespace \
    --old_namespace "a" \
    --new_namespace "" \
    --file_pattern "test.cc" \
    --i \
    test.cc

will produce A thing; but

  clang-change-namespace \
    --old_namespace "a" \
    --new_namespace "foo" \
    --file_pattern "test.cc" \
    --i \
    test.cc

will produce ::foo::A thing;?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood this as fully-qualifying the namespace when necessary to refer to the correct namespace. For example,

namespace other::foo {
a::A thing;
}  // namespace other::foo
namespace foo {
a::A thing;
}  // namespace foo
namespace other {
a::A thing;
struct foo {
  a::A thing;
};
a::A thing2;
}  // namespace other

becomes

namespace other::foo {
::foo::A thing;
}  // namespace other::foo
namespace foo {
A thing;
}  // namespace foo
namespace other {
foo::A thing;
struct foo {
  ::foo::A thing;
};
::foo:A thing2;
}  // namespace other

@kwk is that understanding correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried running the binary and after looking at the tests, I believe I've misunderstood. It looks like the tool only changes symbol references within the moved namespace. It does not update any symbol references from outside the moved namespace that point to symbols inside the moved namespace (which would no longer reference a valid symbol after the changes are applied).

Concrete example:

// a.cc
namespace old {
struct foo {};
}  // namespace old

namespace b {
old::foo g_foo;
}  // namespace b
$ clang-change-namespace --old_namespace 'old' --new_namespace 'new' --file_pattern '.*' a.cc
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "a.cc"
No compilation database found in /tmp or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
================= a.cc ================


namespace new {
  struct foo {};
} // namespace new

namespace b {
old::foo g_foo;
}  // namespace b

============================================

I think that is worth clarifying that this only updates symbol references in the moved namespace. (Unfortunately, I feel like that makes this tool much less useful.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not documented in db0021421e6b.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps not a question on the documentation, but rather the tool. Why is the global namespace special in this regard? It seems like there could be name ambiguities that arise with moving to the global namespace.

Copy link
Contributor Author

@kwk kwk Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tJener that is correct. As @nikic pointed out in a chat, this tool looks unmaintained. Most of the content changes in here look like as if they are 9 years old. The fact that you, @tJener and @AaronBallman ask these very specific questions lets me think that it is also not in active use. I'm going to go on PTO soon but I will put all of your questions on my list to address when I'm back.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I suspect you may be looking for an Eric Liu who appears to have written the majority of the tool, which is not me. Which is to say that my lack of familiarity with this tool shouldn't really mean much.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps not a question on the documentation, but rather the tool. Why is the global namespace special in this regard? It seems like there could be name ambiguities that arise with moving to the global namespace.

Consider this example to showcase ambiguities in names being used more than once:

cat test.cc 
namespace a {
class A {
  int classAFromWithinNamespace_a;
};
} // namespace a

class A {
  int classAInGlobalNamespace;
};

Then run clang-change-namespace --old_namespace "a" --new_namespace "" --file_pattern test.cc test.cc and the result will look like this:

class A {
  int classAFromWithinNamespace_a;
};

class A {
  int classAInGlobalNamespace;
};

The re-factoring looks correct but the code will not compile due to the name duplication. I'd say it is not up to the tool to ensure compilability in that sense. Of course, if you agree to this, then this MUST be documented.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tJener that is correct. As @nikic pointed out in a chat, this tool looks unmaintained. Most of the content changes in here look like as if they are 9 years old. The fact that you, @tJener and @AaronBallman ask these very specific questions lets me think that it is also not in active use. I'm going to go on PTO soon but I will put all of your questions on my list to address when I'm back.

Yeah, the code is basically unmaintained at this point, though I suspect that's because it's in "good enough" state that it doesn't require much active work. I'd say let's document the state of things as they are.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it is not up to the tool to ensure compilability in that sense. Of course, if you agree to this, then this MUST be documented.

I think that's worth documenting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've documented "Content already exists in new namespace" in 73a64b462271.

classes, only classes that are declared/defined in the given namespace in
specified files will be moved: forward declarations will remain in the old
namespace. The will be demonstrated in the next example.

Example usage
-------------

For example, consider this `test.cc` example here with the forward declared
class `FWD` and the defined class `A`, both in the namespace `a`.

.. code-block:: c++

namespace a {
class FWD;
class A {
FWD *fwd;
};
} // namespace a

And now let's change the namespace `a` to `x`.

.. code-block:: console

clang-change-namespace \
--old_namespace "a" \
--new_namespace "x" \
--file_pattern "test.cc" \
--i \
test.cc

Note that in the code below there's still the forward decalred class `FWD` that
stayed in the namespace `a`. It wasn't moved to the new namespace because it
wasn't defined/declared here in `a` but only forward declared.

.. code-block:: c++

namespace a {
class FWD;
} // namespace a
namespace x {

class A {
a::FWD *fwd;
};
} // namespace x


Another example
---------------

Consider this `test.cc` file:

.. code-block:: c++

namespace na {
class X {};
namespace nb {
class Y {
X x;
};
} // namespace nb
} // namespace na

To move the definition of class `Y` from namespace `na::nb` to `x::y`, run:

.. code-block:: console

clang-change-namespace \
--old_namespace "na::nb" \
--new_namespace "x::y" \
--file_pattern "test.cc" \
--i \
test.cc

This will overwrite `test.cc` to look like this:

.. code-block:: c++

namespace na {
class X {};

} // namespace na
namespace x {
namespace y {
class Y {
na::X x;
};
} // namespace y
} // namespace x

Note, that we've successfully moved the class `Y` from namespace `na::nb` to
namespace `x::y`.

Caveats
=======

Content already exists in new namespace
---------------------------------------

Consider this `test.cc` example that defines two `class A` one inside the
namespace `a` and one in namespace `b`:

.. code-block:: c++

namespace a {
class A {
int classAFromWithinNamespace_a;
};
} // namespace a

namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace b

Let's move everything from namespace `a` to namespace `b`:

.. code-block:: console

clang-change-namespace \
--old_namespace "a" \
--new_namespace "b" \
--file_pattern test.cc \
test.cc

As expected we now have to definitions of `class A` inside the namespace `b`:

.. code-block:: c++

namespace b {
class A {
int classAFromWithinNamespace_a;
};
} // namespace b

namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace b

The re-factoring looks correct but the code will not compile due to the name
duplication. It is not up to the tool to ensure compilability in that sense.
But one has to be aware of that.

Inline namespace doesn't work
-----------------------------

Consider this usage of two versions of implementations for a `greet` function:

.. code-block:: c++

#include <cstdio>

namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
namespace Version2 {
const char* greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace Greeter

int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}

Note, that currently `Greeter::greet()` will result in a call to
`Greeter::Version1::greet()` because that's the inlined namespace.

Let's say you want to move one and make `Version2` the default now and remove
the `inline` from the `Version1`. First let's try to turn `namespace Version2`
into `inline namespace Version2`:

.. code-block:: console

clang-change-namespace \
--old_namespace "Greeter::Version2" \
--new_namespace "inline Version2" \
--file_pattern main.cc main.cc

But this will put the `inline` keyword in the wrong place resulting in:

.. code-block:: c++

#include <cstdio>

namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1

} // namespace Greeter
namespace inline Greeter {
namespace Version2 {
const char *greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace inline Greeter

int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}

One cannot use `:program:`clang-change-namespace` to inline a namespace.

Symbol references not updated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the top or bottom, you can summarize the caveat behavior as only symbol references in the moved namespace are updated, references outside of it will not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 55e69ff.

-----------------------------

Consider this `test.cc` file:

.. code-block:: c++

namespace old {
struct foo {};
} // namespace old

namespace b {
old::foo g_foo;
} // namespace b

Notice that namespace `b` defines a global variable of type `old::foo`. If we
now change the name of the `old` namespace to `modern`, the reference will not
be updated:

.. code-block:: console

clang-change-namespace \
--old_namespace "old" \
--new_namespace "modern" \
--file_pattern test.cc \
test.cc

.. code-block:: c++

namespace modern {
struct foo {};
} // namespace modern

namespace b {
old::foo g_foo;
} // namespace b

`g_foo` is still of the no longer existing type `old::foo` while instead it
should use `modern::foo`.

Only symbol references in the moved namespace are updated, not outside of it.


:program:`clang-change-namespace` Command Line Options
======================================================

.. option:: --allowed_file=<string>

A file containing regexes of symbol names that are not expected to be updated
when changing namespaces around them.

.. option:: --dump_result

Dump new file contents in YAML, if specified.

.. option:: --extra-arg=<string>

Additional argument to append to the compiler command line

.. option:: --extra-arg-before=<string>

Additional argument to prepend to the compiler command line

.. option:: --file_pattern=<string>

Only rename namespaces in files that match the given regular expression
pattern.

.. option:: -i

Inplace edit <file>s, if specified.

.. option:: --new_namespace=<string>

New namespace. Use `""` when you target the global namespace.

.. option:: --old_namespace=<string>

Old namespace.

.. option:: -p <string>

Build path

.. option:: --style=<string>

The style name used for reformatting.
1 change: 1 addition & 0 deletions clang-tools-extra/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Contents

clang-tidy/index
clang-include-fixer
clang-change-namespace
modularize
pp-trace
clangd <https://clangd.llvm.org/>
Expand Down
Loading