CLI application to run clang-tidy on a set of files specified using globs in a JSON configuration file.

Last update: Jun 11, 2022

run-clang-tidy

Build status

CLI application for running clang-tidy for an existing .clang-tidy file on a set of files, specified using globs in a .json configuration file.

Quickstart

The minimal command for executing this is the following:

$ run-clang-tidy path/to/tidy.json

Execute run-clang-tidy --help for more details, or run-clang-tidy schema for a complete schema description of the configuration file.

Hints for the impatient user:

Contents

The JSON configuration file

The core of this CLI tool is a .json configuration file that specifies where all the files that should be analyzed can be found. We'll be using a demo file, building it up step by step to explain the individual fields. The structure of the .json file is also documented in the schema sub-command (execute run-clang-tidy schema). To get started, we create an empty .json file that contains an empty object.

{
}

Adding paths

The only fields that are really required in this configuration file are the paths and the buildRoot. The paths always need to be specified in the configuration file, whereas the build root folder can provided as command line parameter. The section The build root and compile_commands.json will provide the necessay information about the build root directory.

For now we're looking at the paths: The paths field contains paths or globs, relative to the parent directory of the configuration file tidy.json. Consider the following folder structure:

ProjectRoot
├── .clang-tidy
│
├── _bld_
│   └── compile_commands.json
│
├── Some
│   └── Path
│       ├── header h
│       └── source.c
│
└── Settings
    ├── tidy.json
    └── <...>

In the configuration file tidy.json, the paths to the two files and and build root directory would need to be specified as following:

{
  "paths": [
    "../Some/Path/header h",
    "../Some/Path/source.c",
  ],
  "buildRoot": "../_bld"
}

Remark: This tool is made for software developers, thus any user should know that paths by themselves can become fairly complex: Take links, throw in character encodings, you get the idea. So anyone using smileys or other surreal things in their paths can contribute to this repository in case of problems, but not all scenarios can or will be tested.

Clearly, no one wants to specify all paths manually, which is why this tool supports the use of Unix-style globs. The following patterns will all resolve to the same paths, but are just provided for reference:

{
  "paths": [
    "../**/*.[ch]",
    "../Some/*/*.*",
  ],
  "buildRoot": "../_bld"
}

Assuming you have clang-tidy installed and a .clang-tidy file in one of the parent directories of your sources, e.g., in ProjectRoot, this is all you need::

$ run-clang-tidy path/to/tidy.json

Notice that the working directory of the tool is irrelevant since all paths are specified relative to the provided tidy.json. For now, this is all you need to know, we'll go into details about the supported scenarios later and will continue exploring the configuration options in the .json file.

The build root and compile_commands.json

