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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7eb37f3..626923c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,13 +54,13 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.4 + rev: v0.12.3 hooks: - id: ruff - id: ruff-format - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.5.1 + rev: v2.6.0 hooks: - id: pyproject-fmt 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..ad942ba 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. # @@ -61,14 +61,14 @@ # 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] [--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 @@ -308,8 +311,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", @@ -806,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")$" @@ -935,6 +936,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 @@ -965,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""" @@ -1056,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() @@ -1457,7 +1487,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 +3322,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. @@ -5715,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, @@ -5768,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: @@ -6021,22 +6064,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) @@ -7494,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: @@ -7678,6 +7727,7 @@ def ParseArguments(args): "exclude=", "recursive", "headers=", + "third_party_headers=", "includeorder=", "config=", "quiet", @@ -7737,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 b20566f..165e192 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. # @@ -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__]) diff --git a/cpplint_unittest.py b/cpplint_unittest.py index 730babd..574e5fd 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. # @@ -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] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy