From 2d2e1c9bbfd25f0d47e1ee193e7160cda4930b70 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:25:32 -0400 Subject: [PATCH 1/7] [pre-commit.ci] pre-commit autoupdate (#374) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7eb37f3..ad76be7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.4 + rev: v0.11.5 hooks: - id: ruff - id: ruff-format From 54f085a6ee825e7adbf244145404f22881617896 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Fri, 18 Apr 2025 12:02:48 -0400 Subject: [PATCH 2/7] chore: bump dev version (#378) Per release policy --- CHANGELOG.rst | 3 +++ cpplint.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4ed9f3e..7aa9397 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog ********* +TBA +=== + 2.0.2 (2025-04-08) =========== diff --git a/cpplint.py b/cpplint.py index 7abd2ba..4573d89 100755 --- a/cpplint.py +++ b/cpplint.py @@ -61,7 +61,7 @@ # if empty, use defaults _valid_extensions: set[str] = set() -__VERSION__ = "2.0.2" +__VERSION__ = "2.0.3-dev0" _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit|sed|gsed] From b32e1f309981df5f3eecc3a65c9ea708cb2eb5ba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:50:14 -0400 Subject: [PATCH 3/7] [pre-commit.ci] pre-commit autoupdate (#379) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad76be7..718e8c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.5 + rev: v0.11.6 hooks: - id: ruff - id: ruff-format From b6ada002ca39c8b55ed08b278e0135c8a940db89 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 18 May 2025 18:39:30 -0400 Subject: [PATCH 4/7] change shebang to python3 (#383) --- cpplint.py | 2 +- cpplint_clitest.py | 2 +- cpplint_unittest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpplint.py b/cpplint.py index 4573d89..e400b05 100755 --- a/cpplint.py +++ b/cpplint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2009 Google Inc. All rights reserved. # diff --git a/cpplint_clitest.py b/cpplint_clitest.py index b20566f..7cb2047 100755 --- a/cpplint_clitest.py +++ b/cpplint_clitest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2009 Google Inc. All rights reserved. # diff --git a/cpplint_unittest.py b/cpplint_unittest.py index 730babd..e36093f 100755 --- a/cpplint_unittest.py +++ b/cpplint_unittest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2009 Google Inc. All rights reserved. # From 26af9717305c4e631b8cd582c3dbde692565509e Mon Sep 17 00:00:00 2001 From: Geoffrey Viola Date: Wed, 9 Jul 2025 11:13:46 -0400 Subject: [PATCH 5/7] granular build/namespaces categories Enhance the using directives check by adding more granularity to the warnings. The warnings are split into multiple categories 1. block/namespace scope 2. source/header file 3. literal/nonliteral namespace Possible filter combinations would look like the following 1. Google: None 2. CppCoreGuidelines 1. -build/namespaces/header/block 2. -build/namespaces/source 3. Like CppCoreGuidlines, but disallow non-literal namespaces in source files at namespace scope 1. -build/namespaces/header/block 2. -build/namespaces/source/block 3. -build/namespaces/source/namespace/literal Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- cpplint.py | 71 ++++++++++++----- cpplint_unittest.py | 83 ++++++++++++++++---- samples/silly-sample/filters.def | 2 +- samples/silly-sample/includeorder_cfirst.def | 2 +- samples/silly-sample/sed.def | 2 +- samples/silly-sample/simple.def | 2 +- 6 files changed, 124 insertions(+), 38 deletions(-) diff --git a/cpplint.py b/cpplint.py index e400b05..24897da 100755 --- a/cpplint.py +++ b/cpplint.py @@ -308,8 +308,14 @@ "build/include_order", "build/include_what_you_use", "build/namespaces_headers", - "build/namespaces_literals", - "build/namespaces", + "build/namespaces/header/block/literals", + "build/namespaces/header/block/nonliterals", + "build/namespaces/header/namespace/literals", + "build/namespaces/header/namespace/nonliterals", + "build/namespaces/source/block/literals", + "build/namespaces/source/block/nonliterals", + "build/namespaces/source/namespace/literals", + "build/namespaces/source/namespace/nonliterals", "build/printf_format", "build/storage_class", "legal/copyright", @@ -935,6 +941,16 @@ "Missing space after ,": r"s/,\([^ ]\)/, \1/g", } +# Used for backwards compatibility and ease of use +_FILTER_SHORTCUTS = { + "build/namespaces_literals": [ + "build/namespaces/header/block/literals", + "build/namespaces/header/namespace/literals", + "build/namespaces/source/block/literals", + "build/namespaces/source/namespace/literals", + ] +} + # The root directory used for deriving header guard CPP variable. # This is set by --root flag. _root = None @@ -1457,7 +1473,12 @@ def AddFilters(self, filters): for filt in filters.split(","): clean_filt = filt.strip() if clean_filt: - self.filters.append(clean_filt) + if len(clean_filt) > 1 and clean_filt[1:] in _FILTER_SHORTCUTS: + starting_char = clean_filt[0] + new_filters = [starting_char + x for x in _FILTER_SHORTCUTS[clean_filt[1:]]] + self.filters.extend(new_filters) + else: + self.filters.append(clean_filt) for filt in self.filters: if not filt.startswith(("+", "-")): msg = f"Every filter in --filters must start with + or - ({filt} does not)" @@ -3287,6 +3308,14 @@ def InAsmBlock(self): """ return self.stack and self.stack[-1].inline_asm != _NO_ASM + def InBlockScope(self): + """Check if we are currently one level inside a block scope. + + Returns: + True if top of the stack is a block scope, False otherwise. + """ + return len(self.stack) > 0 and not isinstance(self.stack[-1], _NamespaceInfo) + def InTemplateArgumentList(self, clean_lines, linenum, pos): """Check if current position is inside template argument list. @@ -6021,22 +6050,26 @@ def CheckLanguage( ) if re.search(r"\busing namespace\b", line): - if re.search(r"\bliterals\b", line): - error( - filename, - linenum, - "build/namespaces_literals", - 5, - "Do not use namespace using-directives. Use using-declarations instead.", - ) - else: - error( - filename, - linenum, - "build/namespaces", - 5, - "Do not use namespace using-directives. Use using-declarations instead.", - ) + is_literals = re.search(r"\bliterals\b", line) is not None + is_header = not _IsSourceExtension(file_extension) + file_type = "header" if is_header else "source" + + # Check for the block scope for multiline blocks. + # Check if the line starts with the using directive as a heuristic in case it's all one line + is_block_scope = nesting_state.InBlockScope() or not line.startswith("using namespace") + + scope_type = "block" if is_block_scope else "namespace" + literal_type = "literals" if is_literals else "nonliterals" + + specific_category = f"build/namespaces/{file_type}/{scope_type}/{literal_type}" + + error( + filename, + linenum, + specific_category, + 5, + "Do not use namespace using-directives. Use using-declarations instead.", + ) # Detect variable-length arrays. match = re.match(r"\s*(.+::)?(\w+) [a-z]\w*\[(.+)];", line) diff --git a/cpplint_unittest.py b/cpplint_unittest.py index e36093f..574e5fd 100755 --- a/cpplint_unittest.py +++ b/cpplint_unittest.py @@ -3640,7 +3640,7 @@ def DoTest(self, lines): assert ( error_collector.Results().count( "Do not use namespace using-directives. Use using-declarations instead. " - "[build/namespaces] [5]" + "[build/namespaces/source/namespace/nonliterals] [5]" ) == 1 ) @@ -3649,20 +3649,6 @@ def DoTest(self, lines): DoTest(self, ["", "", "", "using namespace foo;"]) DoTest(self, ["// hello", "using namespace foo;"]) - def testUsingLiteralsNamespaces(self): - self.TestLint( - "using namespace std::literals;", - "Do not use namespace" - " using-directives. Use using-declarations instead." - " [build/namespaces_literals] [5]", - ) - self.TestLint( - "using namespace std::literals::chrono_literals;", - "Do" - " not use namespace using-directives. Use using-declarations instead." - " [build/namespaces_literals] [5]", - ) - def testNewlineAtEOF(self): def DoTest(self, data, is_missing_eof): error_collector = ErrorCollector(self.assertTrue) @@ -4203,6 +4189,73 @@ def testEndOfNamespaceComments(self): == 0 ) + def testUsingNamespacesGranular(self): + """Test granular using namespace checks for different contexts.""" + + self.TestLanguageRulesCheck( + "foo.h", + "using namespace std;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/namespace/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.h", + "using namespace std::chrono::literals;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/namespace/literals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "using namespace std;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/namespace/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "using namespace std::chrono::literals;", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/namespace/literals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.h", + "{ using namespace std; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/block/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.h", + "{ using namespace std::chrono::literals; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/header/block/literals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "{ using namespace std; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/block/nonliterals] [5]", + ) + + self.TestLanguageRulesCheck( + "foo.cc", + "{ using namespace std::chrono::literals; }", + "Do not use namespace using-directives. " + "Use using-declarations instead." + " [build/namespaces/source/block/literals] [5]", + ) + def testComma(self): self.TestLint("a = f(1,2);", "Missing space after , [whitespace/comma] [3]") self.TestLint( diff --git a/samples/silly-sample/filters.def b/samples/silly-sample/filters.def index e0fac32..5780b3e 100644 --- a/samples/silly-sample/filters.def +++ b/samples/silly-sample/filters.def @@ -13,7 +13,7 @@ src/sillycode.cpp:1: Include the directory when naming header files [build/inc src/sillycode.cpp:2: is an unapproved C++11 header. [build/c++11] [5] src/sillycode.cpp:3: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] src/sillycode.cpp:4: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] -src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5] +src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces/source/namespace/nonliterals] [5] src/sillycode.cpp:40: If/else bodies with multiple statements require braces [readability/braces] [4] src/sillycode.cpp:66: Single-parameter constructors should be marked explicit. [runtime/explicit] [4] src/sillycode.cpp:76: Single-parameter constructors should be marked explicit. [runtime/explicit] [4] diff --git a/samples/silly-sample/includeorder_cfirst.def b/samples/silly-sample/includeorder_cfirst.def index a3b30a4..9be5e44 100644 --- a/samples/silly-sample/includeorder_cfirst.def +++ b/samples/silly-sample/includeorder_cfirst.def @@ -11,7 +11,7 @@ src/sillycode.cpp:2: Should have a space between // and comment [whitespace/co src/sillycode.cpp:2: is an unapproved C++11 header. [build/c++11] [5] src/sillycode.cpp:3: Found other system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] src/sillycode.cpp:4: Found other system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] -src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5] +src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces/source/namespace/nonliterals] [5] src/sillycode.cpp:8: public: should be indented +1 space inside class Date [whitespace/indent] [3] src/sillycode.cpp:15: { should almost always be at the end of the previous line [whitespace/braces] [4] src/sillycode.cpp:39: { should almost always be at the end of the previous line [whitespace/braces] [4] diff --git a/samples/silly-sample/sed.def b/samples/silly-sample/sed.def index b3778ee..850a16d 100644 --- a/samples/silly-sample/sed.def +++ b/samples/silly-sample/sed.def @@ -15,7 +15,7 @@ sed -i '249s/\([^ ]\){/\1 {/' src/sillycode.cpp # Missing space before { [white # src/sillycode.cpp:2: " is an unapproved C++11 header." [build/c++11] [5] # src/sillycode.cpp:3: "Found C system header after other header. Should be: sillycode.h, c system, c++ system, other." [build/include_order] [4] # src/sillycode.cpp:4: "Found C system header after other header. Should be: sillycode.h, c system, c++ system, other." [build/include_order] [4] -# src/sillycode.cpp:5: "Do not use namespace using-directives. Use using-declarations instead." [build/namespaces] [5] +# src/sillycode.cpp:5: "Do not use namespace using-directives. Use using-declarations instead." [build/namespaces/source/namespace/nonliterals] [5] # src/sillycode.cpp:8: "public: should be indented +1 space inside class Date" [whitespace/indent] [3] # src/sillycode.cpp:15: "{ should almost always be at the end of the previous line" [whitespace/braces] [4] # src/sillycode.cpp:39: "{ should almost always be at the end of the previous line" [whitespace/braces] [4] diff --git a/samples/silly-sample/simple.def b/samples/silly-sample/simple.def index 7bc1dc6..b7ce111 100644 --- a/samples/silly-sample/simple.def +++ b/samples/silly-sample/simple.def @@ -12,7 +12,7 @@ src/sillycode.cpp:2: Should have a space between // and comment [whitespace/co src/sillycode.cpp:2: is an unapproved C++11 header. [build/c++11] [5] src/sillycode.cpp:3: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] src/sillycode.cpp:4: Found C system header after other header. Should be: sillycode.h, c system, c++ system, other. [build/include_order] [4] -src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5] +src/sillycode.cpp:5: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces/source/namespace/nonliterals] [5] src/sillycode.cpp:8: public: should be indented +1 space inside class Date [whitespace/indent] [3] src/sillycode.cpp:15: { should almost always be at the end of the previous line [whitespace/braces] [4] src/sillycode.cpp:39: { should almost always be at the end of the previous line [whitespace/braces] [4] From c2b1ce67ba8f2e9cd02eb1edbea24b59eadba640 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 02:17:01 +0000 Subject: [PATCH 6/7] Bump sigstore/gh-action-sigstore-python in the github-actions group Bumps the github-actions group with 1 update: [sigstore/gh-action-sigstore-python](https://github.com/sigstore/gh-action-sigstore-python). Updates `sigstore/gh-action-sigstore-python` from 3.0.0 to 3.0.1 - [Release notes](https://github.com/sigstore/gh-action-sigstore-python/releases) - [Changelog](https://github.com/sigstore/gh-action-sigstore-python/blob/main/CHANGELOG.md) - [Commits](https://github.com/sigstore/gh-action-sigstore-python/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: sigstore/gh-action-sigstore-python dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index edf2366..5e79bcd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,7 +70,7 @@ jobs: name: python-package-distributions path: dist/ - name: Sign the dists with Sigstore - uses: sigstore/gh-action-sigstore-python@v3.0.0 + uses: sigstore/gh-action-sigstore-python@v3.0.1 with: inputs: >- ./dist/*.tar.gz From 2eef70dadc16b5e9578931f26e6ad9da23a7b400 Mon Sep 17 00:00:00 2001 From: Geoffrey Viola Date: Fri, 18 Jul 2025 14:45:51 -0400 Subject: [PATCH 7/7] Allow specifying _third_party_headers_pattern (#388) --- cpplint.py | 41 +++++++++++++++++++++++++---------- cpplint_clitest.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/cpplint.py b/cpplint.py index 24897da..ad942ba 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,16 @@ # 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""" @@ -1072,6 +1077,15 @@ def ProcessIncludeOrderOption(val): 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 +5758,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 +5811,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 +7541,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 +7727,7 @@ def ParseArguments(args): "exclude=", "recursive", "headers=", + "third_party_headers=", "includeorder=", "config=", "quiet", @@ -7770,6 +7787,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..165e192 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,57 @@ def test_codelite_sample(self): self.check_all_in_folder("./samples/codelite-sample", 1) +# Tests for third_party_headers option +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, 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(""" + // 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)) + # Expect a warning about include_subdir + 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" + """) + ) + # Write configuration file to override third_party_headers + config = tmp_path / "CPPLINT.cfg" + config.write_text("third_party_headers=^Bar.h$\n") + 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 == 1, f"stdout\n{out.decode('utf-8')}\nstderr\n{err.decode('utf-8')}" + assert b"build/include_subdir" in err + + if __name__ == "__main__": pytest.main([__file__])