[clang] 560eb22 - [clang-format] Fix bug in parsing `operator<` with template

Marek Kurdej via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 19 23:59:12 PST 2022


Author: Jino Park
Date: 2022-01-20T08:59:04+01:00
New Revision: 560eb2277bb5aea0982ce5f90321788cda3fe4b3

URL: https://github.com/llvm/llvm-project/commit/560eb2277bb5aea0982ce5f90321788cda3fe4b3
DIFF: https://github.com/llvm/llvm-project/commit/560eb2277bb5aea0982ce5f90321788cda3fe4b3.diff

LOG: [clang-format] Fix bug in parsing `operator<` with template

Fixes https://github.com/llvm/llvm-project/issues/44601.

This patch handles a bug when parsing a below example code :

```
template <class> class S;

template <class T> bool operator<(S<T> const &x, S<T> const &y) {
  return x.i < y.i;
}

template <class T> class S {
  int i = 42;
  friend bool operator< <>(S const &, S const &);
};

int main() { return S<int>{} < S<int>{}; }
```
which parse `< <>` as `<< >`, not `< <>` in terms of tokens as discussed in discord.

1. Add a condition in `tryMergeLessLess()` considering `operator` keyword and `>`
2. Force to leave a whitespace between `tok::less` and a template opener
3. Add unit test

Reviewed By: MyDeveloperDay, curdeius

Differential Revision: https://reviews.llvm.org/D117398

Added: 
    

Modified: 
    clang/lib/Format/FormatTokenLexer.cpp
    clang/lib/Format/TokenAnnotator.cpp
    clang/unittests/Format/FormatTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index 7736a7042f862..04629fec1bcaf 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -429,11 +429,18 @@ bool FormatTokenLexer::tryMergeLessLess() {
   if (Tokens.size() < 3)
     return false;
 
+  auto First = Tokens.end() - 3;
   bool FourthTokenIsLess = false;
-  if (Tokens.size() > 3)
-    FourthTokenIsLess = (Tokens.end() - 4)[0]->is(tok::less);
 
-  auto First = Tokens.end() - 3;
+  if (Tokens.size() > 3) {
+    auto Fourth = (Tokens.end() - 4)[0];
+    FourthTokenIsLess = Fourth->is(tok::less);
+
+    // Do not remove a whitespace between the two "<" e.g. "operator< <>".
+    if (First[2]->is(tok::greater) && Fourth->is(tok::kw_operator))
+      return false;
+  }
+
   if (First[2]->is(tok::less) || First[1]->isNot(tok::less) ||
       First[0]->isNot(tok::less) || FourthTokenIsLess)
     return false;

diff  --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 7948dd105ee8b..71f29e8c010e5 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -3346,6 +3346,9 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
     if (Right.is(tok::l_brace) && Right.is(BK_BracedInit) &&
         !Left.opensScope() && Style.SpaceBeforeCpp11BracedList)
       return true;
+    if (Left.is(tok::less) && Left.is(TT_OverloadedOperator) &&
+        Right.is(TT_TemplateOpener))
+      return true;
   } else if (Style.Language == FormatStyle::LK_Proto ||
              Style.Language == FormatStyle::LK_TextProto) {
     if (Right.is(tok::period) &&

diff  --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index b8645b8896d2a..6bd417388fc7d 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -9463,6 +9463,10 @@ TEST_F(FormatTest, UnderstandsOverloadedOperators) {
   verifyFormat("operator SomeType<int>();");
   verifyFormat("operator SomeType<int, int>();");
   verifyFormat("operator SomeType<SomeType<int>>();");
+  verifyFormat("operator< <>();");
+  verifyFormat("operator<< <>();");
+  verifyFormat("< <>");
+
   verifyFormat("void *operator new(std::size_t size);");
   verifyFormat("void *operator new[](std::size_t size);");
   verifyFormat("void operator delete(void *ptr);");


        


More information about the cfe-commits mailing list