Follow style configuration in clangd when inserting missing includes (#140594)
The missing include diagnostic has the capability to introduce the necessary headers into the source file. However, it does not currently follow the inclusion style found in the `.clangd` file. For example, if the file explicitly mentions that headers should be include with angled brackets, they could be included with quotes instead. More details in https://github.com/llvm/llvm-project/issues/138740. This PR fixes this gap, so that the style configuration is followed.
This commit is contained in:
parent
3737e7e273
commit
7df458b473
@ -117,7 +117,9 @@ 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, HeaderFilter QuotedHeaders,
|
||||
const ThreadsafeFS &TFS) {
|
||||
std::vector<Diag> Result;
|
||||
const SourceManager &SM = AST.getSourceManager();
|
||||
const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
|
||||
@ -141,7 +143,18 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
|
||||
AST.getPreprocessor().getHeaderSearchInfo(), MainFile});
|
||||
|
||||
llvm::StringRef HeaderRef{Spelling};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -151,6 +164,11 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
|
||||
if (!Replacement.has_value())
|
||||
continue;
|
||||
|
||||
if (Angled != (Spelling.front() == '<')) {
|
||||
Spelling.front() = Angled ? '<' : '"';
|
||||
Spelling.back() = Angled ? '>' : '"';
|
||||
}
|
||||
|
||||
Diag &D = Result.emplace_back();
|
||||
D.Message =
|
||||
llvm::formatv("No header providing \"{0}\" is directly included",
|
||||
@ -481,18 +499,19 @@ 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,
|
||||
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, TFS);
|
||||
AST, Findings.MissingIncludes, Code, IgnoreHeaders, AngledHeaders,
|
||||
QuotedHeaders, TFS);
|
||||
std::optional<Fix> AddAllMissing = addAllMissingIncludes(MissingIncludeDiags);
|
||||
|
||||
std::optional<Fix> FixAll;
|
||||
|
@ -57,11 +57,11 @@ 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 = {},
|
||||
HeaderFilter QuotedHeaders = {});
|
||||
|
||||
/// Converts the clangd include representation to include-cleaner
|
||||
/// include representation.
|
||||
|
@ -381,8 +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);
|
||||
return issueIncludeCleanerDiagnostics(
|
||||
AST, Code, Findings, TFS, Cfg.Diagnostics.Includes.IgnoreHeader,
|
||||
Cfg.Style.AngledHeaders, Cfg.Style.QuotedHeaders);
|
||||
}
|
||||
|
||||
tidy::ClangTidyCheckFactories
|
||||
|
@ -220,13 +220,16 @@ TEST(IncludeCleaner, ComputeMissingHeaders) {
|
||||
TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
|
||||
Annotations MainFile(R"cpp(
|
||||
#include "a.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[[]]#include <e.h>
|
||||
$insert_f[[]]$insert_vector[[]]
|
||||
$insert_foobar[[]]$insert_quoted[[]]$insert_quoted2[[]]#include "quoted_wrapper.h"
|
||||
$insert_angled[[]]#include <e.h>
|
||||
$insert_f[[]]#include <quoted2_wrapper.h>
|
||||
$insert_vector[[]]
|
||||
|
||||
#define DEF(X) const Foo *X;
|
||||
#define BAZ(X) const X x
|
||||
@ -237,6 +240,9 @@ $insert_f[[]]$insert_vector[[]]
|
||||
|
||||
void foo() {
|
||||
$b[[b]]();
|
||||
$angled[[angled]]();
|
||||
$quoted[[quoted]]();
|
||||
$quoted2[[quoted2]]();
|
||||
|
||||
ns::$bar[[Bar]] bar;
|
||||
bar.d();
|
||||
@ -263,12 +269,22 @@ $insert_f[[]]$insert_vector[[]]
|
||||
TU.AdditionalFiles["a.h"] = guard("#include \"b.h\"");
|
||||
TU.AdditionalFiles["b.h"] = guard("void b();");
|
||||
|
||||
TU.AdditionalFiles["angled_wrapper.h"] = guard("#include <angled.h>");
|
||||
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(); }; }");
|
||||
|
||||
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\"");
|
||||
@ -297,7 +313,15 @@ $insert_f[[]]$insert_vector[[]]
|
||||
Findings.UnusedIncludes.clear();
|
||||
std::vector<clangd::Diag> Diags = issueIncludeCleanerDiagnostics(
|
||||
AST, TU.Code, Findings, MockFS(),
|
||||
{[](llvm::StringRef Header) { return Header.ends_with("buzz.h"); }});
|
||||
/*IgnoreHeaders=*/{[](llvm::StringRef Header) {
|
||||
return Header.ends_with("buzz.h");
|
||||
}},
|
||||
/*AngledHeaders=*/{[](llvm::StringRef Header) {
|
||||
return Header.contains("angled.h");
|
||||
}},
|
||||
/*QuotedHeaders=*/{[](llvm::StringRef Header) {
|
||||
return Header.contains("quoted.h") || Header.contains("quoted2.h");
|
||||
}});
|
||||
EXPECT_THAT(
|
||||
Diags,
|
||||
UnorderedElementsAre(
|
||||
@ -306,6 +330,23 @@ $insert_f[[]]$insert_vector[[]]
|
||||
withFix({Fix(MainFile.range("insert_b"), "#include \"b.h\"\n",
|
||||
"#include \"b.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("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("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"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user