Skip to content

Commit e742b9e

Browse files
committed
codecompliance: check spaces around compound/comparison operators
cppstyle.py already flags a few C++ spacing slips but not missing spaces around the compound-assignment and comparison operators (+=, ==, <=, and friends), which #837 asks for. Add a check for them. The operators overlap, so a naive scan false-matches: >= sits inside >>=, <= inside <<= and the C++20 <=> spaceship. The regex uses lookbehind/lookahead to only match the standalone forms. Comments and string literals are blanked before checking, and operator-overload declarations (operator==, etc.) are skipped, so things like `// a==b`, `"x==y"` and `bool operator==(...)` don't trip it. The existing tree already passes the new check (zero hits across libopenage), so this is purely additive. Closes #837.
1 parent 4176e0e commit e742b9e

1 file changed

Lines changed: 43 additions & 2 deletions

File tree

buildsystem/codecompliance/cppstyle.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2015-2017 the openage authors. See copying.md for legal info.
1+
# Copyright 2015-2026 the openage authors. See copying.md for legal info.
22

33
"""
44
Checks some code style rules for cpp files.
@@ -47,6 +47,33 @@
4747
r")"
4848
)
4949

50+
# Compound-assignment and comparison operators that must be surrounded by spaces.
51+
# Ordered longest-first; < and >-based ops use lookbehind/lookahead to avoid
52+
# matching the shorter pattern inside a longer one (e.g. >= inside >>=,
53+
# <= inside <<= or the C++20 <=> spaceship operator).
54+
_SPACED_OPS_ALT = (
55+
r'<<=|>>=|'
56+
r'\+=|-=|\*=|%=|\^=|\|=|&=|~=|'
57+
r'(?<!<)<=(?!>)|(?<!>)>=|'
58+
r'==|!='
59+
)
60+
61+
OPERATOR_SPACING_RE = re.compile(
62+
r'(?<!\s)(?:' + _SPACED_OPS_ALT + r')'
63+
r'|'
64+
r'(?:' + _SPACED_OPS_ALT + r')(?!\s)'
65+
)
66+
67+
# Strips // comments, inline /* */ comments, and double-quoted string literals
68+
# by replacing each with equal-length spaces (preserves column offsets).
69+
_CLEAN_LINE_RE = re.compile(r'//.*|/\*.*?\*/|"(?:[^"\\]|\\.)*"')
70+
71+
# Matches the start of a block-comment continuation line (e.g. " * text").
72+
_BLOCK_CMT_CONT_RE = re.compile(r'^\s*\*')
73+
74+
# Matches the 'operator' keyword (used to skip operator-overload declarations).
75+
_OPERATOR_KW_RE = re.compile(r'\boperator\b')
76+
5077

5178
def filter_file_list(check_files, dirnames):
5279
"""
@@ -81,7 +108,8 @@ def find_issues(check_files, dirnames):
81108

82109
if MISSING_SPACES_RE.search(data) or\
83110
EXTRA_SPACES_RE.search(data) or\
84-
INDENT_FAIL_RE.search(data):
111+
INDENT_FAIL_RE.search(data) or\
112+
OPERATOR_SPACING_RE.search(data):
85113

86114
analyse_each_line = True
87115

@@ -116,3 +144,16 @@ def find_issues_with_lines(data, filename):
116144
yield issue_str_line("Wrong indentation",
117145
filename, line, num,
118146
(match.start(1), match.end(1)))
147+
148+
# Check that compound-assignment and comparison operators have spaces
149+
# on both sides. Operate on a cleaned copy of the line (comments and
150+
# string literals blanked out) to avoid false positives; also skip
151+
# block-comment continuation lines and operator-overload declarations.
152+
check_line = _CLEAN_LINE_RE.sub(lambda m: ' ' * len(m.group()), line)
153+
if (not _BLOCK_CMT_CONT_RE.match(check_line) and
154+
not _OPERATOR_KW_RE.search(check_line)):
155+
match = OPERATOR_SPACING_RE.search(check_line)
156+
if match:
157+
yield issue_str_line("Missing space around operator",
158+
filename, line, num,
159+
(match.start(), match.end()))

0 commit comments

Comments
 (0)