Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ TBA
* Fixed a whitespace/newline false positive for control conditions containing lambdas. (#410)
* We now error on relative include paths (``./``, ``../``). (#432)
* This makes ``#include "./foo.h"`` produce two separate errors: that foo.cpp should include foo.h and that relative paths are not allowed.
* Fixed a readability/braces false positive for C++20 templated lambdas. (#385)

2.0.2 (2025-04-08)
===========
Expand Down
75 changes: 67 additions & 8 deletions cpplint.py
Original file line number Diff line number Diff line change
Expand Up @@ -5073,6 +5073,50 @@ def CheckBraces(filename, clean_lines, linenum, error):
)


def MatchLambdaCapture(clean_lines, linenum, line_prefix):
"""Matches a lambda capture before an optional template parameter list."""
capture = re.match(r"^(.*\])\s*$", line_prefix)
if capture or not re.search(r">\s*$", line_prefix):
return capture

template_end = line_prefix.rfind(">")
template_start = ReverseCloseExpression(clean_lines, linenum, template_end)
if template_start[2] < 0:
return None

capture_prefix = template_start[0][0 : template_start[2]]
if not capture_prefix.strip() and template_start[1] > 0:
capture_prefix = GetPreviousNonBlankLine(clean_lines, template_start[1])[0]
return re.match(r"^(.*\])\s*$", capture_prefix)


def MatchLambdaRequiresClause(clean_lines, linenum):
"""Matches the lambda capture preceding a multiline requires-clause."""
while linenum >= 0:
line = clean_lines.elided[linenum]
if requires := re.search(r"\brequires\b", line):
capture = MatchLambdaCapture(
clean_lines,
linenum,
line[0 : requires.start()],
)
if capture:
return capture
template_line, template_linenum = GetPreviousNonBlankLine(
clean_lines,
linenum,
)
return MatchLambdaCapture(
clean_lines,
template_linenum,
template_line,
)
if re.search(r"[;{}]\s*$", line):
return None
_, linenum = GetPreviousNonBlankLine(clean_lines, linenum)
return None


def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
"""Looks for redundant trailing semicolon.

Expand Down Expand Up @@ -5163,7 +5207,11 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
if opening_parenthesis[2] > -1:
line_prefix = opening_parenthesis[0][0 : opening_parenthesis[2]]
macro = re.search(r"\b([A-Z_][A-Z0-9_]*)\s*$", line_prefix)
func = re.match(r"^(.*\])\s*$", line_prefix)
func = MatchLambdaCapture(
clean_lines,
opening_parenthesis[1],
line_prefix,
)
if (
(
macro
Expand All @@ -5187,13 +5235,24 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
or re.search(r"\s+=\s*$", line_prefix)
):
match = None
if (
match
and opening_parenthesis[1] > 1
and re.search(r"\]\s*$", clean_lines.elided[opening_parenthesis[1] - 1])
):
# Multi-line lambda-expression
match = None
if match and opening_parenthesis[1] > 0:
previous_line, previous_linenum = GetPreviousNonBlankLine(
clean_lines,
opening_parenthesis[1],
)
func = MatchLambdaCapture(
clean_lines,
previous_linenum,
previous_line,
)
if not func:
func = MatchLambdaRequiresClause(
clean_lines,
previous_linenum,
)
if func and not re.search(r"\boperator\s*\[\s*\]", func.group(1)):
# Multi-line lambda-expression
match = None

else:
# Try matching cases 2-3.
Expand Down
41 changes: 41 additions & 0 deletions cpplint_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3166,9 +3166,50 @@ def testLambda(self):
self.TestLint("auto x = []() {};", "")
self.TestLint("return []() {};", "")
self.TestMultiLineLint("auto x = []() {\n};\n", "")
self.TestMultiLineLint(
"int main() {\n auto identity = []<typename T>(T&& t) {\n return t;\n };\n}\n",
"",
)
self.TestMultiLineLint(
"auto identity = []<\n typename T\n>(T&& t) {\n return t;\n};\n",
"",
)
self.TestMultiLineLint(
"auto identity = []<typename T>\n(T&& t) {\n return t;\n};\n",
"",
)
self.TestMultiLineLint(
"auto identity = []\n<typename T>\n(T&& t) {\n return t;\n};\n",
"",
)
self.TestMultiLineLint(
"auto identity = []<typename T>\n"
" requires std::integral<T>\n"
"(T&& t) {\n"
" return t;\n"
"};\n",
"",
)
self.TestMultiLineLint(
"auto identity = []<typename T>\n"
" requires std::integral<T> &&\n"
" std::copyable<T>\n"
"(T&& t) {\n"
" return t;\n"
"};\n",
"",
)
self.TestLint(
"int operator[](int x) {};", "You don't need a ; after a } [readability/braces] [4]"
)
self.TestMultiLineLint(
"int operator[]\n(int x) {};",
"You don't need a ; after a } [readability/braces] [4]",
)
self.TestMultiLineLint(
"template <typename T>\n requires C<T>\nvoid Function(T value) {};",
"You don't need a ; after a } [readability/braces] [4]",
)

self.TestMultiLineLint("auto x = [&a,\nb]() {};", "")
self.TestMultiLineLint("auto x = [&a,\nb]\n() {};", "")
Expand Down