A collection of C++ refactoring tools (WIP), provided as binaries.
- GCC 12.1.0+ (clang-14 has been tried, with no luck - fails on compiling the mix of std::filesystem iterators with views)
- CMake 3.22
libclang-16-dev
+ andlibllvm-16-dev
+liboost-1.74
+ripgrep
mkdir build
cd build/
cmake -DCMAKE_INSTALL_PREFIX=<where/to/install/the/binaries/> ..
cmake --build .
cmake --install .
# The binaries will be found under the directory: ${CMAKE_INSTALL_PREFIX}/bin.
Allows to generate the function definition from a function declaration. Unfortunately, it needs compilation database of the code, because it works on top of the libclang's C++ AST traversal.
It is used in such a way:
tsepepe_function_definition_generator \
<path to directory with compilation database> \
<path to the C++ file with declaration> \
<cursor position line begin> \
[optional cursor position line end]
When a selection is considered then the range [cursor position line begin; cursor position line end] is taken as the selected range. In case of no selection, cursor position line begin shall be the current position of the cursor.
For multiline declaration expects the first line with the declaration.
Example:
Having a header file called some_header.hpp, with content:
namespace Yolo
{
class SomeClass
{
unsigned int foo() const;
};
}
And compilation database in the current directory; when invoking:
tsepepe_function_definition_generator . some_header.hpp 5 # The declaration of foo() is in the 5th line.
The tool will output:
unsigned int Yolo::SomeClass::foo() const
The generation rules: (should be compliant with the C++ standard, but, please, create an issue in case something is missing)
- All the namespace names, and nested class names will be preserved in the signature. It applies both to the function name and the return type, which might be a custom type.
- Skips
virtual
,override
,static
, ... and other specifiers, which shall not appear in the definition. - Keeps
const
andnoexcept
,noexcept(<bool>)
. - Skips attributes (
[[nodiscard]]
, ...). - Keeps the ref-qualifier:
&
or&&
. - Skips default parameters.
Invoke it like that:
tsepepe_paired_cpp_file_finder \
<project root directory> \
<path to the C++ file for which the paired file will be found>
Tries to find a corresponding (paired) C++ file for the project found under the specified root. Paired C++ files are files with the same stem, e.g.: some_file.cpp/some_file.hpp.
Note: The input C++ file must be located in the child directory of the project root. The path may be absolute, or relative to the project root directory.
If the input file is a header file, then source file is tried to be found. Otherwise, if the input file is a source file, then a header file is tried to be found.
Firstly, tries to find the paired file in the same directory the input file is located. Then, traverses the directories recursively under project root. If finds multiple matches, then outputs them, each in a separate line. It means, that if the paired file is located in the same directory, it is returned straight away. Otherwise, the whole project directory is traversed to find a corresponding file. In case multiple matches are found, each of them is outputted to a separate line.
Allowed extensions are:
.cpp, .cxx, .cc, .h, .hpp, .hh, .hxx.
Some examples:
</root/dir/to/project>
|
|---- some_dir
|---- foo.hpp
|---- foo.cpp
When invoking:
paired_cpp_file_finder /root/dir/to/project some_dir/foo.hpp
or:
paired_cpp_file_finder /root/dir/to/project /root/dir/to/project/some_dir/foo.hpp
The result outputted to stdout is:
/root/dir/to/project/some_dir/foo.cpp
</root/dir/to/project>
|
|---- some_dir1
|---- foo.cpp
|---- some_dir2
|---- foo.hpp
|---- some_dir3
|---- foo.hpp
When invoking:
paired_cpp_file_finder /root/dir/to/project some_dir1/foo.cpp
The result outputted to stdout is:
/root/dir/to/project/some_dir2/foo.hpp
/root/dir/to/project/some_dir3/foo.hpp
</root/dir/to/project>
|
|---- some_dir1
|---- foo.cpp
|---- foo.hpp
|---- some_dir2
|---- foo.hpp
|---- some_dir3
|---- foo.hpp
When invoking:
paired_cpp_file_finder /root/dir/to/project some_dir1/foo.cpp
The result outputted to stdout is:
/root/dir/to/project/some_dir1/foo.hpp
Makes a class/struct implementor of an interface, with the name that is provided as a parameter. The project's C++ source and header files are traversed to find the interface with the specified name. The line number of the current cursor position is used to find the potential implementor of the interface. Thus it means, that the current cursor position must be within the potential implementor body. Takes also the entire file content as a parameter, and returns the new file content, with all the pieces of code added that make the class/struct the implementor of the interface, what means that:
- the proper
#include
statement is added, - the base-clause is created, or extended, with the interface qualified name,
- the pure virtual functions are put to the class body, marked
override
.
The code shall properly compile after the applying the changes. The only exception is that the
#include "<path>"
is not validated. The filename, where the interface is defined, is put as <path>
. It might be
that manual adjustment is needed, to properly resolve the include path, to avoid file not found
error.
Invoke it like that:
tsepepe_implementor maker \
<project root directory> \
<path to directory with compilation database> \
<path to the C++ file which will contain the implementor> \
<content of the C++ file which will contain the implementor> \
<the interface name> \
<line number where the cursor is located within the file with the potential implementor>
Example:
Having a project under path <PROJECT_ROOT>
, and an interface defined within a file src/include/yolo_interface.hpp
,
with content:
namespace Tsepepe
{
struct YoloInterface
{
virtual void do_stuff() = 0;
virtual ~YoloInterface() = default;
};
} // namespace Tsepepe
When the tool is called like that:
tsepepe_implementor_maker
<PROJECT_ROOT>/build # The path to the directory with the compile_commands.json
<PROJECT_ROOT> # The project root directory
<PROJECT_ROOT>/src/implementor.hpp # Path to the file with the potential implementor
'struct Implementor { };' # The source file content
YoloInterface # The inteface name.
1 # The first line contains the Implementor definition.
The output will be:
#include "yolo_interface.hpp"
struct Implementor : Tsepepe::YoloInterface {
void do_stuff() override;
};
Requirements:
- Run:
pip3 install -r tests/gherkin/requirements.txt
. - Install
bear
tool. cucumber
command line tool. On Ubuntu can be installed withapt
. It is a ruby tool, thusgem
can also be used for that.catch2
C++ testing library, version 3+.
Run:
cmake -DTSEPEPE_ENABLE_TESTING=ON ..
cmake --build . && ctest -LE long_running # Will skip leak checking, which takes few minutes with valgrind.
To run all the tests, with leak checking included:
ctest
The tests are written in Gherkin, driven by behave
.
- Extract method.
- Extract function.
- Extract lambda.
- Introduce parameter.
- Introduce lambda parameter.
- Introduce property.
- Extract interface?
- Turn inline definition into out-of-line definition.