Skip to content

Commit 5ae674c

Browse files
committed
fix(indentation_namespace): false positive for MemInitLists
Fix whitespace/indentation_namespace false positive for member initializer lists (MemInitLists) Adds new _ConstructorInfo, _WrappedInfo, and _MemInitListInfo nesting stack element classes Remember last-popped nesting stack item Update descriptor.pb.cc to 2016-12-16 version (commit 183d31c)
1 parent a046f9c commit 5ae674c

File tree

6 files changed

+6949
-7142
lines changed

6 files changed

+6949
-7142
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ Changelog
66
===========
77

88
* Python versions less than 3.9 are no longer supported.
9-
* The false positive for indented function parameters in namespaces was eradicated. (https://github.com/cpplint/cpplint/pull/304)
10-
* build/include-what-you-use now recognizes c-style headers, such as <stdio.h> for symbols from <cstdio>. (https://github.com/cpplint/cpplint/pull/319)
9+
* The false positive for indented member initializer lists in namespaces were eradicated. (https://github.com/cpplint/cpplint/pull/353)
1110
* The warning on non-const references (runtime/references) is now disabled by default pursuant to the May 2020 Google style guide update. (https://github.com/cpplint/cpplint/pull/305)
1211

1312
2.0.1 (2025-03-09)

cpplint.py

Lines changed: 136 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,6 +3097,14 @@ def CheckEnd(self, filename, clean_lines, linenum, error):
30973097
)
30983098

30993099

3100+
class _ConstructorInfo(_BlockInfo):
3101+
"""Stores information about a constructor.
3102+
For detecting member initializer lists."""
3103+
3104+
def __init__(self, linenum: int):
3105+
_BlockInfo.__init__(self, linenum, seen_open_brace=False)
3106+
3107+
31003108
class _NamespaceInfo(_BlockInfo):
31013109
"""Stores information about a namespace."""
31023110

@@ -3174,6 +3182,22 @@ def CheckEnd(self, filename, clean_lines, linenum, error):
31743182
)
31753183

31763184

3185+
class _WrappedInfo(_BlockInfo):
3186+
"""Stores information about parentheses, initializer lists, etc.
3187+
Not exactly a block but we do need the same signature.
3188+
Needed to avoid namespace indentation false positives,
3189+
though parentheses tracking would slow us down a lot
3190+
and is effectively already done by open_parentheses."""
3191+
3192+
pass
3193+
3194+
3195+
class _MemInitListInfo(_WrappedInfo):
3196+
"""Stores information about member initializer lists."""
3197+
3198+
pass
3199+
3200+
31773201
class _PreprocessorInfo:
31783202
"""Stores checkpoints of nesting stacks when #if/#else is seen."""
31793203

@@ -3216,6 +3240,9 @@ def __init__(self):
32163240
# We can't use previous_stack_top, a shallow copy whose open_parentheses value is updated.
32173241
self.previous_open_parentheses = 0
32183242

3243+
# The last stack item we popped.
3244+
self.popped_top: _BlockInfo | None = None
3245+
32193246
# Stack of _PreprocessorInfo objects.
32203247
self.pp_stack = []
32213248

@@ -3370,6 +3397,9 @@ def UpdatePreprocessor(self, line):
33703397
# TODO(google): unexpected #endif, issue warning?
33713398
pass
33723399

3400+
def _Pop(self):
3401+
"""Pop the innermost state (top of the stack) and remember the popped item."""
3402+
self.popped_top = self.stack.pop()
33733403

33743404
def _CountOpenParentheses(self, line: str):
33753405
# Count parentheses. This is to avoid adding struct arguments to
@@ -3423,6 +3453,22 @@ def _UpdateNamesapce(self, line: str, linenum: int) -> str | None:
34233453
line = line[line.find("{") + 1 :]
34243454
return line
34253455

3456+
def _UpdateConstructor(self, line: str, linenum: int, class_name: str | None = None):
3457+
"""
3458+
Check if the given line is a constructor.
3459+
Args:
3460+
line: Line to check.
3461+
class_name: If line checked is inside of a class block, a str of the class's name;
3462+
otherwise, None.
3463+
"""
3464+
if not class_name:
3465+
if not re.match(r"\s*(\w*)\s*::\s*\1\s*\(", line):
3466+
return
3467+
elif not re.match(rf"\s*{re.escape(class_name)}\s*\(", line):
3468+
return
3469+
3470+
self.stack.append(_ConstructorInfo(linenum))
3471+
34263472
# TODO(google): Update() is too long, but we will refactor later.
34273473
def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error):
34283474
"""Update nesting state with current line.
@@ -3492,36 +3538,56 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
34923538
if not self.SeenOpenBrace():
34933539
self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)
34943540

3495-
# Update access control if we are inside a class/struct
3541+
# Update access control if we are directly inside a class/struct
34963542
if self.stack and isinstance(self.stack[-1], _ClassInfo):
3497-
classinfo = self.stack[-1]
3498-
access_match = re.match(
3499-
r"^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?"
3500-
r":(?:[^:]|$)",
3501-
line,
3543+
if self.stack[-1].seen_open_brace:
3544+
classinfo: _ClassInfo = self.stack[-1]
3545+
# Update access control
3546+
if access_match := re.match(
3547+
r"^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?"
3548+
r":([^:].*|$)",
3549+
line,
3550+
):
3551+
classinfo.access = access_match.group(2)
3552+
3553+
# Check that access keywords are indented +1 space. Skip this
3554+
# check if the keywords are not preceded by whitespaces.
3555+
indent = access_match.group(1)
3556+
if len(indent) != classinfo.class_indent + 1 and re.match(r"^\s*$", indent):
3557+
if classinfo.is_struct:
3558+
parent = "struct " + classinfo.name
3559+
else:
3560+
parent = "class " + classinfo.name
3561+
slots = ""
3562+
if access_match.group(3):
3563+
slots = access_match.group(3)
3564+
error(
3565+
filename,
3566+
linenum,
3567+
"whitespace/indent",
3568+
3,
3569+
f"{access_match.group(2)}{slots}:"
3570+
f" should be indented +1 space inside {parent}",
3571+
)
3572+
3573+
line = access_match.group(4)
3574+
else:
3575+
self._UpdateConstructor(line, linenum, class_name=classinfo.name)
3576+
else: # Not in class
3577+
self._UpdateConstructor(line, linenum)
3578+
3579+
# If brace not open and we just finished a parenthetical definition,
3580+
# check if we're in a member initializer list following a constructor.
3581+
if (
3582+
self.stack
3583+
and (
3584+
isinstance(self.stack[-1], _ConstructorInfo)
3585+
or isinstance(self.previous_stack_top, _ConstructorInfo)
35023586
)
3503-
if access_match:
3504-
classinfo.access = access_match.group(2)
3505-
3506-
# Check that access keywords are indented +1 space. Skip this
3507-
# check if the keywords are not preceded by whitespaces.
3508-
indent = access_match.group(1)
3509-
if len(indent) != classinfo.class_indent + 1 and re.match(r"^\s*$", indent):
3510-
if classinfo.is_struct:
3511-
parent = "struct " + classinfo.name
3512-
else:
3513-
parent = "class " + classinfo.name
3514-
slots = ""
3515-
if access_match.group(3):
3516-
slots = access_match.group(3)
3517-
error(
3518-
filename,
3519-
linenum,
3520-
"whitespace/indent",
3521-
3,
3522-
f"{access_match.group(2)}{slots}:"
3523-
f" should be indented +1 space inside {parent}",
3524-
)
3587+
and not self.stack[-1].seen_open_brace
3588+
and re.search(r"[^:]:[^:]", line)
3589+
):
3590+
self.stack.append(_MemInitListInfo(linenum, seen_open_brace=False))
35253591

35263592
# Consume braces or semicolons from what's left of the line
35273593
while True:
@@ -3532,34 +3598,42 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
35323598

35333599
token = matched.group(1)
35343600
if token == "{":
3535-
# If namespace or class hasn't seen a opening brace yet, mark
3601+
# If namespace or class hasn't seen an opening brace yet, mark
35363602
# namespace/class head as complete. Push a new block onto the
35373603
# stack otherwise.
35383604
if not self.SeenOpenBrace():
3605+
# End of initializer list wrap if present
3606+
if isinstance(self.stack[-1], _MemInitListInfo):
3607+
self._Pop()
35393608
self.stack[-1].seen_open_brace = True
35403609
elif re.match(r'^extern\s*"[^"]*"\s*\{', line):
35413610
self.stack.append(_ExternCInfo(linenum))
35423611
else:
35433612
self.stack.append(_BlockInfo(linenum, True))
35443613
if _MATCH_ASM.match(line):
35453614
self.stack[-1].inline_asm = _BLOCK_ASM
3546-
3547-
elif token in {";", ")"}:
3615+
elif token == ";":
35483616
# If we haven't seen an opening brace yet, but we already saw
35493617
# a semicolon, this is probably a forward declaration. Pop
35503618
# the stack for these.
3551-
#
3619+
if not self.SeenOpenBrace():
3620+
self._Pop()
3621+
elif token == ")":
35523622
# Similarly, if we haven't seen an opening brace yet, but we
35533623
# already saw a closing parenthesis, then these are probably
35543624
# function arguments with extra "class" or "struct" keywords.
35553625
# Also pop these stack for these.
3556-
if not self.SeenOpenBrace():
3557-
self.stack.pop()
3626+
if (
3627+
self.stack
3628+
and not self.stack[-1].seen_open_brace
3629+
and isinstance(self.stack[-1], _ClassInfo)
3630+
):
3631+
self._Pop()
35583632
else: # token == '}'
35593633
# Perform end of block checks and pop the stack.
35603634
if self.stack:
35613635
self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
3562-
self.stack.pop()
3636+
self._Pop()
35633637
line = matched.group(2)
35643638

35653639
def InnermostClass(self):
@@ -7097,11 +7171,11 @@ def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):
70977171

70987172
# Returns true if we are at a new block, and it is directly
70997173
# inside of a namespace.
7100-
def IsBlockInNameSpace(nesting_state, is_forward_declaration):
7174+
def IsBlockInNameSpace(nesting_state: NestingState, is_forward_declaration: bool): # noqa: FBT001
71017175
"""Checks that the new block is directly in a namespace.
71027176
71037177
Args:
7104-
nesting_state: The _NestingState object that contains info about our state.
7178+
nesting_state: The NestingState object that contains info about our state.
71057179
is_forward_declaration: If the class is a forward declared class.
71067180
Returns:
71077181
Whether or not the new block is directly in a namespace.
@@ -7117,7 +7191,13 @@ def IsBlockInNameSpace(nesting_state, is_forward_declaration):
71177191
if (
71187192
len(nesting_state.stack) > 1
71197193
and isinstance(nesting_state.previous_stack_top, _NamespaceInfo)
7120-
and isinstance(nesting_state.stack[-2], _NamespaceInfo)
7194+
and (
7195+
isinstance(nesting_state.stack[-2], _NamespaceInfo)
7196+
or len(nesting_state.stack) > 2 # Accommodate for WrappedInfo
7197+
and issubclass(type(nesting_state.stack[-1]), _WrappedInfo)
7198+
and not nesting_state.stack[-2].seen_open_brace
7199+
and isinstance(nesting_state.stack[-3], _NamespaceInfo)
7200+
)
71217201
):
71227202
return True
71237203
return False
@@ -7141,6 +7221,10 @@ def ShouldCheckNamespaceIndentation(
71417221
only works for classes and namespaces inside of a namespace.
71427222
"""
71437223

7224+
# Required by all checks involving nesting_state
7225+
if not nesting_state.stack:
7226+
return False
7227+
71447228
is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, linenum)
71457229

71467230
if not (is_namespace_indent_item or is_forward_declaration):
@@ -7154,6 +7238,20 @@ def ShouldCheckNamespaceIndentation(
71547238
if nesting_state.previous_stack_top and nesting_state.previous_open_parentheses > 0:
71557239
return False
71567240

7241+
# Skip if we are extra-indenting a member initializer list.
7242+
if (
7243+
isinstance(nesting_state.previous_stack_top, _ConstructorInfo) # F/N (A::A() : _a(0) {/{})
7244+
and (
7245+
isinstance(nesting_state.stack[-1], _MemInitListInfo)
7246+
or isinstance(nesting_state.popped_top, _MemInitListInfo)
7247+
)
7248+
) or ( # popping constructor after MemInitList on the same line (: _a(a) {})
7249+
isinstance(nesting_state.previous_stack_top, _ConstructorInfo)
7250+
and isinstance(nesting_state.popped_top, _ConstructorInfo)
7251+
and re.search(r"[^:]:[^:]", raw_lines_no_comments[linenum])
7252+
):
7253+
return False
7254+
71577255
return IsBlockInNameSpace(nesting_state, is_forward_declaration)
71587256

71597257

cpplint_unittest.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,54 @@ def testNamespaceIndentationIndentedParameter(self):
323323
results = self.GetNamespaceResults(lines)
324324
assert results == ""
325325

326+
def testNamespaceIndentationMemberInitializerList(self):
327+
lines = [
328+
"namespace Opossum {",
329+
"",
330+
"Acme::Acme(const std::shared_ptr<const AbstractOperator> left,",
331+
" const std::shared_ptr<const AbstractOperator> nigh)",
332+
" : _left(left), _behind(nigh) {}",
333+
"",
334+
"} // namespace Opossum",
335+
]
336+
assert self.GetNamespaceResults(lines) == ""
337+
338+
# Multiline member initializer List
339+
lines = [
340+
"namespace Rosenfield {",
341+
"class Crush : public Habitual {",
342+
" public:",
343+
" Crush() : _a(1),",
344+
" _b(2)",
345+
" {}",
346+
"};",
347+
"} // namespace Rosenfield",
348+
]
349+
assert self.GetNamespaceResults(lines) == ""
350+
351+
# Same line as constructor declaration
352+
lines = [
353+
"namespace Boucher {",
354+
" X::X() : _s(2) {}",
355+
" A::A() : _e(12) {",
356+
]
357+
assert self.GetNamespaceResults(lines) == [
358+
"Do not indent within a namespace. [whitespace/indent_namespace] [4]",
359+
"Do not indent within a namespace. [whitespace/indent_namespace] [4]",
360+
]
361+
362+
# Definition on indented line
363+
lines = [
364+
"namespace Store {",
365+
"",
366+
" Color::Color() : my_name_is('b')",
367+
" this_is_true(true) {",
368+
]
369+
assert (
370+
self.GetNamespaceResults(lines)
371+
== "Do not indent within a namespace. [whitespace/indent_namespace] [4]"
372+
)
373+
326374
def testNestingInNamespace(self):
327375
lines = [
328376
"namespace Test {",

samples/boost-sample/headers_inspect_exclude.def

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
1
33
3
44
Done processing src/inspect/unnamed_namespace_check.hpp
5-
Total errors found: 50
5+
Total errors found: 49
66

77
src/inspect/unnamed_namespace_check.hpp:0: No #ifndef header guard found, suggested CPP variable is: SAMPLES_BOOST_SAMPLE_SRC_INSPECT_UNNAMED_NAMESPACE_CHECK_HPP_ [build/header_guard] [5]
88
src/inspect/unnamed_namespace_check.hpp:11: Include the directory when naming header files [build/include_subdir] [4]
@@ -18,7 +18,6 @@ src/inspect/unnamed_namespace_check.hpp:28: Do not indent within a namespace.
1818
src/inspect/unnamed_namespace_check.hpp:28: { should almost always be at the end of the previous line [whitespace/braces] [4]
1919
src/inspect/unnamed_namespace_check.hpp:29: Do not indent within a namespace. [whitespace/indent_namespace] [4]
2020
src/inspect/unnamed_namespace_check.hpp:29: Weird number of spaces at line-start. Are you using a 2-space indent? [whitespace/indent] [3]
21-
src/inspect/unnamed_namespace_check.hpp:30: Do not indent within a namespace. [whitespace/indent_namespace] [4]
2221
src/inspect/unnamed_namespace_check.hpp:30: Weird number of spaces at line-start. Are you using a 2-space indent? [whitespace/indent] [3]
2322
src/inspect/unnamed_namespace_check.hpp:30: { should almost always be at the end of the previous line [whitespace/braces] [4]
2423
src/inspect/unnamed_namespace_check.hpp:31: Extra space after ( in function call [whitespace/parens] [4]

0 commit comments

Comments
 (0)
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