Coverage for sources / vibelinter / rules / implementations / vbl101.py: 60%
63 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-01 02:35 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-01 02:35 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
22''' VBL101: Detect blank lines between statements in function bodies.
26 Category: Readability
27 Subcategory: Compactness
29 This rule detects blank lines between statements within function or
30 method bodies and suggests their elimination to improve vertical
31 compactness per the project coding standards. Blank lines inside
32 string literals are allowed.
33'''
36from . import __
39class VBL101( __.BaseRule ):
40 ''' Detects blank lines between statements in function bodies. '''
42 @property
43 def rule_id( self ) -> str:
44 return 'VBL101'
46 def __init__(
47 self,
48 filename: str,
49 wrapper: __.libcst.metadata.MetadataWrapper,
50 source_lines: tuple[ str, ... ],
51 ) -> None:
52 super( ).__init__( filename, wrapper, source_lines )
53 # Collection: store function definitions and their line ranges
54 self._function_ranges: list[
55 tuple[ int, int, __.libcst.FunctionDef ] ] = [ ]
56 # Collection: store triple-quoted string literal line ranges
57 self._string_ranges: list[ tuple[ int, int ] ] = [ ]
59 def visit_FunctionDef( self, node: __.libcst.FunctionDef ) -> bool:
60 ''' Collects function definitions for later analysis. '''
61 # Get the position of the function
62 try:
63 position = self.wrapper.resolve(
64 __.libcst.metadata.PositionProvider )[ node ]
65 start_line = position.start.line
66 end_line = position.end.line
67 self._function_ranges.append( ( start_line, end_line, node ) )
68 except KeyError:
69 # Position not available, skip this function
70 pass
71 return True # Continue visiting children
73 def visit_SimpleString( self, node: __.libcst.SimpleString ) -> bool:
74 ''' Collects triple-quoted string literal ranges. '''
75 # Only track triple-quoted strings (docstrings and multiline strings)
76 if node.quote in ( '"""', "'''" ):
77 try:
78 position = self.wrapper.resolve(
79 __.libcst.metadata.PositionProvider )[ node ]
80 start_line = position.start.line
81 end_line = position.end.line
82 self._string_ranges.append( ( start_line, end_line ) )
83 except KeyError:
84 # Position not available, skip this string
85 pass
86 return True # Continue visiting children
88 def visit_ConcatenatedString(
89 self, node: __.libcst.ConcatenatedString
90 ) -> bool:
91 ''' Collects concatenated string literal ranges. '''
92 # Check if any part is a triple-quoted string
93 has_triple_quote = False
94 for part in ( node.left, node.right ):
95 if isinstance( part, __.libcst.SimpleString ):
96 if part.quote in ( '"""', "'''" ):
97 has_triple_quote = True
98 break
99 elif (
100 isinstance( part, __.libcst.FormattedString )
101 and part.start in ( '"""', "'''" )
102 ):
103 # f-strings can also be triple-quoted
104 has_triple_quote = True
105 break
106 if has_triple_quote:
107 try:
108 position = self.wrapper.resolve(
109 __.libcst.metadata.PositionProvider )[ node ]
110 start_line = position.start.line
111 end_line = position.end.line
112 self._string_ranges.append( ( start_line, end_line ) )
113 except KeyError:
114 # Position not available, skip this string
115 pass
116 return True # Continue visiting children
118 def _analyze_collections( self ) -> None:
119 ''' Analyzes collected functions for blank lines between statements.
120 Blank lines inside string literals are allowed.
121 '''
122 for start_line, end_line, _func_node in self._function_ranges:
123 # Get function body start (after the def line)
124 body_start = start_line + 1
125 for line_num in range( body_start, end_line + 1 ):
126 if line_num - 1 >= len( self.source_lines ): break 126 ↛ 122line 126 didn't jump to line 122 because the break on line 126 wasn't executed
127 line = self.source_lines[ line_num - 1 ]
128 stripped = line.strip( )
129 # Report violation for blank lines between statements
130 # Skip blank lines inside string literals
131 if not stripped and not self._is_in_string( line_num ):
132 self._report_blank_line( line_num )
134 def _is_in_string( self, line_num: int ) -> bool:
135 ''' Checks if line is inside a triple-quoted string literal. '''
136 return any(
137 start <= line_num <= end
138 for start, end in self._string_ranges )
140 def _report_blank_line( self, line_num: int ) -> None:
141 ''' Reports a violation for a blank line in function body. '''
142 from .. import violations as _violations
143 violation = _violations.Violation(
144 rule_id = self.rule_id,
145 filename = self.filename,
146 line = line_num,
147 column = 1,
148 message = "Blank line in function body.",
149 severity = 'warning' )
150 self._violations.append( violation )
153# Self-register this rule
154__.RULE_DESCRIPTORS[ 'VBL101' ] = __.RuleDescriptor(
155 vbl_code = 'VBL101',
156 descriptive_name = 'blank-line-elimination',
157 description = 'Detects blank lines within function bodies.',
158 category = 'readability',
159 subcategory = 'compactness',
160 rule_class = VBL101,
161)