@@ -3097,6 +3097,14 @@ def CheckEnd(self, filename, clean_lines, linenum, error):
3097
3097
)
3098
3098
3099
3099
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
+
3100
3108
class _NamespaceInfo (_BlockInfo ):
3101
3109
"""Stores information about a namespace."""
3102
3110
@@ -3174,6 +3182,22 @@ def CheckEnd(self, filename, clean_lines, linenum, error):
3174
3182
)
3175
3183
3176
3184
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
+
3177
3201
class _PreprocessorInfo :
3178
3202
"""Stores checkpoints of nesting stacks when #if/#else is seen."""
3179
3203
@@ -3216,6 +3240,9 @@ def __init__(self):
3216
3240
# We can't use previous_stack_top, a shallow copy whose open_parentheses value is updated.
3217
3241
self .previous_open_parentheses = 0
3218
3242
3243
+ # The last stack item we popped.
3244
+ self .popped_top : _BlockInfo | None = None
3245
+
3219
3246
# Stack of _PreprocessorInfo objects.
3220
3247
self .pp_stack = []
3221
3248
@@ -3370,6 +3397,9 @@ def UpdatePreprocessor(self, line):
3370
3397
# TODO(google): unexpected #endif, issue warning?
3371
3398
pass
3372
3399
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 ()
3373
3403
3374
3404
def _CountOpenParentheses (self , line : str ):
3375
3405
# Count parentheses. This is to avoid adding struct arguments to
@@ -3423,6 +3453,22 @@ def _UpdateNamesapce(self, line: str, linenum: int) -> str | None:
3423
3453
line = line [line .find ("{" ) + 1 :]
3424
3454
return line
3425
3455
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
+
3426
3472
# TODO(google): Update() is too long, but we will refactor later.
3427
3473
def Update (self , filename : str , clean_lines : CleansedLines , linenum : int , error ):
3428
3474
"""Update nesting state with current line.
@@ -3492,36 +3538,56 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
3492
3538
if not self .SeenOpenBrace ():
3493
3539
self .stack [- 1 ].CheckBegin (filename , clean_lines , linenum , error )
3494
3540
3495
- # Update access control if we are inside a class/struct
3541
+ # Update access control if we are directly inside a class/struct
3496
3542
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 )
3502
3586
)
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 ))
3525
3591
3526
3592
# Consume braces or semicolons from what's left of the line
3527
3593
while True :
@@ -3532,34 +3598,42 @@ def Update(self, filename: str, clean_lines: CleansedLines, linenum: int, error)
3532
3598
3533
3599
token = matched .group (1 )
3534
3600
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
3536
3602
# namespace/class head as complete. Push a new block onto the
3537
3603
# stack otherwise.
3538
3604
if not self .SeenOpenBrace ():
3605
+ # End of initializer list wrap if present
3606
+ if isinstance (self .stack [- 1 ], _MemInitListInfo ):
3607
+ self ._Pop ()
3539
3608
self .stack [- 1 ].seen_open_brace = True
3540
3609
elif re .match (r'^extern\s*"[^"]*"\s*\{' , line ):
3541
3610
self .stack .append (_ExternCInfo (linenum ))
3542
3611
else :
3543
3612
self .stack .append (_BlockInfo (linenum , True ))
3544
3613
if _MATCH_ASM .match (line ):
3545
3614
self .stack [- 1 ].inline_asm = _BLOCK_ASM
3546
-
3547
- elif token in {";" , ")" }:
3615
+ elif token == ";" :
3548
3616
# If we haven't seen an opening brace yet, but we already saw
3549
3617
# a semicolon, this is probably a forward declaration. Pop
3550
3618
# the stack for these.
3551
- #
3619
+ if not self .SeenOpenBrace ():
3620
+ self ._Pop ()
3621
+ elif token == ")" :
3552
3622
# Similarly, if we haven't seen an opening brace yet, but we
3553
3623
# already saw a closing parenthesis, then these are probably
3554
3624
# function arguments with extra "class" or "struct" keywords.
3555
3625
# 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 ()
3558
3632
else : # token == '}'
3559
3633
# Perform end of block checks and pop the stack.
3560
3634
if self .stack :
3561
3635
self .stack [- 1 ].CheckEnd (filename , clean_lines , linenum , error )
3562
- self .stack . pop ()
3636
+ self ._Pop ()
3563
3637
line = matched .group (2 )
3564
3638
3565
3639
def InnermostClass (self ):
@@ -7097,11 +7171,11 @@ def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):
7097
7171
7098
7172
# Returns true if we are at a new block, and it is directly
7099
7173
# inside of a namespace.
7100
- def IsBlockInNameSpace (nesting_state , is_forward_declaration ):
7174
+ def IsBlockInNameSpace (nesting_state : NestingState , is_forward_declaration : bool ): # noqa: FBT001
7101
7175
"""Checks that the new block is directly in a namespace.
7102
7176
7103
7177
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.
7105
7179
is_forward_declaration: If the class is a forward declared class.
7106
7180
Returns:
7107
7181
Whether or not the new block is directly in a namespace.
@@ -7117,7 +7191,13 @@ def IsBlockInNameSpace(nesting_state, is_forward_declaration):
7117
7191
if (
7118
7192
len (nesting_state .stack ) > 1
7119
7193
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
+ )
7121
7201
):
7122
7202
return True
7123
7203
return False
@@ -7141,6 +7221,10 @@ def ShouldCheckNamespaceIndentation(
7141
7221
only works for classes and namespaces inside of a namespace.
7142
7222
"""
7143
7223
7224
+ # Required by all checks involving nesting_state
7225
+ if not nesting_state .stack :
7226
+ return False
7227
+
7144
7228
is_forward_declaration = IsForwardClassDeclaration (raw_lines_no_comments , linenum )
7145
7229
7146
7230
if not (is_namespace_indent_item or is_forward_declaration ):
@@ -7154,6 +7238,20 @@ def ShouldCheckNamespaceIndentation(
7154
7238
if nesting_state .previous_stack_top and nesting_state .previous_open_parentheses > 0 :
7155
7239
return False
7156
7240
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
+
7157
7255
return IsBlockInNameSpace (nesting_state , is_forward_declaration )
7158
7256
7159
7257
0 commit comments