mirror of
https://github.com/rizinorg/cutter
synced 2026-08-11 16:23:06 -04:00
Added scripts/clang-tidy.py Updated doc to tell about clang-tidy Added checks for clang-tidy are defined below * cppcoreguidelines-pro-type-cstyle-cast * misc-const-correctness * misc-unused-parameters * modernize-loop-convert * modernize-use-auto * modernize-use-nullptr * readability-braces-around-statements * readability-identifier-naming * readability-make-member-function-const * performance-for-range-copy * performance-enum-size * performance-move-const-arg * performance-no-automatic-move * performance-noexcept-destructor * performance-noexcept-move-constructor * performance-noexcept-swap * performance-unnecessary-copy-initialization * performance-unnecessary-value-param * performance-use-std-move * performance-move-constructor-init
519 lines
14 KiB
ReStructuredText
519 lines
14 KiB
ReStructuredText
|
|
Cutter Development Guidelines
|
|
===============================
|
|
|
|
.. note::
|
|
New to Cutter development? Check out our :doc:`tutorial for new developers <getting-started>`.
|
|
|
|
|
|
Common Usage
|
|
--------------
|
|
|
|
CutterCore Class
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
This is the main class where every link with Rizin is made. It is *unique*
|
|
across the whole process. To access it, simply call ``Core()``.
|
|
|
|
Example:
|
|
|
|
.. code:: cpp
|
|
|
|
Core()->getOffset();
|
|
|
|
|
|
Seek the Current File
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To modify Rizin seek use ``CutterCore::seek(const RVA offset)``. This
|
|
is important because it will emit a
|
|
``CutterCore::seekChanged(RVA offset)`` signal. Never ever call
|
|
``cmd("s offset")``;
|
|
|
|
Example:
|
|
|
|
.. code:: cpp
|
|
|
|
Core()->seek(0x00C0FFEE);
|
|
|
|
.. note::
|
|
|
|
Cutter also supports a silent seek which doesn't trigger the ``seekChanged`` event and doesn't add new entries to the seek history.
|
|
|
|
|
|
Creating a Widget
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
Make sure to connect the ``CutterCore::seekChanged(RVA offset)`` signal
|
|
so your widget refreshes its output when Rizin seek is modified
|
|
(switching to another function, etc.).
|
|
|
|
Coding Style
|
|
------------
|
|
|
|
clang-format
|
|
~~~~~~~~~~~~
|
|
|
|
In general, we follow a slightly customized version of `the official Qt guidelines <https://wiki.qt.io/Qt_Coding_Style>`__
|
|
to format the code. Before sending a pull request, you will need to use `clang-format <https://clang.llvm.org/docs/ClangFormat.html>`__ (version 8 or newer)
|
|
to format the code. The command line for formatting the code according
|
|
to the style is:
|
|
|
|
.. code:: bash
|
|
|
|
clang-format -style=file -i src/filename.cpp
|
|
|
|
If your changes were done on many files across the codebase, you can use this oneliner to run ``clang-format`` on the entire ``src`` directory:
|
|
|
|
.. code:: bash
|
|
|
|
find ./src -regex '.*\.\(cpp\|h\)' -exec clang-format -style=file -i {} \;
|
|
|
|
clang-tidy
|
|
~~~~~~~~~~
|
|
|
|
Beyond formatting, we use `clang-tidy <https://clang.llvm.org/extra/clang-tidy/>`__ (version 13 or newer) to catch potential style violations.
|
|
|
|
To run ``clang-tidy``, first configure your build to generate ``compile_commands.json`` and run the autogen tools:
|
|
|
|
.. code:: bash
|
|
|
|
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCUTTER_QT=6 -DCUTTER_USE_BUNDLED_RIZIN=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
|
|
cmake --build build --target Cutter_autogen
|
|
|
|
Run ``clang-tidy`` on modified files relative to the latest commit
|
|
|
|
.. code:: bash
|
|
|
|
git diff -U0 --no-color HEAD~1 | clang-tidy-diff.py -p1 -path build/
|
|
|
|
Similar to ``clang-format``, If your changes were done on many files across the codebase, you can use this oneliner to run ``clang-tidy`` on the entire 'src' directory. The following command excludes third-party and auto generated files:
|
|
|
|
.. code:: bash
|
|
|
|
run-clang-tidy -p build ".*src/(?!(themes|bindings|fonts|img|translations|Cutter_autogen)).*\.(cpp|h)$"
|
|
|
|
``clang-tidy`` can also attempt to fix style violations using the ``-fix`` flag. However these may not always be perfect. Make sure to verify the fixes before opening a pull request:
|
|
|
|
.. code:: bash
|
|
|
|
git diff -U0 --no-color HEAD~1 | clang-tidy-diff.py -fix -p1 -path build/
|
|
|
|
Python scripts
|
|
~~~~~~~~~~~~~~
|
|
|
|
If you don't want to run manual commands, cutter also provides scripts for running ``clang-format`` and ``clang-tidy`` in the ``scripts`` directory
|
|
|
|
.. code:: bash
|
|
|
|
python scripts/clang-format.py -h
|
|
usage: clang-format.py [-h] [-C CLANG_FORMAT] [-c] [-v] [-f FILE] [-d DIFF]
|
|
|
|
Clang format the cutter project
|
|
|
|
options:
|
|
-h, --help show this help message and exit
|
|
-C, --clang-format CLANG_FORMAT
|
|
path of clang-format
|
|
-c, --check enable the check mode
|
|
-v, --verbose use verbose output
|
|
-f, --file FILE formats (or checks) only the given file
|
|
-d, --diff DIFF format all modified file related to branch
|
|
|
|
.. code:: bash
|
|
|
|
usage: clang-tidy.py [-h] [-T RUN_CLANG_TIDY] [-p BUILD_PATH] [-j JOBS] [-r REGEX] [-i] [-q]
|
|
|
|
clang-tidy regex wrapper
|
|
|
|
options:
|
|
-h, --help show this help message and exit
|
|
-T, --run-clang-tidy RUN_CLANG_TIDY
|
|
Path of run-clang-tidy binary
|
|
-p, --build-path BUILD_PATH
|
|
Path to the build directory
|
|
-j, --jobs JOBS Number of parallel execution jobs
|
|
-r, --regex REGEX Regex pattern for filtering files
|
|
-i, --fix Apply fixes automatically
|
|
-q, --quiet Suppress configuration logs
|
|
|
|
Below are some of the low level coding conventions that we follow
|
|
|
|
Variables
|
|
~~~~~~~~~
|
|
|
|
Variable names start with a lowercase letter with each consecutive word starting with an uppercase letter. (**camelBack** case)
|
|
|
|
.. code:: cpp
|
|
|
|
int Height // Wrong
|
|
string name_of_widget // Wrong
|
|
|
|
int height; // Correct
|
|
string nameOfWidget // Correct
|
|
|
|
Avoid meaningless variable names
|
|
|
|
.. code:: cpp
|
|
|
|
int a, b; // Wrong
|
|
string c; // Wrong
|
|
|
|
int height, width; // Correct
|
|
string nameOfThis; // Correct
|
|
Object obj; // Also Correct
|
|
|
|
Only first letter of an acronym is uppercase
|
|
|
|
.. code:: cpp
|
|
|
|
CutterJsonWigdet cutterJSONWidget; // Wrong
|
|
|
|
CutterJsonWigdet cutterJsonWigdet; // Correct
|
|
|
|
Variable names don't use leading/trailing underscores, including prefixes like m\_
|
|
|
|
.. code:: cpp
|
|
|
|
class Example {
|
|
private:
|
|
int index_; // Wrong
|
|
int m_index; // Wrong
|
|
|
|
int index; // Correct
|
|
};
|
|
|
|
Global variables follow the same naming conventions
|
|
|
|
Anonymous Namespace
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
Global entities (variables, functions, structs...) private to a source file (internal linkage) are defined in an anonymous namespace
|
|
|
|
.. code:: cpp
|
|
|
|
namespace {
|
|
int globalCounter = 0;
|
|
|
|
bool doSomething() {
|
|
...
|
|
return true;
|
|
}
|
|
};
|
|
|
|
Functions
|
|
~~~~~~~~~
|
|
|
|
Function names follow the same naming convention as `variables`_. (**camelBack** casing)
|
|
|
|
.. code:: cpp
|
|
|
|
void do_something() {} // Wrong
|
|
|
|
void DoSomething() {} // Wrong
|
|
|
|
void doSomething() {} // Correct
|
|
|
|
Unused parameters inside a function are omitted in the definition either by commenting out the parameter name or not writing the name at all.
|
|
|
|
Avoid using ``Q_UNUSED``
|
|
|
|
.. code:: cpp
|
|
|
|
// Bad
|
|
void doSomething(int one, int two) {
|
|
Q_UNUSED(one)
|
|
...
|
|
}
|
|
|
|
// Good
|
|
void doSomething(int /*one*/, int two) {
|
|
...
|
|
}
|
|
|
|
// Good
|
|
void doSomething(int, int two) {
|
|
...
|
|
}
|
|
|
|
Avoid the use of automatic name based connections. See `Connecting Qt Signals`_.
|
|
|
|
Classes
|
|
~~~~~~~~
|
|
|
|
First letter of each word is uppercased. (**CamelCase**)
|
|
|
|
.. code:: cpp
|
|
|
|
class memory_dock_widget; // Wrong
|
|
class memoryDockWidget; // Wrong
|
|
|
|
class MemoryDockWidget; // Correct
|
|
|
|
Only first letter of an acronym is uppercase
|
|
|
|
.. code:: cpp
|
|
|
|
class CutterJSONWidget // Wrong
|
|
|
|
class CutterJsonWidget // Correct
|
|
|
|
|
|
Member variables follow the same naming conventions defined in `variables`_ that means no leading/trailing underscores or m\_ prefixes
|
|
|
|
Casting
|
|
~~~~~~~
|
|
|
|
Avoid the use of C style casts, prefer C++ casts (``static_cast``, ``const_cast`` ...)
|
|
|
|
Use ``qobject_cast`` for ``QObjects``
|
|
|
|
Smart Pointers
|
|
~~~~~~~~~~~~~~
|
|
|
|
Prefer the use of C++ smart pointers (``unique_ptr``, ``shared_ptr``...)
|
|
|
|
.. code:: cpp
|
|
|
|
RegisterProfileDialog *ui; // Bad
|
|
|
|
std::unique_ptr<Ui::RegisterProfileDialog> ui; // Good
|
|
|
|
Braces
|
|
~~~~~~
|
|
|
|
In contrast to the official guidelines of Qt, in Cutter we always use curly braces in conditional statements, even if the body of a conditional statement contains only one line.
|
|
|
|
.. code:: cpp
|
|
|
|
// Wrong
|
|
if (address.isEmpty())
|
|
return false;
|
|
|
|
// Correct
|
|
if (address.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
// Wrong
|
|
for (int i = 0; i < 10; ++i)
|
|
qDebug("%i", i);
|
|
|
|
// Correct
|
|
for (int i = 0; i < 10; ++i) {
|
|
qDebug("%i", i);
|
|
}
|
|
|
|
Loops
|
|
~~~~~
|
|
|
|
We use the C++11 foreach loop style, which means any “foreach” loop should
|
|
look like:
|
|
|
|
.. code:: cpp
|
|
|
|
// Good - If a copy of each element is required
|
|
for (auto import : importsArray) {
|
|
doSomething(import);
|
|
}
|
|
|
|
// Good - If copy is not required
|
|
for (auto &import : importsArray) {
|
|
doSomething(import);
|
|
}
|
|
|
|
// Good - If no modification is required
|
|
for (const auto &import : importsArray) {
|
|
doSomething(import);
|
|
}
|
|
|
|
auto
|
|
~~~~
|
|
|
|
Prefer the use of ``auto`` keyword when working with the following:
|
|
|
|
.. code:: cpp
|
|
|
|
// Iterators
|
|
auto it = myMap.find(key);
|
|
for (const auto &import : importsArray) { ... }
|
|
|
|
// Lambdas
|
|
auto multiply = [](int a, int b) -> int { return a * b; };
|
|
|
|
// Casting
|
|
auto myFloat = static_cast<float>(myInt);
|
|
|
|
// Initializing using new
|
|
auto *item = new QListWidgetItem(text);
|
|
|
|
nullptr
|
|
~~~~~~~
|
|
|
|
Please do not use ``0`` nor ``Q_NULLPTR``, only use ``nullptr``.
|
|
|
|
Example:
|
|
|
|
.. code:: cpp
|
|
|
|
std::unique_ptr<QObject> obj = nullptr;
|
|
|
|
// Note that this is just an example, unique_ptr constructor initializes the internal pointer to nullptr by default
|
|
|
|
|
|
Connecting Qt Signals
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Use one of the following methods for connecting signals to slots:
|
|
|
|
.. code:: cpp
|
|
|
|
// typically you will make connection in the constructor to a member of current class
|
|
connect(this->ui->button1, &QPushButton::clicked,
|
|
this, &MyObject::buttonClicked); // Good
|
|
|
|
// you can also connect directly other object slots
|
|
connect(checkbox, &QCheckBox::toggled, widget, &QWidget::setEnabled); // Good
|
|
|
|
// use lambda for passing extra arguments
|
|
connect(button1, &QPushButton::clicked, this, [this](){ foo(getBar()); }); // Good
|
|
|
|
This syntax performs compile-time type checks and allows the use of lambda
|
|
functions. Other approaches for connecting signals can silently break at runtime.
|
|
|
|
Don't use the older macro based syntax or automatic name based connections.
|
|
|
|
.. code:: cpp
|
|
|
|
// SIGNAL and SLOT macros
|
|
connect(sender, SIGNAL(clicked), this, SLOT(buttonClicked)); // BAD
|
|
|
|
// automatic name based connection
|
|
slot:
|
|
void on_actionNew_triggered(); // BAD
|
|
|
|
// 3 argument connect without receiver object
|
|
connect(sender, &SomeObject::signal, [this](){ this->foo(getBar()); }); // BAD
|
|
|
|
|
|
Includes
|
|
~~~~~~~~
|
|
|
|
Strive to include only **required** definitions inside header files.
|
|
This will avoid triggering additional unnecessary compilations.
|
|
|
|
If you only need to know that a class exists but don't need the prototype,
|
|
you can declare the class like this:
|
|
|
|
.. code:: cpp
|
|
|
|
class MyClassThatExists;
|
|
|
|
/** ... **/
|
|
|
|
private:
|
|
std::unqiue_ptr<MyClassThatExists> classInstance;
|
|
|
|
And then include the class header inside your .cpp so you can use that class.
|
|
|
|
If you need something in the source file (.cpp) that is not a class member,
|
|
then add the include in the source file.
|
|
|
|
The includes must be ordered from local to global. That is, first include
|
|
any local header file (with double quotes like `#include "common/Helpers.h"`.
|
|
Then, after an empty newline, include Qt definitions like
|
|
`#include <QShortcut>`.
|
|
Finally, include the standard C++ headers you need.
|
|
|
|
Includes must be sorted by alphabetical order.
|
|
|
|
This is automatically handled by running ``clang-format``
|
|
|
|
Docstrings
|
|
~~~~~~~~~~
|
|
|
|
Our API reference is generated using Doxygen, so when it comes to
|
|
function documentation, please use the following format:
|
|
|
|
.. code:: cpp
|
|
|
|
/**
|
|
* @brief Add new parameters to the accumulator
|
|
* @param params The parameters to add
|
|
* @return True if the parameters were added, false otherwise
|
|
*/
|
|
bool accumulate(RefreshDeferrerParams params) { ... };
|
|
|
|
Documenting every function is generally not required. Only document those functions whose purpose is not immediately understandable just by looking at the function name.
|
|
|
|
.. code:: cpp
|
|
|
|
// No need for docs
|
|
int getCount() const;
|
|
|
|
// Should provide docs
|
|
bool syncRemote();
|
|
|
|
It is preferred for classes to have documentation just above the definition, explaining their purpose.
|
|
|
|
.. code:: cpp
|
|
|
|
/**
|
|
* @brief A short description about SomeDialog
|
|
*/
|
|
class SomeDialog {
|
|
...
|
|
};
|
|
|
|
General Coding Advices
|
|
----------------------
|
|
|
|
Functions Documentation
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
You can find the class documentation in the `API Reference <https://cutter.re/docs/api.html>`__ menu item.
|
|
|
|
Updating the Git Submodules
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Git submodules play a major part in Cutter. This, because Cutter is powered
|
|
by Rizin, its parent project, and it tries to stay up-to-date with its
|
|
recent version, which allows us to implement new features, and enjoy bug
|
|
fixes and performance improvements on Rizin. Often, we need to update
|
|
the Rizin submodule or the others, to push their most recent
|
|
version to Cutter.
|
|
|
|
You can view the list of all the submodules from the cutter root folder with:
|
|
|
|
.. code:: sh
|
|
|
|
git config --file .gitmodules --get-regexp path | awk '{ print $2 }'
|
|
|
|
To update all the submodules at once, run these commands from the
|
|
cutter root folder:
|
|
|
|
.. code:: sh
|
|
|
|
git submodule foreach git pull origin master
|
|
git add submodule_name_1 submodule_name_2
|
|
git commit -m "Update submodules"
|
|
|
|
More likely, you'll only need to update the *rizin* submodule.
|
|
In order to update one submodule individually, use the following code:
|
|
|
|
.. code:: sh
|
|
|
|
cd rizin
|
|
git checkout dev && git pull
|
|
cd ..
|
|
git add rizin
|
|
git commit -m "Update rizin submodule"
|
|
|
|
|
|
Useful Resources (Qt Development)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
* `Signals & Slots <https://doc.qt.io/qt-6/signalsandslots.html>`__
|
|
* `Model/View Programming <https://doc.qt.io/qt-6/model-view-programming.html>`__ - read this if you are going to work with a list or table-like widgets
|
|
* `QAction <https://doc.qt.io/qt-6/qaction.html#details>`__
|