[clang] [clang-format] Handle AttributeMacro before access modifiers (PR #95634)

Owen Pan via cfe-commits cfe-commits at lists.llvm.org
Sun Jun 16 13:10:06 PDT 2024


https://github.com/owenca updated https://github.com/llvm/llvm-project/pull/95634

>From 1c4ab4a5fd869de44795abd48bbaa43176e7275e Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Fri, 14 Jun 2024 23:36:58 -0700
Subject: [PATCH 1/5] [clang-format] Handle AttributeMacro before access
 modifiers

Closes #95094.
---
 clang/lib/Format/TokenAnnotator.cpp         |  7 ++++-
 clang/lib/Format/TokenAnnotator.h           |  1 +
 clang/lib/Format/UnwrappedLineFormatter.cpp | 35 ++++++++++-----------
 clang/unittests/Format/FormatTest.cpp       | 15 +++++++++
 4 files changed, 39 insertions(+), 19 deletions(-)

diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 1fe3b61a5a81f..ff00e772a75f4 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1970,6 +1970,7 @@ class AnnotatingParser {
       }
     }
 
+    bool SeenAccessModifier = false;
     bool KeywordVirtualFound = false;
     bool ImportStatement = false;
 
@@ -1978,7 +1979,9 @@ class AnnotatingParser {
       ImportStatement = true;
 
     while (CurrentToken) {
-      if (CurrentToken->is(tok::kw_virtual))
+      if (CurrentToken->isAccessSpecifier())
+        SeenAccessModifier = true;
+      else if (CurrentToken->is(tok::kw_virtual))
         KeywordVirtualFound = true;
       if (Style.isJavaScript()) {
         // export {...} from '...';
@@ -1998,6 +2001,8 @@ class AnnotatingParser {
       if (!consumeToken())
         return LT_Invalid;
     }
+    if (SeenAccessModifier)
+      return LT_AccessModifier;
     if (KeywordVirtualFound)
       return LT_VirtualFunctionDecl;
     if (ImportStatement)
diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index d19d3d061e40c..136880eca718b 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -22,6 +22,7 @@ namespace format {
 
 enum LineType {
   LT_Invalid,
+  LT_AccessModifier, // Contains public/protected/private followed by colon.
   LT_ImportStatement,
   LT_ObjCDecl, // An @interface, @implementation, or @protocol line.
   LT_ObjCMethodDecl,
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 4d53361aaf333..729f3d78f4a35 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -57,7 +57,7 @@ class LevelIndentTracker {
   /// Update the indent state given that \p Line is going to be formatted
   /// next.
   void nextLine(const AnnotatedLine &Line) {
-    Offset = getIndentOffset(*Line.First);
+    Offset = getIndentOffset(Line);
     // Update the indent level cache size so that we can rely on it
     // having the right size in adjustToUnmodifiedline.
     if (Line.Level >= IndentForLevel.size())
@@ -111,42 +111,41 @@ class LevelIndentTracker {
   ///
   /// For example, 'public:' labels in classes are offset by 1 or 2
   /// characters to the left from their level.
-  int getIndentOffset(const FormatToken &RootToken) {
+  int getIndentOffset(const AnnotatedLine &Line) {
     if (Style.Language == FormatStyle::LK_Java || Style.isJavaScript() ||
         Style.isCSharp()) {
       return 0;
     }
 
-    auto IsAccessModifier = [this, &RootToken]() {
-      if (RootToken.isAccessSpecifier(Style.isCpp())) {
+    auto IsAccessModifier = [&](const FormatToken &RootToken) {
+      if (Line.Type == LT_AccessModifier || RootToken.isObjCAccessSpecifier())
         return true;
-      } else if (RootToken.isObjCAccessSpecifier()) {
-        return true;
-      }
+
+      const auto *Next = RootToken.Next;
+
       // Handle Qt signals.
-      else if (RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
-               RootToken.Next && RootToken.Next->is(tok::colon)) {
-        return true;
-      } else if (RootToken.Next &&
-                 RootToken.Next->isOneOf(Keywords.kw_slots,
-                                         Keywords.kw_qslots) &&
-                 RootToken.Next->Next && RootToken.Next->Next->is(tok::colon)) {
+      if (RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
+          Next && Next->is(tok::colon)) {
         return true;
       }
-      // Handle malformed access specifier e.g. 'private' without trailing ':'.
-      else if (!RootToken.Next && RootToken.isAccessSpecifier(false)) {
+
+      if (Next && Next->isOneOf(Keywords.kw_slots, Keywords.kw_qslots) &&
+          Next->Next && Next->Next->is(tok::colon)) {
         return true;
       }
-      return false;
+
+      // Handle malformed access specifier e.g. 'private' without trailing ':'.
+      return !Next && RootToken.isAccessSpecifier(false);
     };
 
-    if (IsAccessModifier()) {
+    if (IsAccessModifier(*Line.First)) {
       // The AccessModifierOffset may be overridden by IndentAccessModifiers,
       // in which case we take a negative value of the IndentWidth to simulate
       // the upper indent level.
       return Style.IndentAccessModifiers ? -Style.IndentWidth
                                          : Style.AccessModifierOffset;
     }
+
     return 0;
   }
 
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index fb57333858529..2ca85c7b70e65 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -12912,6 +12912,15 @@ TEST_F(FormatTest, FormatsAccessModifiers) {
                "  int j;\n"
                "};",
                Style);
+  Style.AttributeMacros.push_back("FOO");
+  Style.AttributeMacros.push_back("BAR");
+  verifyFormat("struct foo {\n"
+               "FOO private:\n"
+               "  int i;\n"
+               "BAR private:\n"
+               "  int j;\n"
+               "};",
+               Style);
 
   FormatStyle NoEmptyLines = getLLVMStyle();
   NoEmptyLines.MaxEmptyLinesToKeep = 0;
@@ -26130,6 +26139,12 @@ TEST_F(FormatTest, IndentAccessModifiers) {
                "      int i;\n"
                "};",
                Style);
+  Style.AttributeMacros.push_back("FOO");
+  verifyFormat("class C {\n"
+               "   FOO public:\n"
+               "      int i;\n"
+               "};",
+               Style);
 }
 
 TEST_F(FormatTest, LimitlessStringsAndComments) {

>From fc7e0472f7970f3d4396c66ed3cade5e4579cc83 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Sat, 15 Jun 2024 10:34:29 -0700
Subject: [PATCH 2/5] Add support for function-like `AttributeMacro`.

---
 clang/lib/Format/UnwrappedLineParser.cpp | 2 ++
 clang/unittests/Format/FormatTest.cpp    | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 899e68eadf70e..1fad02de202a3 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -368,6 +368,8 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
   do {
     if (FormatTok->isAttribute()) {
       nextToken();
+      if (FormatTok->is(tok::l_paren))
+        parseParens();
       continue;
     }
     tok::TokenKind Kind = FormatTok->Tok.getKind();
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 2ca85c7b70e65..fc63afce70042 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -12917,7 +12917,7 @@ TEST_F(FormatTest, FormatsAccessModifiers) {
   verifyFormat("struct foo {\n"
                "FOO private:\n"
                "  int i;\n"
-               "BAR private:\n"
+               "BAR(x) protected:\n"
                "  int j;\n"
                "};",
                Style);

>From 00eb2fce9f441cebe7424a7034bd254d1a925391 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Sun, 16 Jun 2024 01:23:17 -0700
Subject: [PATCH 3/5] Set `Line.Type` to `LT_AccessModifier` only if `:` is
 `TT_InheritanceColon`.

---
 clang/lib/Format/TokenAnnotator.cpp | 9 ++++-----
 clang/lib/Format/TokenAnnotator.h   | 3 ++-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index ff00e772a75f4..1332445070314 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1419,6 +1419,8 @@ class AnnotatingParser {
             Tok->setType(TT_CtorInitializerColon);
         } else {
           Tok->setType(TT_InheritanceColon);
+          if (Prev->isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected))
+            Line.Type = LT_AccessModifier;
         }
       } else if (canBeObjCSelectorComponent(*Tok->Previous) && Tok->Next &&
                  (Tok->Next->isOneOf(tok::r_paren, tok::comma) ||
@@ -1970,7 +1972,6 @@ class AnnotatingParser {
       }
     }
 
-    bool SeenAccessModifier = false;
     bool KeywordVirtualFound = false;
     bool ImportStatement = false;
 
@@ -1979,9 +1980,7 @@ class AnnotatingParser {
       ImportStatement = true;
 
     while (CurrentToken) {
-      if (CurrentToken->isAccessSpecifier())
-        SeenAccessModifier = true;
-      else if (CurrentToken->is(tok::kw_virtual))
+      if (CurrentToken->is(tok::kw_virtual))
         KeywordVirtualFound = true;
       if (Style.isJavaScript()) {
         // export {...} from '...';
@@ -2001,7 +2000,7 @@ class AnnotatingParser {
       if (!consumeToken())
         return LT_Invalid;
     }
-    if (SeenAccessModifier)
+    if (Line.Type == LT_AccessModifier)
       return LT_AccessModifier;
     if (KeywordVirtualFound)
       return LT_VirtualFunctionDecl;
diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index 136880eca718b..7a76f72c8a88a 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -22,7 +22,8 @@ namespace format {
 
 enum LineType {
   LT_Invalid,
-  LT_AccessModifier, // Contains public/protected/private followed by colon.
+  // Contains public/private/protected followed by TT_InheritanceColon.
+  LT_AccessModifier,
   LT_ImportStatement,
   LT_ObjCDecl, // An @interface, @implementation, or @protocol line.
   LT_ObjCMethodDecl,

>From a292d93569b8793c20318048405d7463c8c55382 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Sun, 16 Jun 2024 03:29:11 -0700
Subject: [PATCH 4/5] Initialize `AnnotatedLine::Type` in the ctor.

---
 clang/lib/Format/TokenAnnotator.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index 7a76f72c8a88a..08947918a51c0 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -47,7 +47,7 @@ enum ScopeType {
 class AnnotatedLine {
 public:
   AnnotatedLine(const UnwrappedLine &Line)
-      : First(Line.Tokens.front().Tok), Level(Line.Level),
+      : First(Line.Tokens.front().Tok), Type(LT_Invalid), Level(Line.Level),
         PPLevel(Line.PPLevel),
         MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex),
         MatchingClosingBlockLineIndex(Line.MatchingClosingBlockLineIndex),

>From 9f82493e6dcbfbd701f1a30f15eccc06f746bcb9 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Sun, 16 Jun 2024 13:09:40 -0700
Subject: [PATCH 5/5] Initialize `Line.Type` to `LT_Other` instead.

---
 clang/lib/Format/TokenAnnotator.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index 08947918a51c0..f4f2bba0eb217 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -47,7 +47,7 @@ enum ScopeType {
 class AnnotatedLine {
 public:
   AnnotatedLine(const UnwrappedLine &Line)
-      : First(Line.Tokens.front().Tok), Type(LT_Invalid), Level(Line.Level),
+      : First(Line.Tokens.front().Tok), Type(LT_Other), Level(Line.Level),
         PPLevel(Line.PPLevel),
         MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex),
         MatchingClosingBlockLineIndex(Line.MatchingClosingBlockLineIndex),



More information about the cfe-commits mailing list