Skip to content

Commit 322fc9e

Browse files
committed
fix(whitespace/indent_namespace): Specifier & multiline false positives for constructors
Fix false positive for when a function specifier (e.g. `inline`, `protected`) is provided Fix false positive for when constructor ends on the same line a MemInitList does, but on a different line from said list's start Fix ownership assignment of open parentheses Use search instead of match for some cases Fix typo & misc refactoring
1 parent 4df337a commit 322fc9e

File tree

2 files changed

+95
-32
lines changed

2 files changed

+95
-32
lines changed

cpplint.py

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3230,7 +3230,17 @@ class _WrappedInfo(_BlockInfo):
32303230
class _MemInitListInfo(_WrappedInfo):
32313231
"""Stores information about member initializer lists."""
32323232

3233-
pass
3233+
def __init__(self, linenum):
3234+
_WrappedInfo.__init__(self, linenum, False)
3235+
# For `: a(b)`, a is the identifier
3236+
self.expecting_identifier = True
3237+
# Whether we are expecting the value that follows the identifier
3238+
self.expecting_braces = False
3239+
# Whether we are currently within braces
3240+
self.in_braces = False
3241+
# Only used when within braces
3242+
self.is_curly = False
3243+
self.open_brace_count = 0
32343244

32353245

32363246
class _PreprocessorInfo:
@@ -3384,6 +3394,20 @@ def InTemplateArgumentList(self, clean_lines, linenum, pos):
33843394
pos = end_pos
33853395
return False
33863396

3397+
def _BequeathParensAndAppend(self, appendee: _BlockInfo, parens_changed: int):
3398+
"""Pass down number of open parentheses and append to the stack.
3399+
3400+
Args:
3401+
appendee: The object to append to the stack.
3402+
parens_changed: The number of parentheses opened, as computed by _ChangeParentheses().
3403+
"""
3404+
# Compensate for adding parentheses to the previous top block before this update
3405+
if self.stack:
3406+
self.stack[-1].open_parentheses -= parens_changed
3407+
appendee.open_parentheses += parens_changed
3408+
3409+
self.stack.append(appendee)
3410+
33873411
def UpdatePreprocessor(self, line):
33883412
"""Update preprocessor stack.
33893413
@@ -3444,9 +3468,15 @@ def _Pop(self):
34443468
"""Pop the innermost state (top of the stack) and remember the popped item."""
34453469
self.popped_top = self.stack.pop()
34463470

3447-
def _CountOpenParentheses(self, line: str):
3448-
# Count parentheses. This is to avoid adding struct arguments to
3449-
# the nesting stack.
3471+
def _ChangeParentheses(self, line: str) -> int:
3472+
"""
3473+
Count parentheses. This is to avoid adding struct arguments to the nesting stack.
3474+
Args:
3475+
line: The line to count parentheses on.
3476+
3477+
Returns:
3478+
The number of parentheses opened on this line.
3479+
"""
34503480
if self.stack:
34513481
inner_block = self.stack[-1]
34523482
depth_change = line.count("(") - line.count(")")
@@ -3469,7 +3499,10 @@ def _CountOpenParentheses(self, line: str):
34693499
# Exit assembly block
34703500
inner_block.inline_asm = _END_ASM
34713501

3472-
def _UpdateNamesapce(self, line: str, linenum: int) -> str | None:
3502+
return depth_change
3503+
return 0
3504+
3505+
def _ConsumeNamespace(self, line: str, linenum: int) -> str | None:
34733506
"""
34743507
Match start of namespace, append to stack, and consume line
34753508
Args:
@@ -3496,21 +3529,29 @@ def _UpdateNamesapce(self, line: str, linenum: int) -> str | None:
34963529
line = line[line.find("{") + 1 :]
34973530
return line
34983531

