From bc1d5da466a8f1f91a749c15a662509b563f25f8 Mon Sep 17 00:00:00 2001 From: Geoffrey Viola Date: Thu, 17 Jul 2025 16:08:45 +0000 Subject: [PATCH 1/5] Specify Third Party Headers --- cpplint.py | 39 ++++++++++++++++++++++++++++----------- cpplint_clitest.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/cpplint.py b/cpplint.py index 24897da..f8775e8 100755 --- a/cpplint.py +++ b/cpplint.py @@ -68,7 +68,7 @@ [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] [--repository=path] - [--linelength=digits] [--headers=x,y,...] + [--linelength=digits] [--headers=x,y,...] [--third_party_headers=pattern] [--recursive] [--exclude=path] [--extensions=hpp,cpp,...] @@ -240,6 +240,8 @@ The header extensions that cpplint will treat as .h in checks. Values are automatically added to --extensions list. (by default, only files with extensions %s will be assumed to be headers) + third_party_headers=pattern + Regex for identifying third-party headers to exclude from include checks. Examples: --headers=%s @@ -256,6 +258,7 @@ linelength=80 root=subdir headers=x,y,... + third_party_headers=pattern "set noparent" option prevents cpplint from traversing directory tree upwards looking for more .cfg files in parent directories. This option @@ -812,14 +815,6 @@ r")$" ) - -# These headers are excluded from [build/include] and [build/include_order] -# checks: -# - Anything not following google file name conventions (containing an -# uppercase character, such as Python.h or nsStringAPI.h, for example). -# - Lua headers. -_THIRD_PARTY_HEADERS_PATTERN = re.compile(r"^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$") - # Pattern for matching FileInfo.BaseName() against test file name _test_suffixes = ["_test", "_regtest", "_unittest"] _TEST_FILE_SUFFIX = "(" + "|".join(_test_suffixes) + r")$" @@ -981,6 +976,15 @@ # This is set by --headers flag. _hpp_headers: set[str] = set() +# These headers are excluded from [build/include_subdir], [build/include_order], and +# [build/include_alpha] +# The default checks are following +# - Anything not following google file name conventions (containing an +# uppercase character, such as Python.h or nsStringAPI.h, for example). +# - Lua headers. +# Default pattern for third-party headers (uppercase .h or Lua headers). +_third_party_headers_default = r"^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$" +_third_party_headers_pattern = re.compile(_third_party_headers_default) class ErrorSuppressions: """Class to track all error suppressions for cpplint""" @@ -1071,6 +1075,14 @@ def ProcessIncludeOrderOption(val): else: PrintUsage("Invalid includeorder value %s. Expected default|standardcfirst") +def ProcessThirdPartyHeadersOption(val): + """Sets the regex pattern for third-party headers.""" + global _third_party_headers_pattern + try: + _third_party_headers_pattern = re.compile(val) + except re.error: + PrintUsage(f"Invalid third_party_headers pattern: {val}") + def IsHeaderExtension(file_extension): return file_extension in GetHeaderExtensions() @@ -5744,7 +5756,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): if ( match and IsHeaderExtension(match.group(2)) - and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)) + and not _third_party_headers_pattern.match(match.group(1)) ): error( filename, @@ -5797,7 +5809,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): third_src_header = True break - if third_src_header or not _THIRD_PARTY_HEADERS_PATTERN.match(include): + if third_src_header or not _third_party_headers_pattern.match(include): include_state.include_list[-1].append((include, linenum)) # We want to ensure that headers appear in the right order: @@ -7527,6 +7539,8 @@ def ProcessConfigOverrides(filename): _root = os.path.join(os.path.dirname(cfg_file), val) elif name == "headers": ProcessHppHeadersOption(val) + elif name == "third_party_headers": + ProcessThirdPartyHeadersOption(val) elif name == "includeorder": ProcessIncludeOrderOption(val) else: @@ -7711,6 +7725,7 @@ def ParseArguments(args): "exclude=", "recursive", "headers=", + "third_party_headers=", "includeorder=", "config=", "quiet", @@ -7770,6 +7785,8 @@ def ParseArguments(args): ProcessExtensionsOption(val) elif opt == "--headers": ProcessHppHeadersOption(val) + elif opt == "--third_party_headers": + ProcessThirdPartyHeadersOption(val) elif opt == "--recursive": recursive = True elif opt == "--includeorder": diff --git a/cpplint_clitest.py b/cpplint_clitest.py index 7cb2047..3c79479 100755 --- a/cpplint_clitest.py +++ b/cpplint_clitest.py @@ -37,6 +37,7 @@ import subprocess import sys import tempfile +import textwrap import pytest from testfixtures import compare # type: ignore[import-untyped] @@ -224,5 +225,48 @@ def test_codelite_sample(self): self.check_all_in_folder("./samples/codelite-sample", 1) +# Tests for third_party_headers option +def TestThirdPartyHeadersDefault(tmp_path): + # By default, headers with uppercase letters are treated as third-party and not flagged + cpp = tmp_path / "test.cpp" + cpp.write_text(textwrap.dedent(""" + #include "Foo.h" + int main() { return 0; } + """)) + status, _, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) + assert status == 0 + # No include_subdir warning + assert b"build/include_subdir" not in err + +def TestThirdPartyHeadersOverride(tmp_path): + # Override third_party_headers so Foo.h is not recognized as third-party + cpp = tmp_path / "test.cpp" + cpp.write_text(textwrap.dedent(""" + #include "Foo.h" + int main() { return 0; } + """)) + # Use a pattern that matches nothing + flag = "--third_party_headers=^Bar.h$" + status, _, err = run_shell_command(BASE_CMD, f"{flag} {cpp.name}", cwd=str(tmp_path)) + # Expect a warning about include_subdir + assert status != 0 + assert b"build/include_subdir" in err + +def TestThirdPartyHeadersConfig(tmp_path): + # Override third_party_headers via config file so Foo.h is not recognized as third-party + cpp = tmp_path / "test.cpp" + cpp.write_text(textwrap.dedent(""" + #include "Foo.h" + int main() { return 0; } + """)) + # Write configuration file to override third_party_headers + config = tmp_path / "CPPLINT.cfg" + config.write_text("third_party_headers=^Bar.h$\n") + status, _, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) + # Expect a warning about include_subdir due to override + assert status != 0 + assert b"build/include_subdir" in err + + if __name__ == "__main__": pytest.main([__file__]) From a7f8c00c1048aa8ebd8b83591b4132ba1786357f Mon Sep 17 00:00:00 2001 From: Geoffrey Viola Date: Thu, 17 Jul 2025 16:56:03 +0000 Subject: [PATCH 2/5] go back to snake_case --- cpplint_clitest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpplint_clitest.py b/cpplint_clitest.py index 3c79479..9c1e4cc 100755 --- a/cpplint_clitest.py +++ b/cpplint_clitest.py @@ -226,7 +226,7 @@ def test_codelite_sample(self): # Tests for third_party_headers option -def TestThirdPartyHeadersDefault(tmp_path): +def test_third_party_headers_default(tmp_path): # By default, headers with uppercase letters are treated as third-party and not flagged cpp = tmp_path / "test.cpp" cpp.write_text(textwrap.dedent(""" @@ -238,7 +238,7 @@ def TestThirdPartyHeadersDefault(tmp_path): # No include_subdir warning assert b"build/include_subdir" not in err -def TestThirdPartyHeadersOverride(tmp_path): +def test_third_party_headers_override(tmp_path): # Override third_party_headers so Foo.h is not recognized as third-party cpp = tmp_path / "test.cpp" cpp.write_text(textwrap.dedent(""" @@ -252,7 +252,7 @@ def TestThirdPartyHeadersOverride(tmp_path): assert status != 0 assert b"build/include_subdir" in err -def TestThirdPartyHeadersConfig(tmp_path): +def test_third_party_headers_config(tmp_path): # Override third_party_headers via config file so Foo.h is not recognized as third-party cpp = tmp_path / "test.cpp" cpp.write_text(textwrap.dedent(""" From 8f447ab085c0f1073c855c204e48ce5e15a410a6 Mon Sep 17 00:00:00 2001 From: Geoffrey Viola Date: Thu, 17 Jul 2025 17:47:30 +0000 Subject: [PATCH 3/5] fix test --- cpplint_clitest.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cpplint_clitest.py b/cpplint_clitest.py index 9c1e4cc..5c5c7e3 100755 --- a/cpplint_clitest.py +++ b/cpplint_clitest.py @@ -230,11 +230,12 @@ def test_third_party_headers_default(tmp_path): # By default, headers with uppercase letters are treated as third-party and not flagged cpp = tmp_path / "test.cpp" cpp.write_text(textwrap.dedent(""" + // Copyright 2025 cpplint #include "Foo.h" int main() { return 0; } """)) - status, _, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) - assert status == 0 + status, out, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) + assert status == 0, f"stdout\n{out.decode('utf-8')}\nstderr\n{err.decode('utf-8')}" # No include_subdir warning assert b"build/include_subdir" not in err @@ -242,29 +243,29 @@ def test_third_party_headers_override(tmp_path): # Override third_party_headers so Foo.h is not recognized as third-party cpp = tmp_path / "test.cpp" cpp.write_text(textwrap.dedent(""" + // Copyright 2025 cpplint #include "Foo.h" - int main() { return 0; } """)) # Use a pattern that matches nothing flag = "--third_party_headers=^Bar.h$" - status, _, err = run_shell_command(BASE_CMD, f"{flag} {cpp.name}", cwd=str(tmp_path)) + status, out, err = run_shell_command(BASE_CMD, f"{flag} {cpp.name}", cwd=str(tmp_path)) # Expect a warning about include_subdir - assert status != 0 + assert status == 1, f"stdout\n{out.decode('utf-8')}\nstderr\n{err.decode('utf-8')}" assert b"build/include_subdir" in err def test_third_party_headers_config(tmp_path): # Override third_party_headers via config file so Foo.h is not recognized as third-party cpp = tmp_path / "test.cpp" cpp.write_text(textwrap.dedent(""" + // Copyright 2025 cpplint #include "Foo.h" - int main() { return 0; } """)) # Write configuration file to override third_party_headers config = tmp_path / "CPPLINT.cfg" config.write_text("third_party_headers=^Bar.h$\n") - status, _, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) + status, out, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) # Expect a warning about include_subdir due to override - assert status != 0 + assert status == 1, f"stdout\n{out.decode('utf-8')}\nstderr\n{err.decode('utf-8')}" assert b"build/include_subdir" in err From 34281b5034a9afd53a8c7391274eac75a6dcad39 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 17:47:47 +0000 Subject: [PATCH 4/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cpplint.py | 2 ++ cpplint_clitest.py | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cpplint.py b/cpplint.py index f8775e8..9afa32a 100755 --- a/cpplint.py +++ b/cpplint.py @@ -986,6 +986,7 @@ _third_party_headers_default = r"^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$" _third_party_headers_pattern = re.compile(_third_party_headers_default) + class ErrorSuppressions: """Class to track all error suppressions for cpplint""" @@ -1075,6 +1076,7 @@ def ProcessIncludeOrderOption(val): else: PrintUsage("Invalid includeorder value %s. Expected default|standardcfirst") + def ProcessThirdPartyHeadersOption(val): """Sets the regex pattern for third-party headers.""" global _third_party_headers_pattern diff --git a/cpplint_clitest.py b/cpplint_clitest.py index 5c5c7e3..165e192 100755 --- a/cpplint_clitest.py +++ b/cpplint_clitest.py @@ -229,23 +229,28 @@ def test_codelite_sample(self): def test_third_party_headers_default(tmp_path): # By default, headers with uppercase letters are treated as third-party and not flagged cpp = tmp_path / "test.cpp" - cpp.write_text(textwrap.dedent(""" + cpp.write_text( + textwrap.dedent(""" // Copyright 2025 cpplint #include "Foo.h" int main() { return 0; } - """)) + """) + ) status, out, err = run_shell_command(BASE_CMD, f"{cpp.name}", cwd=str(tmp_path)) assert status == 0, f"stdout\n{out.decode('utf-8')}\nstderr\n{err.decode('utf-8')}" # No include_subdir warning assert b"build/include_subdir" not in err + def test_third_party_headers_override(tmp_path): # Override third_party_headers so Foo.h is not recognized as third-party cpp = tmp_path / "test.cpp" - cpp.write_text(textwrap.dedent(""" + cpp.write_text( + textwrap.dedent(""" // Copyright 2025 cpplint #include "Foo.h" - """)) + """) + ) # Use a pattern that matches nothing flag = "--third_party_headers=^Bar.h$" status, out, err = run_shell_command(BASE_CMD, f"{flag} {cpp.name}", cwd=str(tmp_path)) @@ -253,13 +258,16 @@ def test_third_party_headers_override(tmp_path): assert status == 1, f"stdout\n{out.decode('utf-8')}\nstderr\n{err.decode('utf-8')}" assert b"build/include_subdir" in err + def test_third_party_headers_config(tmp_path): # Override third_party_headers via config file so Foo.h is not recognized as third-party cpp = tmp_path / "test.cpp" - cpp.write_text(textwrap.dedent(""" + cpp.write_text( + textwrap.dedent(""" // Copyright 2025 cpplint #include "Foo.h" - """)) + """) + ) # Write configuration file to override third_party_headers config = tmp_path / "CPPLINT.cfg" config.write_text("third_party_headers=^Bar.h$\n") From ebd458364ce1a41fe410b63222e2f3c3c51a9d1e Mon Sep 17 00:00:00 2001 From: Geoffrey Viola Date: Fri, 18 Jul 2025 13:46:07 -0400 Subject: [PATCH 5/5] Update cpplint.py Co-authored-by: Aaron Liu --- cpplint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpplint.py b/cpplint.py index 9afa32a..ad942ba 100755 --- a/cpplint.py +++ b/cpplint.py @@ -983,8 +983,8 @@ # uppercase character, such as Python.h or nsStringAPI.h, for example). # - Lua headers. # Default pattern for third-party headers (uppercase .h or Lua headers). -_third_party_headers_default = r"^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$" -_third_party_headers_pattern = re.compile(_third_party_headers_default) +_THIRD_PARTY_HEADERS_DEFAULT = r"^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$" +_third_party_headers_pattern = re.compile(_THIRD_PARTY_HEADERS_DEFAULT) class ErrorSuppressions: