[clang] [clang-format] java import sorting should ignore imports in comments and text blocks (PR #177326)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 02:23:38 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 1/5] [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 2/5] 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 3/5] 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 4/5] 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 5/5] 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())
More information about the cfe-commits
mailing list