3499-
def _UpdateConstructor(self, line: str, linenum: int, class_name: str | None = None):
3532+
def _UpdateConstructor(
3533+
self, line: str, linenum: int, parens_changed: int, class_name: str | None = None
3534+
) -> bool:
35003535
"""
35013536
Check if the given line is a constructor.
35023537
Args:
35033538
line: Line to check.
35043539
class_name: If line checked is inside of a class block, a str of the class's name;
35053540
otherwise, None.
3541+
parens_changed: The number of parentheses opened on this line.
3542+
3543+
Returns:
3544+
Whether the given line was a constructor.
35063545
"""
3546+
prefix = r"(?:^|public|private|protected|friend|inline|explicit|constexpr)\s*"
35073547
if not class_name:
3508-
if not re.match(r"\s*(\w*)\s*::\s*\1\s*\(", line):
3509-
return
3510-
elif not re.match(rf"\s*{re.escape(class_name)}\s*\(", line):
3511-
return
3548+
if not re.search(prefix + r"(\w*)\s*::\s*\1\s*\(", line):
3549+
return False
3550+
elif not re.search(prefix + rf"{re.escape(class_name)}\s*\(", line):
3551+
return False
35123552

3513-
self.stack.append(_ConstructorInfo(linenum))
3553+
self._BequeathParensAndAppend(_ConstructorInfo(linenum))
3554+
return True
35143555

35153556
# TODO(google): Update() is too long, but we will refactor later.
35163557
def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error):
@@ -3538,12 +3579,12 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
35383579
# Update pp_stack
35393580
self.UpdatePreprocessor(line)
35403581

3541-
self._CountOpenParentheses(line)
3582+
parens_changed = self._ChangeParentheses(line)
35423583

35433584
# Consume namespace declaration at the beginning of the line. Do
35443585
# this in a loop so that we catch same line declarations like this:
35453586
# namespace proto2 { namespace bridge { class MessageSet; } }
3546-
while (new_line := self._UpdateNamesapce(line, linenum)) is not None: # could be empty str
3587+
while (new_line := self._ConsumeNamespace(line, linenum)) is not None: # could be empty str
35473588
line = new_line
35483589

35493590
# Look for a class declaration in whatever is left of the line
@@ -3616,8 +3657,11 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
36163657
line = access_match.group(4)
36173658
else:
36183659
self._UpdateConstructor(line, linenum, class_name=classinfo.name)
3660+
self._UpdateConstructor(
3661+
line, linenum, parens_changed, class_name=classinfo.name
3662+
)
36193663
else: # Not in class
3620-
self._UpdateConstructor(line, linenum)
3664+
self._UpdateConstructor(line, linenum, parens_changed)
36213665

36223666
# If brace not open and we just finished a parenthetical definition,
36233667
# check if we're in a member initializer list following a constructor.
@@ -3630,16 +3674,16 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
36303674
and not self.stack[-1].seen_open_brace
36313675
and re.search(r"[^:]:[^:]", line)
36323676
):
3633-
self.stack.append(_MemInitListInfo(linenum, seen_open_brace=False))
3677+
self._BequeathParensAndAppend(_MemInitListInfo(linenum), parens_changed)
36343678

36353679
# Consume braces or semicolons from what's left of the line
36363680
while True:
36373681
# Match first brace, semicolon, or closed parenthesis.
3638-
matched = re.match(r"^[^{;)}]*([{;)}])(.*)$", line)
3639-
if not matched:
3682+
searched = re.search(r"([{;)}])", line)
3683+
if not searched:
36403684
break
36413685

3642-
token = matched.group(1)
3686+
token = searched.group(1)
36433687
if token == "{":
36443688
# If namespace or class hasn't seen an opening brace yet, mark
36453689
# namespace/class head as complete. Push a new block onto the
@@ -3677,7 +3721,7 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
36773721
if self.stack:
36783722
self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
36793723
self._Pop()
3680-
line = matched.group(2)
3724+
line = line[searched.end(0) :]
36813725

