task: metadata: install: pip install -e .[dev]
patch: diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 03cb330..a537585 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -20,6 +20,7 @@ A bunch of long-overdue modernizations of the codebase!
 * Indented functions inside namespaces will now be correctly erred on, courtesy of @Yujinmon (https://github.com/cpplint/cpplint/pull/235)
 * The check for C-style casts now looks for the standard fixed-width integer typenames instead of non-standard ones (e.g. int32_t instead of int32) thanks to @nate-thirdwave (https://github.com/cpplint/cpplint/pull/282)
 * `[[(un)likely]]` no longer clouds readability/braces's super spy−scanning of braces, courtesy of @aaronliu0130 (https://github.com/cpplint/cpplint/pull/265)
+* `readability/braces` will realize that C++20 concepts require a semicolon, courtesy of @armandas (https://github.com/cpplint/cpplint/pull/288)
 * C++20 headers will no longer be flagged as C headers thanks to @miker2 (https://github.com/cpplint/cpplint/pull/216)
    * Same goes for C++23 and C23 headers, thanks to @aaronliu0130 (https://github.com/cpplint/cpplint/pull/239)
    * "complex.h" will be treated as the C99 header instead of the legacy C++ header by @tkruse (https://github.com/cpplint/cpplint/pull/219)
diff --git a/cpplint.py b/cpplint.py
index 54410f3..aa31357 100755
--- a/cpplint.py
+++ b/cpplint.py
@@ -4539,6 +4539,7 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
     #  - Lambdas
     #  - alignas specifier with anonymous structs
     #  - decltype
+    #  - concepts (requires expression)
     closing_brace_pos = match.group(1).rfind(')')
     opening_parenthesis = ReverseCloseExpression(
         clean_lines, linenum, closing_brace_pos)
@@ -4554,6 +4555,7 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
           (func and not re.search(r'\boperator\s*\[\s*\]', func.group(1))) or
           re.search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or
           re.search(r'\bdecltype$', line_prefix) or
+          re.search(r'\brequires.*$', line_prefix) or
           re.search(r'\s+=\s*$', line_prefix)):
         match = None
     if (match and

problem_statement: C++20 concepts require a semicolon after the braces
I have a code part like:
```C++
template <typename T>
concept IndexableToChar = requires(T t) {
  { t[0] } -> std::convertible_to<char>;
};
```

And cpplint says `You don't need a ; after a }  [readability/braces] [4]`. But here this `;` is needed, otherwise there will be compiling error.
repo: cpplint/cpplint
test_cmd: pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
test_patch: diff --git a/cpplint_unittest.py b/cpplint_unittest.py
index 94dd81c..c20384c 100755
--- a/cpplint_unittest.py
+++ b/cpplint_unittest.py
@@ -2807,6 +2807,21 @@ class CpplintTest(CpplintTestBase):
 
     self.TestLint('file_tocs_[i] = (FileToc) {a, b, c};', '')
     self.TestMultiLineLint('class X : public Y,\npublic Z {};', '')
+    self.TestMultiLineLint('template<typename T>\n'
+                           'concept Addable = requires(T x) { x + x; };',
+                           '')
+    self.TestMultiLineLint('template <typename T>\n'
+                           'concept C = requires(T a, T b) {\n'
+                           '    requires a == b;\n'
+                           '};',
+                           '')
+    self.TestMultiLineLint('template <typename T, typename U>\n'
+                           'concept C = (std::integral<T> || std::floating_point<T>) &&\n'
+                           '            (std::integral<U> || std::floating_point<U>) &&\n'
+                           '            requires(T t, U u) {\n'
+                           '    std::min(static_cast<float>(t), static_cast<float>(u));\n'
+                           '};',
+                           '')
 
   def testSpacingBeforeBrackets(self):
     self.TestLint('int numbers [] = { 1, 2, 3 };',