| ================ |
| Getting Involved |
| ================ |
| |
| :program:`clang-tidy` has several own checks and can run Clang static analyzer |
| checks, but its power is in the ability to easily write custom checks. |
| |
| Checks are organized in modules, which can be linked into :program:`clang-tidy` |
| with minimal or no code changes in :program:`clang-tidy`. |
| |
| Checks can plug into the analysis on the preprocessor level using `PPCallbacks`_ |
| or on the AST level using `AST Matchers`_. When an error is found, checks can |
| report them in a way similar to how Clang diagnostics work. A fix-it hint can be |
| attached to a diagnostic message. |
| |
| The interface provided by :program:`clang-tidy` makes it easy to write useful |
| and precise checks in just a few lines of code. If you have an idea for a good |
| check, the rest of this document explains how to do this. |
| |
| There are a few tools particularly useful when developing clang-tidy checks: |
| * ``add_new_check.py`` is a script to automate the process of adding a new |
| check, it will create the check, update the CMake file and create a test; |
| * ``rename_check.py`` does what the script name suggests, renames an existing |
| check; |
| * :program:`clang-query` is invaluable for interactive prototyping of AST |
| matchers and exploration of the Clang AST; |
| * `clang-check`_ with the ``-ast-dump`` (and optionally ``-ast-dump-filter``) |
| provides a convenient way to dump AST of a C++ program. |
| |
| If CMake is configured with ``CLANG_ENABLE_STATIC_ANALYZER``, |
| :program:`clang-tidy` will not be built with support for the |
| ``clang-analyzer-*`` checks or the ``mpi-*`` checks. |
| |
| |
| .. _AST Matchers: https://clang.llvm.org/docs/LibASTMatchers.html |
| .. _PPCallbacks: https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html |
| .. _clang-check: https://clang.llvm.org/docs/ClangCheck.html |
| |
| |
| Choosing the Right Place for your Check |
| --------------------------------------- |
| |
| If you have an idea of a check, you should decide whether it should be |
| implemented as a: |
| |
| + *Clang diagnostic*: if the check is generic enough, targets code patterns that |
| most probably are bugs (rather than style or readability issues), can be |
| implemented effectively and with extremely low false positive rate, it may |
| make a good Clang diagnostic. |
| |
| + *Clang static analyzer check*: if the check requires some sort of control flow |
| analysis, it should probably be implemented as a static analyzer check. |
| |
| + *clang-tidy check* is a good choice for linter-style checks, checks that are |
| related to a certain coding style, checks that address code readability, etc. |
| |
| |
| Preparing your Workspace |
| ------------------------ |
| |
| If you are new to LLVM development, you should read the `Getting Started with |
| the LLVM System`_, `Using Clang Tools`_ and `How To Setup Clang Tooling For |
| LLVM`_ documents to check out and build LLVM, Clang and Clang Extra Tools with |
| CMake. |
| |
| Once you are done, change to the ``llvm/clang-tools-extra`` directory, and |
| let's start! |
| |
| .. _Getting Started with the LLVM System: https://llvm.org/docs/GettingStarted.html |
| .. _Using Clang Tools: https://clang.llvm.org/docs/ClangTools.html |
| .. _How To Setup Clang Tooling For LLVM: https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html |
| |
| |
| The Directory Structure |
| ----------------------- |
| |
| :program:`clang-tidy` source code resides in the |
| ``llvm/clang-tools-extra`` directory and is structured as follows: |
| |
| :: |
| |
| clang-tidy/ # Clang-tidy core. |
| |-- ClangTidy.h # Interfaces for users. |
| |-- ClangTidyCheck.h # Interfaces for checks. |
| |-- ClangTidyModule.h # Interface for clang-tidy modules. |
| |-- ClangTidyModuleRegistry.h # Interface for registering of modules. |
| ... |
| |-- google/ # Google clang-tidy module. |
| |-+ |
| |-- GoogleTidyModule.cpp |
| |-- GoogleTidyModule.h |
| ... |
| |-- llvm/ # LLVM clang-tidy module. |
| |-+ |
| |-- LLVMTidyModule.cpp |
| |-- LLVMTidyModule.h |
| ... |
| |-- objc/ # Objective-C clang-tidy module. |
| |-+ |
| |-- ObjCTidyModule.cpp |
| |-- ObjCTidyModule.h |
| ... |
| |-- tool/ # Sources of the clang-tidy binary. |
| ... |
| test/clang-tidy/ # Integration tests. |
| ... |
| unittests/clang-tidy/ # Unit tests. |
| |-- ClangTidyTest.h |
| |-- GoogleModuleTest.cpp |
| |-- LLVMModuleTest.cpp |
| |-- ObjCModuleTest.cpp |
| ... |
| |
| |
| Writing a clang-tidy Check |
| -------------------------- |
| |
| So you have an idea of a useful check for :program:`clang-tidy`. |
| |
| First, if you're not familiar with LLVM development, read through the `Getting |
| Started with LLVM`_ document for instructions on setting up your workflow and |
| the `LLVM Coding Standards`_ document to familiarize yourself with the coding |
| style used in the project. For code reviews we mostly use `LLVM Phabricator`_. |
| |
| .. _Getting Started with LLVM: https://llvm.org/docs/GettingStarted.html |
| .. _LLVM Coding Standards: https://llvm.org/docs/CodingStandards.html |
| .. _LLVM Phabricator: https://llvm.org/docs/Phabricator.html |
| |
| Next, you need to decide which module the check belongs to. Modules |
| are located in subdirectories of `clang-tidy/ |
| <https://github.com/llvm/llvm-project/tree/master/clang-tools-extra/clang-tidy/>`_ |
| and contain checks targeting a certain aspect of code quality (performance, |
| readability, etc.), certain coding style or standard (Google, LLVM, CERT, etc.) |
| or a widely used API (e.g. MPI). Their names are same as user-facing check |
| groups names described :ref:`above <checks-groups-table>`. |
| |
| After choosing the module and the name for the check, run the |
| ``clang-tidy/add_new_check.py`` script to create the skeleton of the check and |
| plug it to :program:`clang-tidy`. It's the recommended way of adding new checks. |
| |
| If we want to create a `readability-awesome-function-names`, we would run: |
| |
| .. code-block:: console |
| |
| $ clang-tidy/add_new_check.py readability awesome-function-names |
| |
| |
| The ``add_new_check.py`` script will: |
| * create the class for your check inside the specified module's directory and |
| register it in the module and in the build system; |
| * create a lit test file in the ``test/clang-tidy/`` directory; |
| * create a documentation file and include it into the |
| ``docs/clang-tidy/checks/list.rst``. |
| |
| Let's see in more detail at the check class definition: |
| |
| .. code-block:: c++ |
| |
| ... |
| |
| #include "../ClangTidyCheck.h" |
| |
| namespace clang { |
| namespace tidy { |
| namespace readability { |
| |
| ... |
| class AwesomeFunctionNamesCheck : public ClangTidyCheck { |
| public: |
| AwesomeFunctionNamesCheck(StringRef Name, ClangTidyContext *Context) |
| : ClangTidyCheck(Name, Context) {} |
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; |
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; |
| }; |
| |
| } // namespace readability |
| } // namespace tidy |
| } // namespace clang |
| |
| ... |
| |
| Constructor of the check receives the ``Name`` and ``Context`` parameters, and |
| must forward them to the ``ClangTidyCheck`` constructor. |
| |
| In our case the check needs to operate on the AST level and it overrides the |
| ``registerMatchers`` and ``check`` methods. If we wanted to analyze code on the |
| preprocessor level, we'd need instead to override the ``registerPPCallbacks`` |
| method. |
| |
| In the ``registerMatchers`` method we create an AST Matcher (see `AST Matchers`_ |
| for more information) that will find the pattern in the AST that we want to |
| inspect. The results of the matching are passed to the ``check`` method, which |
| can further inspect them and report diagnostics. |
| |
| .. code-block:: c++ |
| |
| using namespace ast_matchers; |
| |
| void AwesomeFunctionNamesCheck::registerMatchers(MatchFinder *Finder) { |
| Finder->addMatcher(functionDecl().bind("x"), this); |
| } |
| |
| void AwesomeFunctionNamesCheck::check(const MatchFinder::MatchResult &Result) { |
| const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x"); |
| if (MatchedDecl->getName().startswith("awesome_")) |
| return; |
| diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome") |
| << MatchedDecl |
| << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); |
| } |
| |
| (If you want to see an example of a useful check, look at |
| `clang-tidy/google/ExplicitConstructorCheck.h |
| <https://github.com/llvm/llvm-project/blob/master/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.h>`_ |
| and `clang-tidy/google/ExplicitConstructorCheck.cpp |
| <https://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/ExplicitConstructorCheck.cpp>`_). |
| |
| |
| Registering your Check |
| ---------------------- |
| |
| (The ``add_new_check.py`` takes care of registering the check in an existing |
| module. If you want to create a new module or know the details, read on.) |
| |
| The check should be registered in the corresponding module with a distinct name: |
| |
| .. code-block:: c++ |
| |
| class MyModule : public ClangTidyModule { |
| public: |
| void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { |
| CheckFactories.registerCheck<ExplicitConstructorCheck>( |
| "my-explicit-constructor"); |
| } |
| }; |
| |
| Now we need to register the module in the ``ClangTidyModuleRegistry`` using a |
| statically initialized variable: |
| |
| .. code-block:: c++ |
| |
| static ClangTidyModuleRegistry::Add<MyModule> X("my-module", |
| "Adds my lint checks."); |
| |
| |
| When using LLVM build system, we need to use the following hack to ensure the |
| module is linked into the :program:`clang-tidy` binary: |
| |
| Add this near the ``ClangTidyModuleRegistry::Add<MyModule>`` variable: |
| |
| .. code-block:: c++ |
| |
| // This anchor is used to force the linker to link in the generated object file |
| // and thus register the MyModule. |
| volatile int MyModuleAnchorSource = 0; |
| |
| And this to the main translation unit of the :program:`clang-tidy` binary (or |
| the binary you link the ``clang-tidy`` library in) |
| ``clang-tidy/tool/ClangTidyMain.cpp``: |
| |
| .. code-block:: c++ |
| |
| // This anchor is used to force the linker to link the MyModule. |
| extern volatile int MyModuleAnchorSource; |
| static int MyModuleAnchorDestination = MyModuleAnchorSource; |
| |
| |
| Configuring Checks |
| ------------------ |
| |
| If a check needs configuration options, it can access check-specific options |
| using the ``Options.get<Type>("SomeOption", DefaultValue)`` call in the check |
| constructor. In this case the check should also override the |
| ``ClangTidyCheck::storeOptions`` method to make the options provided by the |
| check discoverable. This method lets :program:`clang-tidy` know which options |
| the check implements and what the current values are (e.g. for the |
| ``-dump-config`` command line option). |
| |
| .. code-block:: c++ |
| |
| class MyCheck : public ClangTidyCheck { |
| const unsigned SomeOption1; |
| const std::string SomeOption2; |
| |
| public: |
| MyCheck(StringRef Name, ClangTidyContext *Context) |
| : ClangTidyCheck(Name, Context), |
| SomeOption(Options.get("SomeOption1", -1U)), |
| SomeOption(Options.get("SomeOption2", "some default")) {} |
| |
| void storeOptions(ClangTidyOptions::OptionMap &Opts) override { |
| Options.store(Opts, "SomeOption1", SomeOption1); |
| Options.store(Opts, "SomeOption2", SomeOption2); |
| } |
| ... |
| |
| Assuming the check is registered with the name "my-check", the option can then |
| be set in a ``.clang-tidy`` file in the following way: |
| |
| .. code-block:: yaml |
| |
| CheckOptions: |
| - key: my-check.SomeOption1 |
| value: 123 |
| - key: my-check.SomeOption2 |
| value: 'some other value' |
| |
| If you need to specify check options on a command line, you can use the inline |
| YAML format: |
| |
| .. code-block:: console |
| |
| $ clang-tidy -config="{CheckOptions: [{key: a, value: b}, {key: x, value: y}]}" ... |
| |
| |
| Testing Checks |
| -------------- |
| |
| To run tests for :program:`clang-tidy` use the command: |
| |
| .. code-block:: console |
| |
| $ ninja check-clang-tools |
| |
| :program:`clang-tidy` checks can be tested using either unit tests or |
| `lit`_ tests. Unit tests may be more convenient to test complex replacements |
| with strict checks. `Lit`_ tests allow using partial text matching and regular |
| expressions which makes them more suitable for writing compact tests for |
| diagnostic messages. |
| |
| The ``check_clang_tidy.py`` script provides an easy way to test both |
| diagnostic messages and fix-its. It filters out ``CHECK`` lines from the test |
| file, runs :program:`clang-tidy` and verifies messages and fixes with two |
| separate `FileCheck`_ invocations: once with FileCheck's directive |
| prefix set to ``CHECK-MESSAGES``, validating the diagnostic messages, |
| and once with the directive prefix set to ``CHECK-FIXES``, running |
| against the fixed code (i.e., the code after generated fix-its are |
| applied). In particular, ``CHECK-FIXES:`` can be used to check |
| that code was not modified by fix-its, by checking that it is present |
| unchanged in the fixed code. The full set of `FileCheck`_ directives |
| is available (e.g., ``CHECK-MESSAGES-SAME:``, ``CHECK-MESSAGES-NOT:``), though |
| typically the basic ``CHECK`` forms (``CHECK-MESSAGES`` and ``CHECK-FIXES``) |
| are sufficient for clang-tidy tests. Note that the `FileCheck`_ |
| documentation mostly assumes the default prefix (``CHECK``), and hence |
| describes the directive as ``CHECK:``, ``CHECK-SAME:``, ``CHECK-NOT:``, etc. |
| Replace ``CHECK`` by either ``CHECK-FIXES`` or ``CHECK-MESSAGES`` for |
| clang-tidy tests. |
| |
| An additional check enabled by ``check_clang_tidy.py`` ensures that |
| if `CHECK-MESSAGES:` is used in a file then every warning or error |
| must have an associated CHECK in that file. Or, you can use ``CHECK-NOTES:`` |
| instead, if you want to **also** ensure that all the notes are checked. |
| |
| To use the ``check_clang_tidy.py`` script, put a .cpp file with the |
| appropriate ``RUN`` line in the ``test/clang-tidy`` directory. Use |
| ``CHECK-MESSAGES:`` and ``CHECK-FIXES:`` lines to write checks against |
| diagnostic messages and fixed code. |
| |
| It's advised to make the checks as specific as possible to avoid checks matching |
| to incorrect parts of the input. Use ``[[@LINE+X]]``/``[[@LINE-X]]`` |
| substitutions and distinct function and variable names in the test code. |
| |
| Here's an example of a test using the ``check_clang_tidy.py`` script (the full |
| source code is at `test/clang-tidy/google-readability-casting.cpp`_): |
| |
| .. code-block:: c++ |
| |
| // RUN: %check_clang_tidy %s google-readability-casting %t |
| |
| void f(int a) { |
| int b = (int)a; |
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type [google-readability-casting] |
| // CHECK-FIXES: int b = a; |
| } |
| |
| To check more than one scenario in the same test file use |
| ``-check-suffix=SUFFIX-NAME`` on ``check_clang_tidy.py`` command line or |
| ``-check-suffixes=SUFFIX-NAME-1,SUFFIX-NAME-2,...``. |
| With ``-check-suffix[es]=SUFFIX-NAME`` you need to replace your ``CHECK-*`` |
| directives with ``CHECK-MESSAGES-SUFFIX-NAME`` and ``CHECK-FIXES-SUFFIX-NAME``. |
| |
| Here's an example: |
| |
| .. code-block:: c++ |
| |
| // RUN: %check_clang_tidy -check-suffix=USING-A %s misc-unused-using-decls %t -- -- -DUSING_A |
| // RUN: %check_clang_tidy -check-suffix=USING-B %s misc-unused-using-decls %t -- -- -DUSING_B |
| // RUN: %check_clang_tidy %s misc-unused-using-decls %t |
| ... |
| // CHECK-MESSAGES-USING-A: :[[@LINE-8]]:10: warning: using decl 'A' {{.*}} |
| // CHECK-MESSAGES-USING-B: :[[@LINE-7]]:10: warning: using decl 'B' {{.*}} |
| // CHECK-MESSAGES: :[[@LINE-6]]:10: warning: using decl 'C' {{.*}} |
| // CHECK-FIXES-USING-A-NOT: using a::A;$ |
| // CHECK-FIXES-USING-B-NOT: using a::B;$ |
| // CHECK-FIXES-NOT: using a::C;$ |
| |
| |
| There are many dark corners in the C++ language, and it may be difficult to make |
| your check work perfectly in all cases, especially if it issues fix-it hints. The |
| most frequent pitfalls are macros and templates: |
| |
| 1. code written in a macro body/template definition may have a different meaning |
| depending on the macro expansion/template instantiation; |
| 2. multiple macro expansions/template instantiations may result in the same code |
| being inspected by the check multiple times (possibly, with different |
| meanings, see 1), and the same warning (or a slightly different one) may be |
| issued by the check multiple times; :program:`clang-tidy` will deduplicate |
| _identical_ warnings, but if the warnings are slightly different, all of them |
| will be shown to the user (and used for applying fixes, if any); |
| 3. making replacements to a macro body/template definition may be fine for some |
| macro expansions/template instantiations, but easily break some other |
| expansions/instantiations. |
| |
| .. _lit: https://llvm.org/docs/CommandGuide/lit.html |
| .. _FileCheck: https://llvm.org/docs/CommandGuide/FileCheck.html |
| .. _test/clang-tidy/google-readability-casting.cpp: https://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp |
| |
| |
| Running clang-tidy on LLVM |
| -------------------------- |
| |
| To test a check it's best to try it out on a larger code base. LLVM and Clang |
| are the natural targets as you already have the source code around. The most |
| convenient way to run :program:`clang-tidy` is with a compile command database; |
| CMake can automatically generate one, for a description of how to enable it see |
| `How To Setup Clang Tooling For LLVM`_. Once ``compile_commands.json`` is in |
| place and a working version of :program:`clang-tidy` is in ``PATH`` the entire |
| code base can be analyzed with ``clang-tidy/tool/run-clang-tidy.py``. The script |
| executes :program:`clang-tidy` with the default set of checks on every |
| translation unit in the compile command database and displays the resulting |
| warnings and errors. The script provides multiple configuration flags. |
| |
| .. _How To Setup Clang Tooling For LLVM: https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html |
| |
| |
| * The default set of checks can be overridden using the ``-checks`` argument, |
| taking the identical format as :program:`clang-tidy` does. For example |
| ``-checks=-*,modernize-use-override`` will run the ``modernize-use-override`` |
| check only. |
| |
| * To restrict the files examined you can provide one or more regex arguments |
| that the file names are matched against. |
| ``run-clang-tidy.py clang-tidy/.*Check\.cpp`` will only analyze clang-tidy |
| checks. It may also be necessary to restrict the header files warnings are |
| displayed from using the ``-header-filter`` flag. It has the same behavior |
| as the corresponding :program:`clang-tidy` flag. |
| |
| * To apply suggested fixes ``-fix`` can be passed as an argument. This gathers |
| all changes in a temporary directory and applies them. Passing ``-format`` |
| will run clang-format over changed lines. |
| |
| |
| On checks profiling |
| ------------------- |
| |
| :program:`clang-tidy` can collect per-check profiling info, and output it |
| for each processed source file (translation unit). |
| |
| To enable profiling info collection, use the ``-enable-check-profile`` argument. |
| The timings will be output to ``stderr`` as a table. Example output: |
| |
| .. code-block:: console |
| |
| $ clang-tidy -enable-check-profile -checks=-*,readability-function-size source.cpp |
| ===-------------------------------------------------------------------------=== |
| clang-tidy checks profiling |
| ===-------------------------------------------------------------------------=== |
| Total Execution Time: 1.0282 seconds (1.0258 wall clock) |
| |
| ---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- |
| 0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) readability-function-size |
| 0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) Total |
| |
| It can also store that data as JSON files for further processing. Example output: |
| |
| .. code-block:: console |
| |
| $ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp |
| $ # Note that there won't be timings table printed to the console. |
| $ ls /tmp/out/ |
| 20180516161318717446360-source.cpp.json |
| $ cat 20180516161318717446360-source.cpp.json |
| { |
| "file": "/path/to/source.cpp", |
| "timestamp": "2018-05-16 16:13:18.717446360", |
| "profile": { |
| "time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00, |
| "time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01, |
| "time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01 |
| } |
| } |
| |
| There is only one argument that controls profile storage: |
| |
| * ``-store-check-profile=<prefix>`` |
| |
| By default reports are printed in tabulated format to stderr. When this option |
| is passed, these per-TU profiles are instead stored as JSON. |
| If the prefix is not an absolute path, it is considered to be relative to the |
| directory from where you have run :program:`clang-tidy`. All ``.`` and ``..`` |
| patterns in the path are collapsed, and symlinks are resolved. |
| |
| Example: |
| Let's suppose you have a source file named ``example.cpp``, located in the |
| ``/source`` directory. Only the input filename is used, not the full path |
| to the source file. Additionally, it is prefixed with the current timestamp. |
| |
| * If you specify ``-store-check-profile=/tmp``, then the profile will be saved |
| to ``/tmp/<ISO8601-like timestamp>-example.cpp.json`` |
| |
| * If you run :program:`clang-tidy` from within ``/foo`` directory, and specify |
| ``-store-check-profile=.``, then the profile will still be saved to |
| ``/foo/<ISO8601-like timestamp>-example.cpp.json`` |