Modern build systems like cmake or clang support the generation of a [compilation database][https://clang.llvm.org/docs/JSONCompilationDatabase.html]. This compilation database is simply a .json file (the convention is to name it compile_commands.json) placed in the root directory of your build that contains all the commands with all parameters that are invoked in the build process.

Remark: Any static analyzer will need to know all compile options and files to analyze your code. It is therefore important that your codebase is buildable before trying to analyze it, though clang-tidy is amongst the more tolerating tools.

Remark: In case you're using plain old make, a primitive way to generate this file is demonstrated in the example makefile for the target build-data.

This compilation database is a major input for clang-tidy. Therefore, the path to the folder containing this file must be specified either in the configuration file using the buildRoot, or via the command line parameter --build-root. If specified in the configuration file, the path is resolved relative to the configuration file. As command line parameter an aboslute or relative path to the invocation of run-clang-tidy must be provided.

Glob- and path syntax

This tool uses the globset rust crate to resolve globs. It therefore also relies on its syntax. We're borrowing the explanation here. When using globs, standard Unix-style glob syntax is supported:

  • ? matches any single character. It does not match path separators.
  • * matches zero or more characters but does not match across directory boundaries, i.e., it does not match a path separator. You have to use ** for that:
  • ** recursively matches directories and if used without a path separator it means "match everything".
  • {a,b} matches a or b where a and b are arbitrary glob patterns. Nesting {...} is not supported.
  • [ab] matches a or b where a and b are characters. Use [!ab] to match any character except for a and b.
  • Metacharacters such as * and ? can be escaped with the character class notation. e.g., [*] matches *.
  • A backslash \ will escape all metacharacters in a glob, but it must be specified as double backslash \\ due to the fact that the glob is defined in a .json configuration file. If it precedes a non-meta character, then the slash is ignored.

For Windows paths, all globs are case insensitive.

Remark: Due to the caveat that backslashes must be escaped in .json files, and that a backslash in a glob behaves differently depending on whether or not the following character is a metacharacter, it is highly recommended to use a forward slash / as path separator on any platform. On Windows it is possible to use \\ as path separators, but only if it does not precede a metacharacter.

Pre-filtering

By default, this tool will exclude all hidden files and folders from its search. This behaviour can be configured with the field filterPre. This field sets up a filter that is applied while recursively searching for files and therefore before matching files against the provided globs in the field paths. A typical pattern for such a filter is to exclude folders used by revision control systems, e.g., .git (or .svn) folders.

For this field, you can still use globs, but keep in mind that such a filter is applied on directories as well and thus if the filter matches then the directory will not even be searched, making it unnecessary to use, e.g., ** after the name. The following example shows a pre-filter configured to exclude all files within the .git folder, and also excludes all hidden files and directories.

{
  "paths": [
    "../**/*.[ch]",
    "../Some/*/*.*",
  ],
  "filterPre": ["**/.git", ".*"],
  "buildRoot": "../_bld"
}

If no hidden folders should be skipped simply set this field to an empty list [].

Post-filtering

With the previous configuration file, we matched all files and folders except for hidden files. Sometimes, however, it is useful to apply a filter after matching all paths, e.g., to exclude specific filenames that occur multiple times, or to simplify the patterns in the field paths. This can be achieved with filterPost:

{
  "paths": [
    "../**/*.[ch]",
    "../Some/*/*.*",
  ],
  "filterPre": ["**/.git", ".*"],
  "filterPost": ["FreeRTOS.h", "**/Hal*/**"],
  "buildRoot": "../_bld"
}

In the above example, any Hal* folder within any of the paths will be filtered without having to create a complex glob for paths.

Specifying a .clang-tidy file and a root directory

If no .clang-tidy file is placed in the root directory of your project (assuming there is one), executing run-clang-tidy without any additional command-line parameters (explained below) would not produce the desired results - quite the opposite since clang-tidy checks any root folder until it might encounter a .clang-tidy file. Therefore the configuration file allows to specify the tidy file using the field tidyFile, and the root common root directory of all paths using tidyRoot:

ProjectRoot
├── _bld_
│   └── compile_commands.json
│
├── Some
│   └── Path
│       ├── header.h
│       └── source.c
│
└── Settings
    ├── tidy.json
    ├── tidy.clang-tidy
    └── <...>
{
  "paths": [
    "../**/*.[ch]",
    "../Some/*/*.*",
  ],
  "filterPre": ["**/.git", ".*"],
  "filterPost": ["FreeRTOS.h", "**/Hal*/**"],
  "tidyFile": "./tidy.clang-tidy",
  "tidyRoot": "../",
  "buildRoot": "../_bld"
}

The name or the extension of the tidyFile must be .clang-tidy. This allows you to store multiple .clang-tidy files in the same directory, e.g., driver.clang-tidy and application.clang-tidy.

When analyzing the files, run-clang-tidy will:

  • Copy the provided tidy file to the specified root directory (renaming it to .clang-tidy, if necessary),
  • execute clang-tidy for all resolved paths,
  • and finally remove the temporary file.

Only if you kill the execution of the tool (e.g., via CTRL+C) it won't be able to delete the temporary file.

Remark: Specifying a root directory is necessary since it is not feasible to determine a common denominator for all paths. Also, killing the execution of the tool will prevent deleting the temporary file and therefore might clutter your workspace with tidy files, since adding new globs or paths might result in a different root directory.

Remark: The tool will check whether a .clang-tidy file with different content already exists in tidyRoot - and abort with an error if that is the case. If the contents match, the tool won't copy or delete any files and execute as if no tidyRoot and tidyFile were specified.

The tidyFile configuration will be replaced by the --tidy command-line parameter, if provided.

Specifying the clang-tidy command

By default, the tool tries to use the command clang-tidy for analyzing all resolved paths. If this command is not in your path, or if you use a different name for your executable (e.g., clang-tidy-10), then you need to specify the command or full path to the executable either via the command-line parameter --command or using the command field in your configuration file:

{
  "paths": [
    "../**/*.[ch]",
    "../Some/*/*.*",
  ],
  "filterPre": ["**/.git", ".*"],
  "filterPost": ["FreeRTOS.h", "**/Hal*/**"],
  "tidyFile": "./tidy.clang-tidy",
  "tidyRoot": "../",
  "buildRoot": "../_bld",
  "command": "/path/to/clang-tidy"
}

In contrast to the patterns in the field 'paths', the command can be specified as a path relative to the configuration file, as an absolute path, or as a simple executable name.

Similar to the tidyFile field, this configuration will be replaced by the --command command-line parameter, if provided. When specifying a relative path specified as command line parameter the path is resolved relative to the current working directory.

Notice: It is important that your tidy file is compatible with the version of clang-tidy that you are using. This is the main reason why clang-tidy is not installed with this tool.

Notice: Configuration files aim to be cross-platform as well. It is therefore allowed to omit the .exe extension for the clang-tidy executable. This also applies to the --command parameter.

Command-line Parameters

All available command-line parameters should be sufficiently described by the tool itself, when providing any of the options -h, --help, help. Also, the JSON schema of the configuration file can be displayed by using the schema subcommand. This JSON schema also contains descriptions for each of the options described above:

$ run-clang-tidy --help
$ run-clang-tidy help
$ run-clang-tidy schema

In the following, the most important options are described briefly.

Verbosity and --quiet

The verbosity is best configured by using the -v option:

  • -v is the default option; the tool will provide a "pretty-print" output complete with progress bar (implemented by the rust crate indicatif).

The "pretty" output is only available for the -v log level, for any other log level the tool will switch to a debug-style output. This kind of output is not optimized for being redirected to a file since the progress bar will rewrite previous lines. Use the -vv debug option instead.

  • -vv switches to the log level "debug", providing timestamps and a purely sequential output: No lines are being overwritten, and each message is logged to a new line.

  • -vvv and above switch to the log level "trace", which can contain even more (probably irrelevant) messages. This is intended mainly for debugging the tool in case you find issues.

To turn off any kind of output except for error messages, use the --quiet option. This overwrites the --verbosity level.

Speeding up the execution

By default, the tool will process each resolved path one by one. This can be rather slow for large projects. The command-line option -j, --jobs allows specifying the number of jobs that should be used the analysis.

  • If specified without a value, e.g., run-clang-tidy tidy.json -j, then all available logical cores will be used the analysis.
  • If specified with a value, e.g., run-clang-tidy tidy.json -j 3, then the tool will only spawn as many jobs as specified.

Remark: On slower machines, when executed with normal log level, the progress bar might flicker since the terminal might not be able to re-draw the new line fast enough. Currently, there's no way around this.

Specifying an alternative tidy file and command

The command-line options --tidy and --command allow specifying a .clang-tidy file and the command to use for executing clang-tidy. Please refer to the description of the .json configuration file for the fields tidyFile and command.

Remark: Specifying --tidy requires the field tidyRoot to be configured.

Specifying an alternative build root

The build root containing the compilation database is typically not fixed; each build might use a different output folder and tools may be installed in different directories (e.g., if executed as part of a CI chain).

Therefore the command-line option --build-root allows to specify the build directory when invoking this script, overriding, e.g., a default directory specified in the configuration .json file.

Use-cases

Due to the nature of this tool, i.e., the underlying clang tools, the use-cases are very similar when executing clang-format, for which a dedicated wrapper exists. Please refer to the matching section in the documentation of run-clang-format.

Pitfalls

Multiple .clang-tidy files

In case other .clang-tidy files exist in different folders, .clang-tidy will always use the first file that it finds when going back from the file to analyze. E.g., for source.c the file ProjectRoot/Some/.clang-tidy will be used.

ProjectRoot
│
├── Some
│   ├── .clang-tidy
│   └── Path
│       ├── header.h
│       └── source.c
│
└── .clang-tidy

When executing the tool with the following configuration, the files in Some/Path will be analzyed using Some/.clang-tidy and not with the configured tidy file, since this tool does not scan any paths for existing .clang-tidy files.

{
  "paths": [
    "./Some/**/*.[ch]",
  ],
  "tidyFile": ".clang-tidy",
  "tidyRoot": "../"
}

clang-tidy keeps on analyzing an excluded header files

There seems to be a catch when using clang-tidy to analyze sources, since it will also partly analyze included header files. E.g., assume the following module_untidy.h header file exists and is excluded from the analysis using filters and/or globs, i.e., clang-tidy is never invoked for the header file module_untidy.h:

#pragma once

// This definition violates the
// readability-uppercase-literal-suffix rule
// The `u` suffix of `2uL` must be uppercase, see
// https://clang.llvm.org/extra/clang-tidy/checks/readability-uppercase-literal-suffix.html
#define MODULE_UNTIDY_SMTH 2uL

This header file might be used in the following main.c, a file for which clang-tidy is executed. Here clang-tidy will still produce an error since MODULE_UNTIDY_SMTH is expanded within main.c:

#include "module_untidy.h"

int main (int argc, const char *argv[]) // NOLINT : unused argument argv
{
    // MODULE_UNTIDY_SMTH is expanded here and violates
    // readability-uppercase-literal-suffix rule
    unsigned int some_variable = MODULE_UNTIDY_SMTH;
    return (int) some_variable;
}

For this exact usage, i.e., where a macro is expanded in a file that is part of the analysis, there is no way to tell clang-tidy to ignore this finding except by adding the NOLINT exclusion in your code (refer to the documentation for suppressing undesired diagnostics).

For other scenarios it might be sufficient to exclude the HeaderFilterRegex setting from your .clang-tidy configuration file: This wrapper runs clang-tidy without the -header-filter parameter, therefore it will not be used except if specified in your .clang-tidy file. Also, please notice that clang-tidy uses the posix ERE flavor in its regexes.

Some files do not seem to be not correctly analyzed

When adding paths using globs, run-clang-tidy will resolve the globs to all files it can find, regardless of whether they are part of your build (compile_commands.json) or not. It will therefore call clang-tidy even for files that are not in your build. This has two effects:

  • clang-tidy will still try to compile the files, even if they are not part of your build. You'll get errors if the file doesn't compile, e.g., due to missing include paths or conflicting types for the function declaration and function definition.

  • For file that can be successfully compiled by clang-tidy no errors or warning will be generated, since clang-tidy will skip the actual analysis step. The files will still be listed in the execution of run-clang-tidy.

Remark: You may reproduce this by not excluding the module_unused.h in the test of this tool. This header file is not used in the build and the e_module_unused_a_enum violates the naming convention defined in the .clang-tidy files.

This might be fixed in a future version of run-clang-tidy, i.e., the tool might search for the compilation database too, and might try to find out which files are really part of the build. It should generate an error (or ignore it, see the roadmap below) if a glob resolved to a file that is not part of the build.

Roadmap

  • A --fix command line option might be added to allow invoking clang-tidy with the -fix option: clang-tidy is able to fix some violations by itself, using this command line option will instruct clang-tidy to fix the files in place when analyzing.

  • Update the default behaviour or add a --strict option to avoid running the analysis for files that are not part of the compilation database.

GitHub

https://github.com/lmapii/run-clang-tidy
You might also like...

Run your Rust CLI programs as state machines with persistence and recovery abilities

step-machine Run your CLI programs as state machines with persistence and recovery abilities. When such a program breaks you'll have opportunity to ch

Jan 9, 2022

koyo is a cli tool that lets you run commands as another user. It is similar to doas or sudo.

koyo is a cli tool that lets you run commands as another user. It is similar to doas or sudo.

Nov 27, 2021

A run-codes cli front end with some extra features

run-cli Run-cli A run-codes cli front end with some extra features Report Bug · Request Feature Table of Contents About The Project Built With Getting

May 27, 2022

A very opinionated, zero-configuration shell prompt

A very opinionated, zero-configuration shell prompt

A very opinionated, zero-configuration shell prompt

Nov 4, 2021

Irx-config - The library provides convenient way to represent/parse configuration from different sources

The irx-config library provides convenient way to represent/parse configuration from different sources. The main goals is to be very easy to use and t

Dec 31, 2021

Single File Assets is a file storage format for images

SFA (Rust) Single File Assets is a file storage format for images. The packed images are not guaranteed to be of same format because the format while

Jan 23, 2022

A command-line tool aiming to upload the local image used in your markdown file to the GitHub repo and replace the local file path with the returned URL.

A command-line tool aiming to upload the local image used in your markdown file to the GitHub repo and replace the local file path with the returned URL.

Pup A command line tool aiming to upload the local image used in your markdown file to the GitHub repo and replace the local file path with the return

Apr 20, 2022

Emulates an Edge hardware-based room sensor client purely as a CLI application

ambi_mock_client Usage You must have Rust installed to build ambi_mock_client. You can find documentation on installing Rust here. Using cargo run c

Jan 22, 2022

cli application to visualize crate download metrics.

cli application to visualize crate download metrics.

Jan 28, 2022
📺(tv) Tidy Viewer is a cross-platform CLI csv pretty printer that uses column styling to maximize viewer enjoyment.
📺(tv) Tidy Viewer is a cross-platform CLI csv pretty printer that uses column styling to maximize viewer enjoyment.

??(tv) Tidy Viewer is a cross-platform CLI csv pretty printer that uses column styling to maximize viewer enjoyment.

Jun 19, 2022
Set Shell Environment Variables across multiple shells with a single configuration file.

Xshe – Cross-Shell Environment Vars xshe allows for setting Shell Environment Variables across multiple shells with a single TOML configuration file.

Jun 19, 2022
Lists Steam applications that have specified a Steam Play compatibility tool

proton-usage Lists Steam applications that have specified a Steam Play compatibility tool. Useful for when you want to remove/uninstall unused compati

May 18, 2022
Application microframework with command-line option parsing, configuration, error handling, logging, and shell interactions
Application microframework with command-line option parsing, configuration, error handling, logging, and shell interactions

Abscissa is a microframework for building Rust applications (either CLI tools or network/web services), aiming to provide a large number of features w

Jun 16, 2022
A Rust CLI to provide last publish dates for packages in a package-lock.json file

NPM Package Age A Rust CLI which if you provide a npm lockfile (package-lock.json to start), it will give you a listing of all of the packages & the l

Feb 4, 2022
A command line tool for easily generating multiple versions of a configuration file from a single template

MultiConf A command line tool for easily generating multiple versions of a configuration file from a single template. Why? I'm a big fan of the i3 win

Feb 12, 2022
An ultra-fast CLI app that fixes JSON files in large codebase or folders

minosse An ultra fast CLI app that fixes json files in large codebase or folders USAGE: minosse [OPTIONS] <input-dir> FLAGS: -h, --help Prints

Jun 10, 2022
A simple CLI for combining json and yaml files

A simple CLI for combining json and yaml files

Apr 18, 2022
This is a simple command line application to convert bibtex to json written in Rust and Python

bibtex-to-json This is a simple command line application to convert bibtex to json written in Rust and Python. Why? To enable you to convert very big

Mar 23, 2022
A small CLI tool to query ArcGIS REST API services, implemented in Rust. The server response is returned as pretty JSON.

A small CLI tool to query ArcGIS REST API services, implemented in Rust. The server response is returned as pretty JSON.

Apr 25, 2022