[clang] [clang-format] Ignore imports in comments for Java import sorting (PR #177326)

via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 9 23:58:20 PST 2026


https://github.com/nataliakokoromyti updated https://github.com/llvm/llvm-project/pull/177326

>From e281c4373cbb56cdd226a8bf7f9af681564d5c08 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Thu, 22 Jan 2026 01:35:54 -0800
Subject: [PATCH 01/22] [clang-format]  java import sorting should ignore
 imports in comments and text blocks

---
 clang/lib/Format/Format.cpp                   | 30 ++++++++++++++++++-
 .../unittests/Format/SortImportsTestJava.cpp  | 26 ++++++++++++++++
 2 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index f0e9aff2fd21a..58dfae897ed0f 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3757,6 +3757,8 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
   SmallVector<StringRef> AssociatedCommentLines;
 
   bool FormattingOff = false;
+  bool InBlockComment = false;
+  bool InTextBlock = false;
 
   for (;;) {
     auto Pos = Code.find('\n', SearchFrom);
@@ -3769,7 +3771,33 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     else if (isClangFormatOn(Trimmed))
       FormattingOff = false;
 
-    if (ImportRegex.match(Line, &Matches)) {
+    // Track block comments (/* ... */)
+    // Check if we're starting a block comment on this line
+    bool IsBlockComment = false;
+    if (Trimmed.starts_with("/*")) {
+      IsBlockComment = true;
+      if (!Trimmed.contains("*/"))
+        InBlockComment = true;
+    }
+    // Check if we're ending a block comment that started on a previous line
+    if (InBlockComment && Trimmed.contains("*/")) {
+      InBlockComment = false;
+      IsBlockComment = true;
+    }
+    // If we're in a multi-line block comment (not the first or last line)
+    if (InBlockComment && !Trimmed.starts_with("/*"))
+      IsBlockComment = true;
+
+    // Track Java text blocks (""" ... """)
+    size_t Count = 0;
+    size_t StartPos = 0;
+    while ((StartPos = Trimmed.find("\"\"\"", StartPos)) != StringRef::npos) {
+      ++Count;
+      StartPos += 3;
+    }
+    if (Count % 2 == 1)
+      InTextBlock = !InTextBlock;
+    if (!IsBlockComment && !InTextBlock && ImportRegex.match(Line, &Matches)) {
       if (FormattingOff) {
         // If at least one import line has formatting turned off, turn off
         // formatting entirely.
diff --git a/clang/unittests/Format/SortImportsTestJava.cpp b/clang/unittests/Format/SortImportsTestJava.cpp
index 26674c75e97b1..9af9d8860fe73 100644
--- a/clang/unittests/Format/SortImportsTestJava.cpp
+++ b/clang/unittests/Format/SortImportsTestJava.cpp
@@ -349,6 +349,32 @@ TEST_F(SortImportsTestJava, NoReplacementsForValidImportsWindows) {
       sortIncludes(FmtStyle, Code, GetCodeRange(Code), "input.java").empty());
 }
 
+TEST_F(SortImportsTestJava, DoNotSortImportsInBlockComment) {
+  EXPECT_EQ("/* import org.d;\n"
+            "import org.c;\n"
+            "import org.b; */\n"
+            "import org.a;",
+            sort("/* import org.d;\n"
+                 "import org.c;\n"
+                 "import org.b; */\n"
+                 "import org.a;"));
+}
+
+TEST_F(SortImportsTestJava, DoNotSortImportsInTextBlock) {
+  EXPECT_EQ("String code = \"\"\"\n"
+            "    import org.c;\n"
+            "    \\\"\"\"\n"
+            "    import org.b;\n"
+            "\\\\\"\"\";\n"
+            "import org.a;",
+            sort("String code = \"\"\"\n"
+                 "    import org.c;\n"
+                 "    \\\"\"\"\n"
+                 "    import org.b;\n"
+                 "\\\\\"\"\";\n"
+                 "import org.a;"));
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang

>From bd865fcd182a9e45053713485760f6b616b41b25 Mon Sep 17 00:00:00 2001
From: Natalia Kokoromyti <knatalia at yost-cm-01-imme.stanford.edu>
Date: Sun, 25 Jan 2026 02:07:27 -0800
Subject: [PATCH 02/22] fix

---
 clang/lib/Format/Format.cpp                   | 39 ++++++++-----------
 .../unittests/Format/SortImportsTestJava.cpp  | 30 +++++++-------
 2 files changed, 33 insertions(+), 36 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 58dfae897ed0f..40ce229b9844f 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3743,6 +3743,10 @@ namespace {
 const char JavaImportRegexPattern[] =
     "^[\t ]*import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;";
 
+const char JavaTypeDeclRegexPattern[] =
+    "^[\t ]*(public|private|protected|static|final|abstract|sealed|strictfp)?"
+    "[\t ]*(class|interface|enum|record|@interface)[\t ]+";
+
 } // anonymous namespace
 
 tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
@@ -3752,13 +3756,12 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
   unsigned Prev = 0;
   unsigned SearchFrom = 0;
   llvm::Regex ImportRegex(JavaImportRegexPattern);
+  llvm::Regex TypeDeclRegex(JavaTypeDeclRegexPattern);
   SmallVector<StringRef, 4> Matches;
   SmallVector<JavaImportDirective, 16> ImportsInBlock;
   SmallVector<StringRef> AssociatedCommentLines;
 
   bool FormattingOff = false;
-  bool InBlockComment = false;
-  bool InTextBlock = false;
 
   for (;;) {
     auto Pos = Code.find('\n', SearchFrom);
@@ -3771,33 +3774,23 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     else if (isClangFormatOn(Trimmed))
       FormattingOff = false;
 
-    // Track block comments (/* ... */)
-    // Check if we're starting a block comment on this line
+    // Track block comments (/* ... */).
     bool IsBlockComment = false;
     if (Trimmed.starts_with("/*")) {
       IsBlockComment = true;
-      if (!Trimmed.contains("*/"))
-        InBlockComment = true;
-    }
-    // Check if we're ending a block comment that started on a previous line
-    if (InBlockComment && Trimmed.contains("*/")) {
-      InBlockComment = false;
-      IsBlockComment = true;
+      // Only skip multi-line comments if we haven't started collecting imports yet.
+      // Comments between imports should be associated with the import below.
+      if (ImportsInBlock.empty()) {
+        Pos = Code.find("*/", SearchFrom + 2);
+      }
     }
-    // If we're in a multi-line block comment (not the first or last line)
-    if (InBlockComment && !Trimmed.starts_with("/*"))
-      IsBlockComment = true;
 
-    // Track Java text blocks (""" ... """)
-    size_t Count = 0;
-    size_t StartPos = 0;
-    while ((StartPos = Trimmed.find("\"\"\"", StartPos)) != StringRef::npos) {
-      ++Count;
-      StartPos += 3;
+    // Check if we've encountered a type declaration - we're past imports.
+    if (!IsBlockComment && TypeDeclRegex.match(Trimmed)) {
+      break;
     }
-    if (Count % 2 == 1)
-      InTextBlock = !InTextBlock;
-    if (!IsBlockComment && !InTextBlock && ImportRegex.match(Line, &Matches)) {
+
+    if (!IsBlockComment && ImportRegex.match(Line, &Matches)) {
       if (FormattingOff) {
         // If at least one import line has formatting turned off, turn off
         // formatting entirely.
diff --git a/clang/unittests/Format/SortImportsTestJava.cpp b/clang/unittests/Format/SortImportsTestJava.cpp
index 9af9d8860fe73..1b2b0654cfdae 100644
--- a/clang/unittests/Format/SortImportsTestJava.cpp
+++ b/clang/unittests/Format/SortImportsTestJava.cpp
@@ -360,19 +360,23 @@ TEST_F(SortImportsTestJava, DoNotSortImportsInBlockComment) {
                  "import org.a;"));
 }
 
-TEST_F(SortImportsTestJava, DoNotSortImportsInTextBlock) {
-  EXPECT_EQ("String code = \"\"\"\n"
-            "    import org.c;\n"
-            "    \\\"\"\"\n"
-            "    import org.b;\n"
-            "\\\\\"\"\";\n"
-            "import org.a;",
-            sort("String code = \"\"\"\n"
-                 "    import org.c;\n"
-                 "    \\\"\"\"\n"
-                 "    import org.b;\n"
-                 "\\\\\"\"\";\n"
-                 "import org.a;"));
+TEST_F(SortImportsTestJava, StopAtClassDeclaration) {
+  EXPECT_EQ("import org.a;\n"
+            "\n"
+            "class Foo {\n"
+            "  String code = \"\"\"\n"
+            "      import org.c;\n"
+            "      import org.b;\n"
+            "  \"\"\";\n"
+            "}",
+            sort("import org.a;\n"
+                 "\n"
+                 "class Foo {\n"
+                 "  String code = \"\"\"\n"
+                 "      import org.c;\n"
+                 "      import org.b;\n"
+                 "  \"\"\";\n"
+                 "}"));
 }
 
 } // end namespace

>From ee14636f8f69aa1e73f160fa584a13cccda9076b Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 26 Jan 2026 00:27:08 -0800
Subject: [PATCH 03/22] fix format

---
 clang/lib/Format/Format.cpp | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 40ce229b9844f..07580f7f3311e 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3778,17 +3778,14 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     bool IsBlockComment = false;
     if (Trimmed.starts_with("/*")) {
       IsBlockComment = true;
-      // Only skip multi-line comments if we haven't started collecting imports yet.
-      // Comments between imports should be associated with the import below.
-      if (ImportsInBlock.empty()) {
+      // Skip block comments before imports start.
+      if (ImportsInBlock.empty())
         Pos = Code.find("*/", SearchFrom + 2);
-      }
     }
 
-    // Check if we've encountered a type declaration - we're past imports.
-    if (!IsBlockComment && TypeDeclRegex.match(Trimmed)) {
+    // Check if we've encountered a type declaration.
+    if (!IsBlockComment && TypeDeclRegex.match(Trimmed))
       break;
-    }
 
     if (!IsBlockComment && ImportRegex.match(Line, &Matches)) {
       if (FormattingOff) {

>From d3ceb2220c68629af2457b03c7286127c709889c Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 26 Jan 2026 00:43:41 -0800
Subject: [PATCH 04/22] simplify regexpattern

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

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 07580f7f3311e..dbae8d36b620d 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3744,7 +3744,7 @@ const char JavaImportRegexPattern[] =
     "^[\t ]*import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;";
 
 const char JavaTypeDeclRegexPattern[] =
-    "^[\t ]*(public|private|protected|static|final|abstract|sealed|strictfp)?"
+    "^[\t ]*(public|private|protected|static|final|abstract)*"
     "[\t ]*(class|interface|enum|record|@interface)[\t ]+";
 
 } // anonymous namespace

>From 0446edfe571fba5912fdc04ded62791ff8d78dde Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 2 Feb 2026 02:23:13 -0800
Subject: [PATCH 05/22] rm JavaTypeDeclRegexPattern + handle trimmed.empty() at
 top

---
 clang/lib/Format/Format.cpp | 41 ++++++++++++++++++-------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index dbae8d36b620d..1879b7c9ee60a 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3743,9 +3743,7 @@ namespace {
 const char JavaImportRegexPattern[] =
     "^[\t ]*import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;";
 
-const char JavaTypeDeclRegexPattern[] =
-    "^[\t ]*(public|private|protected|static|final|abstract)*"
-    "[\t ]*(class|interface|enum|record|@interface)[\t ]+";
+const char JavaPackageRegexPattern[] = "^[\t ]*package[\t ]+[^;]+;";
 
 } // anonymous namespace
 
@@ -3756,7 +3754,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
   unsigned Prev = 0;
   unsigned SearchFrom = 0;
   llvm::Regex ImportRegex(JavaImportRegexPattern);
-  llvm::Regex TypeDeclRegex(JavaTypeDeclRegexPattern);
+  llvm::Regex PackageRegex(JavaPackageRegexPattern);
   SmallVector<StringRef, 4> Matches;
   SmallVector<JavaImportDirective, 16> ImportsInBlock;
   SmallVector<StringRef> AssociatedCommentLines;
@@ -3774,20 +3772,22 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     else if (isClangFormatOn(Trimmed))
       FormattingOff = false;
 
-    // Track block comments (/* ... */).
-    bool IsBlockComment = false;
-    if (Trimmed.starts_with("/*")) {
-      IsBlockComment = true;
-      // Skip block comments before imports start.
-      if (ImportsInBlock.empty())
-        Pos = Code.find("*/", SearchFrom + 2);
-    }
-
-    // Check if we've encountered a type declaration.
-    if (!IsBlockComment && TypeDeclRegex.match(Trimmed))
-      break;
-
-    if (!IsBlockComment && ImportRegex.match(Line, &Matches)) {
+    if (Trimmed.empty()) {
+      // Skip empty lines.
+    } else if (Trimmed.starts_with("//")) {
+      if (!ImportsInBlock.empty())
+        AssociatedCommentLines.push_back(Line);
+    } else if (Trimmed.starts_with("/*")) {
+      auto EndPos = Code.find("*/", SearchFrom + 2);
+      if (EndPos != StringRef::npos) {
+        Line = Code.substr(Prev, EndPos + 2 - Prev);
+        Pos = EndPos + 1;
+      }
+      if (!ImportsInBlock.empty())
+        AssociatedCommentLines.push_back(Line);
+    } else if (PackageRegex.match(Trimmed)) {
+      // Skip package declarations.
+    } else if (ImportRegex.match(Trimmed, &Matches)) {
       if (FormattingOff) {
         // If at least one import line has formatting turned off, turn off
         // formatting entirely.
@@ -3801,9 +3801,8 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       ImportsInBlock.push_back(
           {Identifier, Line, Prev, AssociatedCommentLines, IsStatic});
       AssociatedCommentLines.clear();
-    } else if (!Trimmed.empty() && !ImportsInBlock.empty()) {
-      // Associating comments within the imports with the nearest import below
-      AssociatedCommentLines.push_back(Line);
+    } else {
+      break;
     }
     Prev = Pos + 1;
     if (Pos == StringRef::npos || Pos + 1 == Code.size())

>From a775a5c401d57208464b356a6e871abf9e6f6cb3 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Sat, 7 Feb 2026 15:11:01 -0800
Subject: [PATCH 06/22] fix regex

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 1879b7c9ee60a..a4ae7bb797119 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3740,10 +3740,10 @@ static void sortJavaImports(const FormatStyle &Style,
 
 namespace {
 
-const char JavaImportRegexPattern[] =
-    "^[\t ]*import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;";
+constexpr StringRef JavaImportRegexPattern(
+"^import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;");
 
-const char JavaPackageRegexPattern[] = "^[\t ]*package[\t ]+[^;]+;";
+constexpr StringRef JavaPackageRegexPattern("^package[\t ]+");
 
 } // anonymous namespace
 

>From ddca59c48fccc497b5b4df901a630606d94176a1 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Sat, 7 Feb 2026 15:33:32 -0800
Subject: [PATCH 07/22] fix formatting

---
 clang/lib/Format/Format.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index a4ae7bb797119..387711d6af712 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3740,8 +3740,8 @@ static void sortJavaImports(const FormatStyle &Style,
 
 namespace {
 
-constexpr StringRef JavaImportRegexPattern(
-"^import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;");
+constexpr StringRef 
+    JavaImportRegexPattern("^import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;");
 
 constexpr StringRef JavaPackageRegexPattern("^package[\t ]+");
 

>From 0d1ac13b1d764bfcb0cff9f645c27ee2e75119ef Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Sat, 7 Feb 2026 15:49:32 -0800
Subject: [PATCH 08/22] fix formatting

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

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 387711d6af712..352e7c2723f1f 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3740,7 +3740,7 @@ static void sortJavaImports(const FormatStyle &Style,
 
 namespace {
 
-constexpr StringRef 
+constexpr StringRef
     JavaImportRegexPattern("^import[\t ]+(static[\t ]*)?([^\t ]*)[\t ]*;");
 
 constexpr StringRef JavaPackageRegexPattern("^package[\t ]+");

>From 5f93956bed006ab283b674d22395d6fe6e32cb0f Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Sat, 7 Feb 2026 16:04:58 -0800
Subject: [PATCH 09/22] move trimmed.empty() to top

---
 clang/lib/Format/Format.cpp | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 352e7c2723f1f..a71da41ed5938 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3766,15 +3766,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     StringRef Line =
         Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
 
-    StringRef Trimmed = Line.trim();
-    if (isClangFormatOff(Trimmed))
-      FormattingOff = true;
-    else if (isClangFormatOn(Trimmed))
-      FormattingOff = false;
-
-    if (Trimmed.empty()) {
-      // Skip empty lines.
-    } else if (Trimmed.starts_with("//")) {
+    StringRef Trimmed = Line.trim();`r`n    if (Trimmed.empty()) {`r`n      // Skip empty lines.`r`n    } else if (isClangFormatOff(Trimmed)) {`r`n      FormattingOff = true;`r`n    } else if (isClangFormatOn(Trimmed)) {`r`n      FormattingOff = false;`r`n    } else if (Trimmed.starts_with("//")) {
       if (!ImportsInBlock.empty())
         AssociatedCommentLines.push_back(Line);
     } else if (Trimmed.starts_with("/*")) {

>From 8b4fbe9019be18989750d8a1d359f91c71ce7fb4 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Sat, 7 Feb 2026 16:05:00 -0800
Subject: [PATCH 10/22] fix newline

---
 clang/lib/Format/Format.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index a71da41ed5938..b9fa503d00c24 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3766,7 +3766,14 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     StringRef Line =
         Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
 
-    StringRef Trimmed = Line.trim();`r`n    if (Trimmed.empty()) {`r`n      // Skip empty lines.`r`n    } else if (isClangFormatOff(Trimmed)) {`r`n      FormattingOff = true;`r`n    } else if (isClangFormatOn(Trimmed)) {`r`n      FormattingOff = false;`r`n    } else if (Trimmed.starts_with("//")) {
+    StringRef Trimmed = Line.trim();
+    if (Trimmed.empty()) {
+      // Skip empty lines.
+    } else if (isClangFormatOff(Trimmed)) {
+      FormattingOff = true;
+    } else if (isClangFormatOn(Trimmed)) {
+      FormattingOff = false;
+    } else if (Trimmed.starts_with("//")) {
       if (!ImportsInBlock.empty())
         AssociatedCommentLines.push_back(Line);
     } else if (Trimmed.starts_with("/*")) {

>From 5c3449e9780d8f41de71d3f9dbfad2549ac81b5a Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:18:32 -0800
Subject: [PATCH 11/22] keep original comment

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index b9fa503d00c24..ef5f2ef3455b7 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3774,7 +3774,8 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     } else if (isClangFormatOn(Trimmed)) {
       FormattingOff = false;
     } else if (Trimmed.starts_with("//")) {
-      if (!ImportsInBlock.empty())
+      // Associating comments within the imports with the nearest import below.
+      if (HasImport)
         AssociatedCommentLines.push_back(Line);
     } else if (Trimmed.starts_with("/*")) {
       auto EndPos = Code.find("*/", SearchFrom + 2);

>From cb0b54fda6da289fca5c2d33ad82cae702d3a09d Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:19:03 -0800
Subject: [PATCH 12/22] set HasImport

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index ef5f2ef3455b7..cd4736fb8f8bc 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3800,6 +3800,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
         IsStatic = true;
       ImportsInBlock.push_back(
           {Identifier, Line, Prev, AssociatedCommentLines, IsStatic});
+      HasImport = true;
       AssociatedCommentLines.clear();
     } else {
       break;

>From 6474762cc073aa45146cfeb1bf78a0d4c7f9a7be Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:28:47 -0800
Subject: [PATCH 13/22] rm SearchFrom + HasImport decl

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index cd4736fb8f8bc..d770cf31e6a53 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3752,7 +3752,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
                                       StringRef FileName,
                                       tooling::Replacements &Replaces) {
   unsigned Prev = 0;
-  unsigned SearchFrom = 0;
+  bool HasImport = false;
   llvm::Regex ImportRegex(JavaImportRegexPattern);
   llvm::Regex PackageRegex(JavaPackageRegexPattern);
   SmallVector<StringRef, 4> Matches;

>From c98d19c83c9f6f31ef5930b97c974fda5a0b7082 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:29:11 -0800
Subject: [PATCH 14/22] Update clang/lib/Format/Format.cpp

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index d770cf31e6a53..f0edfc76d8265 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3810,7 +3810,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       break;
     SearchFrom = Pos + 1;
   }
-  if (!ImportsInBlock.empty())
+  if (HasImport)
     sortJavaImports(Style, ImportsInBlock, Ranges, FileName, Code, Replaces);
   return Replaces;
 }

>From c5692e56491192d0d0df331bb97fdc73298428df Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:29:23 -0800
Subject: [PATCH 15/22] Update clang/lib/Format/Format.cpp

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index f0edfc76d8265..ea61b7b0e3ac4 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3778,15 +3778,15 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       if (HasImport)
         AssociatedCommentLines.push_back(Line);
     } else if (Trimmed.starts_with("/*")) {
-      auto EndPos = Code.find("*/", SearchFrom + 2);
-      if (EndPos != StringRef::npos) {
-        Line = Code.substr(Prev, EndPos + 2 - Prev);
-        Pos = EndPos + 1;
-      }
-      if (!ImportsInBlock.empty())
+      Pos = Code.find("*/", Pos + 2);
+      if (Pos != StringRef::npos)
+        Pos = Code.find('\n', Pos + 2);
+      if (HasImport) {
+        // Extend `Line` for a multiline comment to include all lines the
+        // comment spans.
+        Line = GetLine();
         AssociatedCommentLines.push_back(Line);
-    } else if (PackageRegex.match(Trimmed)) {
-      // Skip package declarations.
+      }
     } else if (ImportRegex.match(Trimmed, &Matches)) {
       if (FormattingOff) {
         // If at least one import line has formatting turned off, turn off

>From 483eb72f4e57a125bc1c14ef610e7e874107cdfd Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:29:35 -0800
Subject: [PATCH 16/22] Update clang/lib/Format/Format.cpp

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index ea61b7b0e3ac4..16178b0d8dbda 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3767,8 +3767,8 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
         Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
 
     StringRef Trimmed = Line.trim();
-    if (Trimmed.empty()) {
-      // Skip empty lines.
+    if (Trimmed.empty() || PackageRegex.match(Trimmed)) {
+      // Skip empty line and package statement.
     } else if (isClangFormatOff(Trimmed)) {
       FormattingOff = true;
     } else if (isClangFormatOn(Trimmed)) {

>From 22de9b38893050a78ac3ba1aaaee623ed02cbbb5 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:29:45 -0800
Subject: [PATCH 17/22] Update clang/lib/Format/Format.cpp

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 16178b0d8dbda..fbcf0421c94ce 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3803,6 +3803,8 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       HasImport = true;
       AssociatedCommentLines.clear();
     } else {
+      // `Trimmed` is neither empty, nor a comment or a package/import
+      // statement.
       break;
     }
     Prev = Pos + 1;

>From 6894468478f4c976ca0698abe67fe906371073cb Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:29:56 -0800
Subject: [PATCH 18/22] Update clang/unittests/Format/SortImportsTestJava.cpp

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/unittests/Format/SortImportsTestJava.cpp | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/clang/unittests/Format/SortImportsTestJava.cpp b/clang/unittests/Format/SortImportsTestJava.cpp
index 1b2b0654cfdae..68838cd90961d 100644
--- a/clang/unittests/Format/SortImportsTestJava.cpp
+++ b/clang/unittests/Format/SortImportsTestJava.cpp
@@ -350,14 +350,11 @@ TEST_F(SortImportsTestJava, NoReplacementsForValidImportsWindows) {
 }
 
 TEST_F(SortImportsTestJava, DoNotSortImportsInBlockComment) {
-  EXPECT_EQ("/* import org.d;\n"
-            "import org.c;\n"
-            "import org.b; */\n"
-            "import org.a;",
-            sort("/* import org.d;\n"
-                 "import org.c;\n"
-                 "import org.b; */\n"
-                 "import org.a;"));
+  constexpr StringRef Code("/* import org.d;\n"
+                           "import org.c;\n"
+                           "import org.b; */\n"
+                           "import org.a;");
+  EXPECT_EQ(Code, sort(Code));
 }
 
 TEST_F(SortImportsTestJava, StopAtClassDeclaration) {

>From 23f28e75d862698066ec44e50e0a454dfd9750d1 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:30:17 -0800
Subject: [PATCH 19/22] Update clang/lib/Format/Format.cpp

Co-authored-by: owenca <owenpiano at gmail.com>
---
 clang/lib/Format/Format.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index fbcf0421c94ce..4ccf4f865e1eb 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3810,7 +3810,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
     Prev = Pos + 1;
     if (Pos == StringRef::npos || Pos + 1 == Code.size())
       break;
-    SearchFrom = Pos + 1;
+    Prev = Pos + 1;
   }
   if (HasImport)
     sortJavaImports(Style, ImportsInBlock, Ranges, FileName, Code, Replaces);

>From 8b6dca095837fa950d867b07cc2e8961307e1943 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:41:35 -0800
Subject: [PATCH 20/22] rm SearchFrom usage everywhere

---
 clang/lib/Format/Format.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 4ccf4f865e1eb..ed3a8e45c2c19 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3762,7 +3762,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
   bool FormattingOff = false;
 
   for (;;) {
-    auto Pos = Code.find('\n', SearchFrom);
+    auto Pos = Code.find('\n', Prev);
     StringRef Line =
         Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
 
@@ -3778,13 +3778,14 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       if (HasImport)
         AssociatedCommentLines.push_back(Line);
     } else if (Trimmed.starts_with("/*")) {
-      Pos = Code.find("*/", Pos + 2);
-      if (Pos != StringRef::npos)
-        Pos = Code.find('\n', Pos + 2);
+      const auto EndPos = Code.find("*/", Prev + 2);
+      Pos = EndPos != StringRef::npos ? Code.find('\n', EndPos + 2)
+                                      : StringRef::npos;
       if (HasImport) {
         // Extend `Line` for a multiline comment to include all lines the
         // comment spans.
-        Line = GetLine();
+        Line =
+            Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
         AssociatedCommentLines.push_back(Line);
       }
     } else if (ImportRegex.match(Trimmed, &Matches)) {
@@ -3807,7 +3808,6 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       // statement.
       break;
     }
-    Prev = Pos + 1;
     if (Pos == StringRef::npos || Pos + 1 == Code.size())
       break;
     Prev = Pos + 1;

>From 3ade17fbaae4929b78935fbf9357529393cdc628 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:54:12 -0800
Subject: [PATCH 21/22] small refactor

---
 clang/lib/Format/Format.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index ed3a8e45c2c19..51e880fb69169 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3759,12 +3759,13 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
   SmallVector<JavaImportDirective, 16> ImportsInBlock;
   SmallVector<StringRef> AssociatedCommentLines;
 
-  bool FormattingOff = false;
-
-  for (;;) {
+  for (bool FormattingOff = false;;) {
     auto Pos = Code.find('\n', Prev);
-    StringRef Line =
-        Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
+    auto GetLine = [&] {
+      return Code.substr(Prev,
+                         (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
+    };
+    StringRef Line = GetLine();
 
     StringRef Trimmed = Line.trim();
     if (Trimmed.empty() || PackageRegex.match(Trimmed)) {
@@ -3784,8 +3785,7 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
       if (HasImport) {
         // Extend `Line` for a multiline comment to include all lines the
         // comment spans.
-        Line =
-            Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
+        Line = GetLine();
         AssociatedCommentLines.push_back(Line);
       }
     } else if (ImportRegex.match(Trimmed, &Matches)) {

>From d4ded3135d780323870bf50733275bc6b4d65365 Mon Sep 17 00:00:00 2001
From: nataliakokoromyti <nataliakokoromyti at gmail.com>
Date: Mon, 9 Feb 2026 23:57:43 -0800
Subject: [PATCH 22/22] add test that starts with package statement

---
 clang/unittests/Format/SortImportsTestJava.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/clang/unittests/Format/SortImportsTestJava.cpp b/clang/unittests/Format/SortImportsTestJava.cpp
index 68838cd90961d..166540e448627 100644
--- a/clang/unittests/Format/SortImportsTestJava.cpp
+++ b/clang/unittests/Format/SortImportsTestJava.cpp
@@ -376,6 +376,17 @@ TEST_F(SortImportsTestJava, StopAtClassDeclaration) {
                  "}"));
 }
 
+TEST_F(SortImportsTestJava, SortImportsAfterPackageStatement) {
+  EXPECT_EQ("package org.a;\n"
+            "\n"
+            "import org.a;\n"
+            "import org.b;\n",
+            sort("package org.a;\n"
+                 "\n"
+                 "import org.b;\n"
+                 "import org.a;\n"));
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang



More information about the cfe-commits mailing list