dkt01 created this revision.
dkt01 added reviewers: MyDeveloperDay, owenpan.
dkt01 added a project: clang-format.
Herald added a project: All.
dkt01 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Token annotator for clang-format incorrectly identifies operator& as a 
reference type in situations like Boost serialization archives 
<https://www.boost.org/doc/libs/1_81_0/libs/serialization/doc/tutorial.html>.

Add annotation rules for standalone and chained operator& instances while 
preserving behavior for reference declarations at class scope.  Add tests to 
validate annotation and formatting behavior.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D141959

Files:
  clang/lib/Format/TokenAnnotator.cpp
  clang/unittests/Format/FormatTest.cpp
  clang/unittests/Format/TokenAnnotatorTest.cpp

Index: clang/unittests/Format/TokenAnnotatorTest.cpp
===================================================================
--- clang/unittests/Format/TokenAnnotatorTest.cpp
+++ clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -175,6 +175,42 @@
   ASSERT_EQ(Tokens.size(), 17u) << Tokens;
   EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_PointerOrReference);
   EXPECT_TOKEN(Tokens[12], tok::ampamp, TT_PointerOrReference);
+
+  Tokens = annotate("val1 & val2;");
+  ASSERT_EQ(Tokens.size(), 5u) << Tokens;
+  EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator);
+
+  Tokens = annotate("val1 & val2.member;");
+  ASSERT_EQ(Tokens.size(), 7u) << Tokens;
+  EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator);
+
+  Tokens = annotate("val1 & val2 & val3;");
+  ASSERT_EQ(Tokens.size(), 7u) << Tokens;
+  EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator);
+  EXPECT_TOKEN(Tokens[3], tok::amp, TT_BinaryOperator);
+
+  Tokens = annotate("val1 & val2 // comment\n"
+                    "     & val3;");
+  ASSERT_EQ(Tokens.size(), 8u) << Tokens;
+  EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator);
+  EXPECT_TOKEN(Tokens[4], tok::amp, TT_BinaryOperator);
+
+  Tokens =
+      annotate("val1 & val2.member & val3.member() & val4 & val5->member;");
+  ASSERT_EQ(Tokens.size(), 19u) << Tokens;
+  EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator);
+  EXPECT_TOKEN(Tokens[5], tok::amp, TT_BinaryOperator);
+  EXPECT_TOKEN(Tokens[11], tok::amp, TT_BinaryOperator);
+  EXPECT_TOKEN(Tokens[13], tok::amp, TT_BinaryOperator);
+
+  Tokens = annotate("class c {\n"
+                    "  void func(type &a) { a & member; }\n"
+                    "  anotherType &member;\n"
+                    "}");
+  ASSERT_EQ(Tokens.size(), 22u) << Tokens;
+  EXPECT_TOKEN(Tokens[7], tok::amp, TT_PointerOrReference);
+  EXPECT_TOKEN(Tokens[12], tok::amp, TT_BinaryOperator);
+  EXPECT_TOKEN(Tokens[17], tok::amp, TT_PointerOrReference);
 }
 
 TEST_F(TokenAnnotatorTest, UnderstandsUsesOfPlusAndMinus) {
Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -11267,6 +11267,13 @@
 
   verifyFormat("int operator()(T (&&)[N]) { return 1; }");
   verifyFormat("int operator()(T (&)[N]) { return 0; }");
+
+  verifyFormat("val1 & val2;");
+  verifyFormat("val1 & val2 & val3;");
+  verifyFormat("class c {\n"
+               "  void func(type &a) { a & member; }\n"
+               "  anotherType &member;\n"
+               "}");
 }
 
 TEST_F(FormatTest, UnderstandsAttributes) {
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -847,6 +847,8 @@
     unsigned CommaCount = 0;
     while (CurrentToken) {
       if (CurrentToken->is(tok::r_brace)) {
+        if (Scopes.size() > 1)
+          Scopes.pop_back();
         assert(OpeningBrace.Optional == CurrentToken->Optional);
         OpeningBrace.MatchingParen = CurrentToken;
         CurrentToken->MatchingParen = &OpeningBrace;
@@ -1146,8 +1148,32 @@
         if (Previous && Previous->getType() != TT_DictLiteral)
           Previous->setType(TT_SelectorName);
       }
-      if (!parseBrace())
+      switch (Tok->getType()) {
+      case TT_FunctionLBrace:
+      case TT_LambdaLBrace:
+        Scopes.push_back(ScopeType::Function);
+        break;
+      case TT_ClassLBrace:
+      case TT_StructLBrace:
+      case TT_UnionLBrace:
+        Scopes.push_back(ScopeType::Class);
+        break;
+      case TT_EnumLBrace:
+      case TT_ControlStatementLBrace:
+      case TT_ElseLBrace:
+      case TT_BracedListLBrace:
+      case TT_CompoundRequirementLBrace:
+      case TT_ObjCBlockLBrace:
+      case TT_RecordLBrace:
+      case TT_RequiresExpressionLBrace:
+      default:
+        Scopes.push_back(ScopeType::Other);
+      }
+      if (!parseBrace()) {
+        if (Scopes.size() > 1)
+          Scopes.pop_back();
         return false;
+      }
       break;
     case tok::less:
       if (parseAngle()) {
@@ -1176,6 +1202,8 @@
     case tok::r_square:
       return false;
     case tok::r_brace:
+      if (Scopes.size() > 1)
+        Scopes.pop_back();
       // Lines can start with '}'.
       if (Tok->Previous)
         return false;
@@ -1643,6 +1671,17 @@
     } ContextType = Unknown;
   };
 
+  enum class ScopeType {
+    // Not contained within scope block
+    None,
+    // Contained in class declaration/definition
+    Class,
+    // Contained within function definition
+    Function,
+    // Contained within other scope block (loop, if/else, etc)
+    Other,
+  };
+
   /// Puts a new \c Context onto the stack \c Contexts for the lifetime
   /// of each instance.
   struct ScopedContextCreator {
@@ -1895,7 +1934,7 @@
       Current.setType(TT_TrailingReturnArrow);
     } else if (Current.isOneOf(tok::star, tok::amp, tok::ampamp)) {
       Current.setType(determineStarAmpUsage(
-          Current,
+          Current, Scopes.back(),
           Contexts.back().CanBeExpression && Contexts.back().IsExpression,
           Contexts.back().ContextType == Context::TemplateArgument));
     } else if (Current.isOneOf(tok::minus, tok::plus, tok::caret) ||
@@ -2342,7 +2381,8 @@
   }
 
   /// Return the type of the given token assuming it is * or &.
-  TokenType determineStarAmpUsage(const FormatToken &Tok, bool IsExpression,
+  TokenType determineStarAmpUsage(const FormatToken &Tok,
+                                  const ScopeType TokScope, bool IsExpression,
                                   bool InTemplateArgument) {
     if (Style.isJavaScript())
       return TT_BinaryOperator;
@@ -2446,6 +2486,31 @@
     if (IsExpression && !Contexts.back().CaretFound)
       return TT_BinaryOperator;
 
+    // Opeartors at class scope are likely pointer or reference members
+    if (TokScope == ScopeType::Class)
+      return TT_PointerOrReference;
+
+    // It's more likely that & represents operator& than an uninitialized
+    // reference
+    if (Tok.is(tok::amp) && (PrevToken && PrevToken->Tok.isAnyIdentifier()) &&
+        ((PrevToken->getPreviousNonComment() &&
+          (PrevToken->getPreviousNonComment()->is(tok::amp) ||
+           PrevToken->getPreviousNonComment()->is(tok::period) ||
+           PrevToken->getPreviousNonComment()->is(tok::arrow) ||
+           PrevToken->getPreviousNonComment()->is(tok::arrowstar) ||
+           PrevToken->getPreviousNonComment()->is(tok::periodstar))) ||
+         !PrevToken->getPreviousNonComment()) &&
+        (NextToken && NextToken->Tok.isAnyIdentifier()) &&
+        (NextToken->getNextNonComment() &&
+         (NextToken->getNextNonComment()->is(tok::amp) ||
+          NextToken->getNextNonComment()->is(tok::semi) ||
+          NextToken->getNextNonComment()->is(tok::period) ||
+          NextToken->getNextNonComment()->is(tok::arrow) ||
+          NextToken->getNextNonComment()->is(tok::arrowstar) ||
+          NextToken->getNextNonComment()->is(tok::periodstar)))) {
+      return TT_BinaryOperator;
+    }
+
     return TT_PointerOrReference;
   }
 
@@ -2476,6 +2541,7 @@
   }
 
   SmallVector<Context, 8> Contexts;
+  static SmallVector<ScopeType, 8> Scopes;
 
   const FormatStyle &Style;
   AnnotatedLine &Line;
@@ -2722,6 +2788,9 @@
   FormatToken *Current;
 };
 
+SmallVector<AnnotatingParser::ScopeType, 8> AnnotatingParser::Scopes = {
+    AnnotatingParser::ScopeType::None};
+
 } // end anonymous namespace
 
 void TokenAnnotator::setCommentLineLevels(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to