From 8d6b474bf67bb8a75e8f60196f7eaf7528a85c62 Mon Sep 17 00:00:00 2001 From: Lei YU Date: Wed, 30 Aug 2023 06:47:14 +0000 Subject: [PATCH] Add clang-tidy-fix target Add the `clang-tidy-fix` target to apply clang-tidy fixes to the source code. This is done by calling `run-clang-tidy` with `-fix` argument. Add a test case to run `clang-tidy-fix` and verify the file is changed. Signed-off-by: Lei YU --- .../markdown/snippets/meson_clang_tidy_fix.md | 9 ++++++++ mesonbuild/backend/ninjabackend.py | 1 + mesonbuild/scripts/clangtidy.py | 7 +++++- unittests/allplatformstests.py | 22 ++++++++++++++++++- unittests/helpers.py | 5 ++++- 5 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/meson_clang_tidy_fix.md diff --git a/docs/markdown/snippets/meson_clang_tidy_fix.md b/docs/markdown/snippets/meson_clang_tidy_fix.md new file mode 100644 index 000000000..3a8c772f3 --- /dev/null +++ b/docs/markdown/snippets/meson_clang_tidy_fix.md @@ -0,0 +1,9 @@ +## clang-tidy-fix target + +If `clang-tidy` is installed and the project's source root contains a +`.clang-tidy` (or `_clang-tidy`) file, Meson will automatically define +a `clang-tidy-fix` target that runs `run-clang-tidy` tool with `-fix` +option to apply the changes found by clang-tidy to the source code. + +If you have defined your own `clang-tidy-fix` target, Meson will not +generate its own target. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a57b249fe..925941a96 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3659,6 +3659,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) if not shutil.which('clang-tidy'): return self.generate_clangtool('tidy') + self.generate_clangtool('tidy', 'fix') def generate_tags(self, tool: str, target_name: str) -> None: import shutil diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py index 324a26ea0..943bde578 100644 --- a/mesonbuild/scripts/clangtidy.py +++ b/mesonbuild/scripts/clangtidy.py @@ -23,8 +23,12 @@ import typing as T def run_clang_tidy(fname: Path, builddir: Path) -> subprocess.CompletedProcess: return subprocess.run(['clang-tidy', '-p', str(builddir), str(fname)]) +def run_clang_tidy_fix(fname: Path, builddir: Path) -> subprocess.CompletedProcess: + return subprocess.run(['run-clang-tidy', '-fix', '-format', '-quiet', '-p', str(builddir), str(fname)]) + def run(args: T.List[str]) -> int: parser = argparse.ArgumentParser() + parser.add_argument('--fix', action='store_true') parser.add_argument('sourcedir') parser.add_argument('builddir') options = parser.parse_args(args) @@ -32,4 +36,5 @@ def run(args: T.List[str]) -> int: srcdir = Path(options.sourcedir) builddir = Path(options.builddir) - return run_tool('clang-tidy', srcdir, builddir, run_clang_tidy, builddir) + run_func = run_clang_tidy_fix if options.fix else run_clang_tidy + return run_tool('clang-tidy', srcdir, builddir, run_func, builddir) diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index acab026e9..f06279a2f 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -42,7 +42,7 @@ from mesonbuild.mesonlib import ( is_sunos, windows_proof_rmtree, python_command, version_compare, split_args, quote_arg, relpath, is_linux, git, search_version, do_conf_file, do_conf_str, default_prefix, MesonException, EnvironmentException, OptionKey, - windows_proof_rm + windows_proof_rm, quiet_git ) from mesonbuild.programs import ExternalProgram @@ -3029,6 +3029,26 @@ class AllPlatformTests(BasePlatformTests): self.assertIn('cttest.cpp:4:20', out) self.assertNotIn(dummydir, out) + @skipIfNoExecutable('clang-tidy') + @unittest.skipIf(not is_git_repo(), 'Skipping because this is not in git repo') + def test_clang_tidy_fix(self): + if self.backend is not Backend.ninja: + raise SkipTest(f'Clang-tidy is for now only supported on Ninja, not {self.backend.name}') + if shutil.which('c++') is None: + raise SkipTest('Clang-tidy breaks when ccache is used and "c++" not in path.') + if is_osx(): + raise SkipTest('Apple ships a broken clang-tidy that chokes on -pipe.') + testdir = os.path.join(self.unit_test_dir, '68 clang-tidy') + dummydir = os.path.join(testdir, 'dummydir.h') + self.init(testdir, override_envvars={'CXX': 'c++'}) + out = self.run_target('clang-tidy-fix') + self.assertIn('cttest.cpp:4:20', out) + self.assertNotIn(dummydir, out) + ret = quiet_git(['diff', '--exit-code', 'test cases/unit/68 clang-tidy/cttest.cpp'], '.') + self.assertFalse(ret[0]) + # Restore the file + quiet_git(['checkout', '--', 'test cases/unit/68 clang-tidy/cttest.cpp'], '.') + def test_identity_cross(self): testdir = os.path.join(self.unit_test_dir, '69 cross') # Do a build to generate a cross file where the host is this target diff --git a/unittests/helpers.py b/unittests/helpers.py index 7483f51b7..83327cbad 100644 --- a/unittests/helpers.py +++ b/unittests/helpers.py @@ -12,7 +12,7 @@ from contextlib import contextmanager from mesonbuild.compilers import detect_c_compiler, compiler_from_language from mesonbuild.mesonlib import ( MachineChoice, is_osx, is_cygwin, EnvironmentException, OptionKey, MachineChoice, - OrderedSet + OrderedSet, quiet_git ) from run_tests import get_fake_env @@ -135,6 +135,9 @@ def is_tarball(): return True return False +def is_git_repo(): + return quiet_git(['branch'], '.')[0] + @contextmanager def chdir(path: str): curdir = os.getcwd()