36823726
def InnermostClass(self):
36833727
"""Get class info on the top of the stack.
@@ -7294,15 +7338,22 @@ def ShouldCheckNamespaceIndentation(
72947338

72957339
# Skip if we are extra-indenting a member initializer list.
72967340
if (
7297-
isinstance(nesting_state.previous_stack_top, _ConstructorInfo) # F/N (A::A() : _a(0) {/{})
7298-
and (
7299-
isinstance(nesting_state.stack[-1], _MemInitListInfo)
7300-
or isinstance(nesting_state.popped_top, _MemInitListInfo)
7341+
(
7342+
isinstance(nesting_state.previous_stack_top, _ConstructorInfo) # F/N (A::A() : _a(0){)
7343+
and (
7344+
isinstance(nesting_state.stack[-1], _MemInitListInfo)
7345+
or isinstance(nesting_state.popped_top, _MemInitListInfo)
7346+
)
7347+
)
7348+
or ( # empty constructor in multiline list
7349+
isinstance(nesting_state.previous_stack_top, _MemInitListInfo)
7350+
and isinstance(nesting_state.popped_top, _ConstructorInfo)
7351+
)
7352+
or ( # popping constructor after MemInitList on the same line (: _a(a) {})
7353+
isinstance(nesting_state.previous_stack_top, _ConstructorInfo)
7354+
and isinstance(nesting_state.popped_top, _ConstructorInfo)
7355+
and re.search(r"[^:]:[^:]", raw_lines_no_comments[linenum])
73017356
)
7302-
) or ( # popping constructor after MemInitList on the same line (: _a(a) {})
7303-
isinstance(nesting_state.previous_stack_top, _ConstructorInfo)
7304-
and isinstance(nesting_state.popped_top, _ConstructorInfo)
7305-
and re.search(r"[^:]:[^:]", raw_lines_no_comments[linenum])
73067357
):
73077358
return False
73087359

cpplint_unittest.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -327,27 +327,39 @@ def testNamespaceIndentationMemberInitializerList(self):
327327
lines = [
328328
"namespace Opossum {",
329329
"",
330-
"Acme::Acme(const std::shared_ptr<const AbstractOperator> left,",
331-
" const std::shared_ptr<const AbstractOperator> nigh)",
330+
"protected Acme::Acme(const std::shared_ptr<const AbstractOperator> left,",
331+
" const std::shared_ptr<const AbstractOperator> nigh)",
332332
" : _left(left), _behind(nigh) {}",
333333
"",
334334
"} // namespace Opossum",
335335
]
336336
assert self.GetNamespaceResults(lines) == ""
337337

338-
# Multiline member initializer List
338+
# Multiline member initializer list; constructor on different line from list
339339
lines = [
340340
"namespace Rosenfield {",
341341
"class Crush : public Habitual {",
342342
" public:",
343-
" Crush() : _a(1),",
343+
" explicit Crush() : _a(1),",
344344
" _b(2)",
345345
" {}",
346346
"};",
347347
"} // namespace Rosenfield",
348348
]
349349
assert self.GetNamespaceResults(lines) == ""
350350

351+
# Inline + multiline, ends on the same line as a list's end
352+
lines = [
353+
"namespace Humpety {",
354+
"Dumpety::Dumpety(const int a)",
355+
" : number(a),",
356+
" looooooong_attr1(0),",
357+
" looooooong_attr2(0),",
358+
" looooooong_attr3(0),",
359+
" looooooong_attr4(0) {}",
360+
]
361+
assert self.GetNamespaceResults(lines) == ""
362+
351363
# Same line as constructor declaration
352364
lines = [
353365
"namespace Boucher {",
@@ -363,7 +375,7 @@ def testNamespaceIndentationMemberInitializerList(self):
363375
lines = [
364376
"namespace Store {",
365377
"",
366-
" Color::Color() : my_name_is('b')",
378+
" Color::Color() : my_name_is('b'),",
367379
" this_is_true(true) {",
368380
]
369381
assert (

0 commit comments

Comments
 (0)