[clang-tools-extra] Follow style configuration in clangd when inserting missing includes (PR #140594)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 29 11:02:13 PDT 2025
https://github.com/Harald-R updated https://github.com/llvm/llvm-project/pull/140594
>From 2e261ba9fa08f7c482f9c9e11385d0975468b76f Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Sun, 18 May 2025 19:07:59 +0300
Subject: [PATCH 1/8] Follow style configuration in clangd include cleaner
---
clang-tools-extra/clangd/IncludeCleaner.cpp | 21 ++++++++++++-------
clang-tools-extra/clangd/IncludeCleaner.h | 9 ++++----
clang-tools-extra/clangd/ParsedAST.cpp | 3 ++-
.../clangd/unittests/IncludeCleanerTests.cpp | 15 +++++++++++--
4 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index dc4c8fc498b1f..ce16d060a1f06 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -117,7 +117,8 @@ bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
std::vector<Diag> generateMissingIncludeDiagnostics(
ParsedAST &AST, llvm::ArrayRef<MissingIncludeDiagInfo> MissingIncludes,
- llvm::StringRef Code, HeaderFilter IgnoreHeaders, const ThreadsafeFS &TFS) {
+ llvm::StringRef Code, HeaderFilter IgnoreHeaders,
+ HeaderFilter AngledHeaders, const ThreadsafeFS &TFS) {
std::vector<Diag> Result;
const SourceManager &SM = AST.getSourceManager();
const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
@@ -142,6 +143,13 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
llvm::StringRef HeaderRef{Spelling};
bool Angled = HeaderRef.starts_with("<");
+ for (auto Filter : AngledHeaders) {
+ if (Filter(HeaderRef)) {
+ Angled = true;
+ break;
+ }
+ }
+
// We might suggest insertion of an existing include in edge cases, e.g.,
// include is present in a PP-disabled region, or spelling of the header
// turns out to be the same as one of the unresolved includes in the
@@ -481,18 +489,17 @@ bool isPreferredProvider(const Inclusion &Inc,
return false; // no header provides the symbol
}
-std::vector<Diag>
-issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code,
- const IncludeCleanerFindings &Findings,
- const ThreadsafeFS &TFS,
- HeaderFilter IgnoreHeaders) {
+std::vector<Diag> issueIncludeCleanerDiagnostics(
+ ParsedAST &AST, llvm::StringRef Code,
+ const IncludeCleanerFindings &Findings, const ThreadsafeFS &TFS,
+ HeaderFilter IgnoreHeaders, HeaderFilter AngledHeaders) {
trace::Span Tracer("IncludeCleaner::issueIncludeCleanerDiagnostics");
std::vector<Diag> UnusedIncludes = generateUnusedIncludeDiagnostics(
AST.tuPath(), Findings.UnusedIncludes, Code, IgnoreHeaders);
std::optional<Fix> RemoveAllUnused = removeAllUnusedIncludes(UnusedIncludes);
std::vector<Diag> MissingIncludeDiags = generateMissingIncludeDiagnostics(
- AST, Findings.MissingIncludes, Code, IgnoreHeaders, TFS);
+ AST, Findings.MissingIncludes, Code, IgnoreHeaders, AngledHeaders, TFS);
std::optional<Fix> AddAllMissing = addAllMissingIncludes(MissingIncludeDiags);
std::optional<Fix> FixAll;
diff --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h
index 3f6e3b2fd45b6..884feb69488d5 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.h
+++ b/clang-tools-extra/clangd/IncludeCleaner.h
@@ -57,11 +57,10 @@ IncludeCleanerFindings
computeIncludeCleanerFindings(ParsedAST &AST,
bool AnalyzeAngledIncludes = false);
-std::vector<Diag>
-issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code,
- const IncludeCleanerFindings &Findings,
- const ThreadsafeFS &TFS,
- HeaderFilter IgnoreHeader = {});
+std::vector<Diag> issueIncludeCleanerDiagnostics(
+ ParsedAST &AST, llvm::StringRef Code,
+ const IncludeCleanerFindings &Findings, const ThreadsafeFS &TFS,
+ HeaderFilter IgnoreHeader = {}, HeaderFilter AngledHeaders = {});
/// Converts the clangd include representation to include-cleaner
/// include representation.
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index 9e1f6bb977226..4fedea35334f6 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -382,7 +382,8 @@ std::vector<Diag> getIncludeCleanerDiags(ParsedAST &AST, llvm::StringRef Code,
if (SuppressUnused)
Findings.UnusedIncludes.clear();
return issueIncludeCleanerDiagnostics(AST, Code, Findings, TFS,
- Cfg.Diagnostics.Includes.IgnoreHeader);
+ Cfg.Diagnostics.Includes.IgnoreHeader,
+ Cfg.Style.AngledHeaders);
}
tidy::ClangTidyCheckFactories
diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index 0ee748c1ed2d0..fda1b3b1835ae 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -220,12 +220,13 @@ TEST(IncludeCleaner, ComputeMissingHeaders) {
TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
Annotations MainFile(R"cpp(
#include "a.h"
+#include "a_angled.h"
#include "all.h"
$insert_b[[]]#include "baz.h"
#include "dir/c.h"
$insert_d[[]]$insert_foo[[]]#include "fuzz.h"
#include "header.h"
-$insert_foobar[[]]#include <e.h>
+$insert_foobar[[]]$insert_b_angled[[]]#include <e.h>
$insert_f[[]]$insert_vector[[]]
#define DEF(X) const Foo *X;
@@ -237,6 +238,7 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
void foo() {
$b[[b]]();
+ $b_angled[[b_angled]]();
ns::$bar[[Bar]] bar;
bar.d();
@@ -262,6 +264,8 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
TU.Filename = "main.cpp";
TU.AdditionalFiles["a.h"] = guard("#include \"b.h\"");
TU.AdditionalFiles["b.h"] = guard("void b();");
+ TU.AdditionalFiles["a_angled.h"] = guard("#include \"b_angled.h\"");
+ TU.AdditionalFiles["b_angled.h"] = guard("void b_angled();");
TU.AdditionalFiles["dir/c.h"] = guard("#include \"d.h\"");
TU.AdditionalFiles["dir/d.h"] =
@@ -297,7 +301,8 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
Findings.UnusedIncludes.clear();
std::vector<clangd::Diag> Diags = issueIncludeCleanerDiagnostics(
AST, TU.Code, Findings, MockFS(),
- {[](llvm::StringRef Header) { return Header.ends_with("buzz.h"); }});
+ {[](llvm::StringRef Header) { return Header.ends_with("buzz.h"); }},
+ {[](llvm::StringRef Header) { return Header.contains("b_angled.h"); }});
EXPECT_THAT(
Diags,
UnorderedElementsAre(
@@ -306,6 +311,12 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
withFix({Fix(MainFile.range("insert_b"), "#include \"b.h\"\n",
"#include \"b.h\""),
FixMessage("add all missing includes")})),
+ AllOf(Diag(MainFile.range("b_angled"),
+ "No header providing \"b_angled\" is directly included"),
+ withFix(
+ {Fix(MainFile.range("insert_b_angled"),
+ "#include <b_angled.h>\n", "#include \"b_angled.h\""),
+ FixMessage("add all missing includes")})),
AllOf(Diag(MainFile.range("bar"),
"No header providing \"ns::Bar\" is directly included"),
withFix({Fix(MainFile.range("insert_d"),
>From 6b91e7d25e9a27eff86e59f406e56df63e2ba7d9 Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Sat, 24 May 2025 15:56:58 +0300
Subject: [PATCH 2/8] Use auto& for iteration
---
clang-tools-extra/clangd/IncludeCleaner.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index ce16d060a1f06..a8fcc6c7dd027 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -143,7 +143,7 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
llvm::StringRef HeaderRef{Spelling};
bool Angled = HeaderRef.starts_with("<");
- for (auto Filter : AngledHeaders) {
+ for (auto &Filter : AngledHeaders) {
if (Filter(HeaderRef)) {
Angled = true;
break;
>From 164513654e5a9b22c9e191b4fdbd2dd5978bf16a Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Sat, 24 May 2025 16:19:36 +0300
Subject: [PATCH 3/8] Add helper comment to test arguments
---
.../clangd/unittests/IncludeCleanerTests.cpp | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index fda1b3b1835ae..a27c340c37923 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -301,8 +301,12 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
Findings.UnusedIncludes.clear();
std::vector<clangd::Diag> Diags = issueIncludeCleanerDiagnostics(
AST, TU.Code, Findings, MockFS(),
- {[](llvm::StringRef Header) { return Header.ends_with("buzz.h"); }},
- {[](llvm::StringRef Header) { return Header.contains("b_angled.h"); }});
+ /*IgnoreHeaders=*/{[](llvm::StringRef Header) {
+ return Header.ends_with("buzz.h");
+ }},
+ /*AngledHeaders=*/{[](llvm::StringRef Header) {
+ return Header.contains("b_angled.h");
+ }});
EXPECT_THAT(
Diags,
UnorderedElementsAre(
>From c0b88cab7f34649feeb02474d1df7f51aa44b007 Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Sat, 24 May 2025 16:26:48 +0300
Subject: [PATCH 4/8] Ensure the message field is also angled
---
clang-tools-extra/clangd/IncludeCleaner.cpp | 5 +++++
.../clangd/unittests/IncludeCleanerTests.cpp | 12 ++++++------
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index a8fcc6c7dd027..92c2ddf250420 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -159,6 +159,11 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
if (!Replacement.has_value())
continue;
+ if (Angled && Spelling.front() == '\"') {
+ Spelling.front() = '<';
+ Spelling.back() = '>';
+ }
+
Diag &D = Result.emplace_back();
D.Message =
llvm::formatv("No header providing \"{0}\" is directly included",
diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index a27c340c37923..bf1540280d5b5 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -315,12 +315,12 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
withFix({Fix(MainFile.range("insert_b"), "#include \"b.h\"\n",
"#include \"b.h\""),
FixMessage("add all missing includes")})),
- AllOf(Diag(MainFile.range("b_angled"),
- "No header providing \"b_angled\" is directly included"),
- withFix(
- {Fix(MainFile.range("insert_b_angled"),
- "#include <b_angled.h>\n", "#include \"b_angled.h\""),
- FixMessage("add all missing includes")})),
+ AllOf(
+ Diag(MainFile.range("b_angled"),
+ "No header providing \"b_angled\" is directly included"),
+ withFix({Fix(MainFile.range("insert_b_angled"),
+ "#include <b_angled.h>\n", "#include <b_angled.h>"),
+ FixMessage("add all missing includes")})),
AllOf(Diag(MainFile.range("bar"),
"No header providing \"ns::Bar\" is directly included"),
withFix({Fix(MainFile.range("insert_d"),
>From e8e828cf4dd5de9a3edf717f181db70bcf7bb72b Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Mon, 26 May 2025 18:50:59 +0300
Subject: [PATCH 5/8] Rename test headers
---
.../clangd/unittests/IncludeCleanerTests.cpp | 25 ++++++++++---------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index bf1540280d5b5..f51e6e7896f39 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -220,13 +220,13 @@ TEST(IncludeCleaner, ComputeMissingHeaders) {
TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
Annotations MainFile(R"cpp(
#include "a.h"
-#include "a_angled.h"
+#include "angled_wrapper.h"
#include "all.h"
$insert_b[[]]#include "baz.h"
#include "dir/c.h"
$insert_d[[]]$insert_foo[[]]#include "fuzz.h"
#include "header.h"
-$insert_foobar[[]]$insert_b_angled[[]]#include <e.h>
+$insert_foobar[[]]$insert_angled[[]]#include <e.h>
$insert_f[[]]$insert_vector[[]]
#define DEF(X) const Foo *X;
@@ -238,7 +238,7 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
void foo() {
$b[[b]]();
- $b_angled[[b_angled]]();
+ $angled[[angled]]();
ns::$bar[[Bar]] bar;
bar.d();
@@ -264,8 +264,10 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
TU.Filename = "main.cpp";
TU.AdditionalFiles["a.h"] = guard("#include \"b.h\"");
TU.AdditionalFiles["b.h"] = guard("void b();");
- TU.AdditionalFiles["a_angled.h"] = guard("#include \"b_angled.h\"");
- TU.AdditionalFiles["b_angled.h"] = guard("void b_angled();");
+
+ TU.AdditionalFiles["angled_wrapper.h"] = guard("#include <angled.h>");
+ TU.AdditionalFiles["angled.h"] = guard("void angled();");
+ TU.ExtraArgs.push_back("-I" + testPath("."));
TU.AdditionalFiles["dir/c.h"] = guard("#include \"d.h\"");
TU.AdditionalFiles["dir/d.h"] =
@@ -305,7 +307,7 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
return Header.ends_with("buzz.h");
}},
/*AngledHeaders=*/{[](llvm::StringRef Header) {
- return Header.contains("b_angled.h");
+ return Header.contains("angled.h");
}});
EXPECT_THAT(
Diags,
@@ -315,12 +317,11 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
withFix({Fix(MainFile.range("insert_b"), "#include \"b.h\"\n",
"#include \"b.h\""),
FixMessage("add all missing includes")})),
- AllOf(
- Diag(MainFile.range("b_angled"),
- "No header providing \"b_angled\" is directly included"),
- withFix({Fix(MainFile.range("insert_b_angled"),
- "#include <b_angled.h>\n", "#include <b_angled.h>"),
- FixMessage("add all missing includes")})),
+ AllOf(Diag(MainFile.range("angled"),
+ "No header providing \"angled\" is directly included"),
+ withFix({Fix(MainFile.range("insert_angled"),
+ "#include <angled.h>\n", "#include <angled.h>"),
+ FixMessage("add all missing includes")})),
AllOf(Diag(MainFile.range("bar"),
"No header providing \"ns::Bar\" is directly included"),
withFix({Fix(MainFile.range("insert_d"),
>From 328025e88eedeba7fc0551678a87f220036073a9 Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Mon, 26 May 2025 20:38:17 +0300
Subject: [PATCH 6/8] Use QuotedHeaders filter in determining inclusion style
---
clang-tools-extra/clangd/IncludeCleaner.cpp | 30 ++++++++++++++-----
clang-tools-extra/clangd/IncludeCleaner.h | 3 +-
clang-tools-extra/clangd/ParsedAST.cpp | 6 ++--
.../clangd/unittests/IncludeCleanerTests.cpp | 16 +++++++++-
4 files changed, 43 insertions(+), 12 deletions(-)
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index 92c2ddf250420..d62003a6a19ee 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -118,7 +118,8 @@ bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
std::vector<Diag> generateMissingIncludeDiagnostics(
ParsedAST &AST, llvm::ArrayRef<MissingIncludeDiagInfo> MissingIncludes,
llvm::StringRef Code, HeaderFilter IgnoreHeaders,
- HeaderFilter AngledHeaders, const ThreadsafeFS &TFS) {
+ HeaderFilter AngledHeaders, HeaderFilter QuotedHeaders,
+ const ThreadsafeFS &TFS) {
std::vector<Diag> Result;
const SourceManager &SM = AST.getSourceManager();
const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
@@ -142,24 +143,37 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
AST.getPreprocessor().getHeaderSearchInfo(), MainFile});
llvm::StringRef HeaderRef{Spelling};
- bool Angled = HeaderRef.starts_with("<");
+
+ bool IsAngled = false;
for (auto &Filter : AngledHeaders) {
if (Filter(HeaderRef)) {
- Angled = true;
+ IsAngled = true;
+ break;
+ }
+ }
+ bool IsQuoted = false;
+ for (auto &Filter : QuotedHeaders) {
+ if (Filter(HeaderRef)) {
+ IsQuoted = true;
break;
}
}
+ if (IsAngled == IsQuoted) {
+ IsAngled = HeaderRef.starts_with("<");
+ } else if (!IsAngled && IsQuoted) {
+ IsAngled = false;
+ }
// We might suggest insertion of an existing include in edge cases, e.g.,
// include is present in a PP-disabled region, or spelling of the header
// turns out to be the same as one of the unresolved includes in the
// main file.
std::optional<tooling::Replacement> Replacement = HeaderIncludes.insert(
- HeaderRef.trim("\"<>"), Angled, tooling::IncludeDirective::Include);
+ HeaderRef.trim("\"<>"), IsAngled, tooling::IncludeDirective::Include);
if (!Replacement.has_value())
continue;
- if (Angled && Spelling.front() == '\"') {
+ if (IsAngled && Spelling.front() == '\"') {
Spelling.front() = '<';
Spelling.back() = '>';
}
@@ -497,14 +511,16 @@ bool isPreferredProvider(const Inclusion &Inc,
std::vector<Diag> issueIncludeCleanerDiagnostics(
ParsedAST &AST, llvm::StringRef Code,
const IncludeCleanerFindings &Findings, const ThreadsafeFS &TFS,
- HeaderFilter IgnoreHeaders, HeaderFilter AngledHeaders) {
+ HeaderFilter IgnoreHeaders, HeaderFilter AngledHeaders,
+ HeaderFilter QuotedHeaders) {
trace::Span Tracer("IncludeCleaner::issueIncludeCleanerDiagnostics");
std::vector<Diag> UnusedIncludes = generateUnusedIncludeDiagnostics(
AST.tuPath(), Findings.UnusedIncludes, Code, IgnoreHeaders);
std::optional<Fix> RemoveAllUnused = removeAllUnusedIncludes(UnusedIncludes);
std::vector<Diag> MissingIncludeDiags = generateMissingIncludeDiagnostics(
- AST, Findings.MissingIncludes, Code, IgnoreHeaders, AngledHeaders, TFS);
+ AST, Findings.MissingIncludes, Code, IgnoreHeaders, AngledHeaders,
+ QuotedHeaders, TFS);
std::optional<Fix> AddAllMissing = addAllMissingIncludes(MissingIncludeDiags);
std::optional<Fix> FixAll;
diff --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h
index 884feb69488d5..9439eeed6a3e4 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.h
+++ b/clang-tools-extra/clangd/IncludeCleaner.h
@@ -60,7 +60,8 @@ computeIncludeCleanerFindings(ParsedAST &AST,
std::vector<Diag> issueIncludeCleanerDiagnostics(
ParsedAST &AST, llvm::StringRef Code,
const IncludeCleanerFindings &Findings, const ThreadsafeFS &TFS,
- HeaderFilter IgnoreHeader = {}, HeaderFilter AngledHeaders = {});
+ HeaderFilter IgnoreHeader = {}, HeaderFilter AngledHeaders = {},
+ HeaderFilter QuotedHeaders = {});
/// Converts the clangd include representation to include-cleaner
/// include representation.
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index 4fedea35334f6..48896e5f4ff87 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -381,9 +381,9 @@ std::vector<Diag> getIncludeCleanerDiags(ParsedAST &AST, llvm::StringRef Code,
Findings.MissingIncludes.clear();
if (SuppressUnused)
Findings.UnusedIncludes.clear();
- return issueIncludeCleanerDiagnostics(AST, Code, Findings, TFS,
- Cfg.Diagnostics.Includes.IgnoreHeader,
- Cfg.Style.AngledHeaders);
+ return issueIncludeCleanerDiagnostics(
+ AST, Code, Findings, TFS, Cfg.Diagnostics.Includes.IgnoreHeader,
+ Cfg.Style.AngledHeaders, Cfg.Style.QuotedHeaders);
}
tidy::ClangTidyCheckFactories
diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index f51e6e7896f39..9a37247c8799a 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -226,7 +226,8 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
#include "dir/c.h"
$insert_d[[]]$insert_foo[[]]#include "fuzz.h"
#include "header.h"
-$insert_foobar[[]]$insert_angled[[]]#include <e.h>
+$insert_foobar[[]]$insert_quoted[[]]#include "quoted_wrapper.h"
+$insert_angled[[]]#include <e.h>
$insert_f[[]]$insert_vector[[]]
#define DEF(X) const Foo *X;
@@ -239,6 +240,7 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
void foo() {
$b[[b]]();
$angled[[angled]]();
+ $quoted[[quoted]]();
ns::$bar[[Bar]] bar;
bar.d();
@@ -269,6 +271,9 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
TU.AdditionalFiles["angled.h"] = guard("void angled();");
TU.ExtraArgs.push_back("-I" + testPath("."));
+ TU.AdditionalFiles["quoted_wrapper.h"] = guard("#include \"quoted.h\"");
+ TU.AdditionalFiles["quoted.h"] = guard("void quoted();");
+
TU.AdditionalFiles["dir/c.h"] = guard("#include \"d.h\"");
TU.AdditionalFiles["dir/d.h"] =
guard("namespace ns { struct Bar { void d(); }; }");
@@ -308,6 +313,9 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
}},
/*AngledHeaders=*/{[](llvm::StringRef Header) {
return Header.contains("angled.h");
+ }},
+ /*QuotedHeaders=*/{[](llvm::StringRef Header) {
+ return Header.contains("quoted.h");
}});
EXPECT_THAT(
Diags,
@@ -322,6 +330,12 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
withFix({Fix(MainFile.range("insert_angled"),
"#include <angled.h>\n", "#include <angled.h>"),
FixMessage("add all missing includes")})),
+ AllOf(
+ Diag(MainFile.range("quoted"),
+ "No header providing \"quoted\" is directly included"),
+ withFix({Fix(MainFile.range("insert_quoted"),
+ "#include \"quoted.h\"\n", "#include \"quoted.h\""),
+ FixMessage("add all missing includes")})),
AllOf(Diag(MainFile.range("bar"),
"No header providing \"ns::Bar\" is directly included"),
withFix({Fix(MainFile.range("insert_d"),
>From 4043fe9c5610510280ff3c5079eac8d96b6c465f Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Tue, 27 May 2025 21:30:47 +0300
Subject: [PATCH 7/8] Use resolved path for filter
---
clang-tools-extra/clangd/IncludeCleaner.cpp | 29 +++++++--------------
1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index d62003a6a19ee..0195d0ce9b709 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -144,36 +144,27 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
llvm::StringRef HeaderRef{Spelling};
- bool IsAngled = false;
- for (auto &Filter : AngledHeaders) {
- if (Filter(HeaderRef)) {
- IsAngled = true;
- break;
- }
- }
- bool IsQuoted = false;
- for (auto &Filter : QuotedHeaders) {
- if (Filter(HeaderRef)) {
- IsQuoted = true;
- break;
+ bool Angled = HeaderRef.starts_with("<");
+ if (SymbolWithMissingInclude.Providers.front().kind() ==
+ include_cleaner::Header::Kind::Physical) {
+ for (auto &Filter : Angled ? QuotedHeaders : AngledHeaders) {
+ if (Filter(ResolvedPath)) {
+ Angled = !Angled;
+ break;
+ }
}
}
- if (IsAngled == IsQuoted) {
- IsAngled = HeaderRef.starts_with("<");
- } else if (!IsAngled && IsQuoted) {
- IsAngled = false;
- }
// We might suggest insertion of an existing include in edge cases, e.g.,
// include is present in a PP-disabled region, or spelling of the header
// turns out to be the same as one of the unresolved includes in the
// main file.
std::optional<tooling::Replacement> Replacement = HeaderIncludes.insert(
- HeaderRef.trim("\"<>"), IsAngled, tooling::IncludeDirective::Include);
+ HeaderRef.trim("\"<>"), Angled, tooling::IncludeDirective::Include);
if (!Replacement.has_value())
continue;
- if (IsAngled && Spelling.front() == '\"') {
+ if (Angled && Spelling.front() == '\"') {
Spelling.front() = '<';
Spelling.back() = '>';
}
>From eaedbef84b3f932f92d8d0e6192516daf683747a Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Thu, 29 May 2025 20:59:35 +0300
Subject: [PATCH 8/8] Fix message when angled becomes quoted
---
clang-tools-extra/clangd/IncludeCleaner.cpp | 6 +++---
.../clangd/unittests/IncludeCleanerTests.cpp | 16 +++++++++++++---
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index 0195d0ce9b709..382ea3ffe342b 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -164,9 +164,9 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
if (!Replacement.has_value())
continue;
- if (Angled && Spelling.front() == '\"') {
- Spelling.front() = '<';
- Spelling.back() = '>';
+ if (Angled != (Spelling.front() == '<')) {
+ Spelling.front() = Angled ? '<' : '"';
+ Spelling.back() = Angled ? '>' : '"';
}
Diag &D = Result.emplace_back();
diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index 9a37247c8799a..7034685e60d4b 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -226,9 +226,10 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
#include "dir/c.h"
$insert_d[[]]$insert_foo[[]]#include "fuzz.h"
#include "header.h"
-$insert_foobar[[]]$insert_quoted[[]]#include "quoted_wrapper.h"
+$insert_foobar[[]]$insert_quoted[[]]$insert_quoted2[[]]#include "quoted_wrapper.h"
$insert_angled[[]]#include <e.h>
-$insert_f[[]]$insert_vector[[]]
+$insert_f[[]]#include <quoted2_wrapper.h>
+$insert_vector[[]]
#define DEF(X) const Foo *X;
#define BAZ(X) const X x
@@ -241,6 +242,7 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
$b[[b]]();
$angled[[angled]]();
$quoted[[quoted]]();
+ $quoted2[[quoted2]]();
ns::$bar[[Bar]] bar;
bar.d();
@@ -280,6 +282,8 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
TU.AdditionalFiles["system/e.h"] = guard("#include <f.h>");
TU.AdditionalFiles["system/f.h"] = guard("void f();");
+ TU.AdditionalFiles["system/quoted2_wrapper.h"] = guard("#include <system/quoted2.h>");
+ TU.AdditionalFiles["system/quoted2.h"] = guard("void quoted2();");
TU.ExtraArgs.push_back("-isystem" + testPath("system"));
TU.AdditionalFiles["fuzz.h"] = guard("#include \"buzz.h\"");
@@ -315,7 +319,7 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
return Header.contains("angled.h");
}},
/*QuotedHeaders=*/{[](llvm::StringRef Header) {
- return Header.contains("quoted.h");
+ return Header.contains("quoted.h") || Header.contains("quoted2.h");
}});
EXPECT_THAT(
Diags,
@@ -336,6 +340,12 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
withFix({Fix(MainFile.range("insert_quoted"),
"#include \"quoted.h\"\n", "#include \"quoted.h\""),
FixMessage("add all missing includes")})),
+ AllOf(
+ Diag(MainFile.range("quoted2"),
+ "No header providing \"quoted2\" is directly included"),
+ withFix({Fix(MainFile.range("insert_quoted2"),
+ "#include \"quoted2.h\"\n", "#include \"quoted2.h\""),
+ FixMessage("add all missing includes")})),
AllOf(Diag(MainFile.range("bar"),
"No header providing \"ns::Bar\" is directly included"),
withFix({Fix(MainFile.range("insert_d"),
More information about the cfe-commits
mailing list