@@ -339,6 +339,72 @@ std::string noteMessage(const Diag &Main, const DiagBase &Note,
339339 return capitalize (std::move (Result));
340340}
341341
342+ std::optional<Fix>
343+ generateApplyAllFromOption (const llvm::StringRef Name,
344+ llvm::ArrayRef<Diag *> AllDiagnostics) {
345+ Fix ApplyAll;
346+ for (auto *const Diag : AllDiagnostics) {
347+ for (const auto &Fix : Diag->Fixes )
348+ ApplyAll.Edits .insert (ApplyAll.Edits .end (), Fix.Edits .begin (),
349+ Fix.Edits .end ());
350+ }
351+ llvm::sort (ApplyAll.Edits );
352+ ApplyAll.Edits .erase (
353+ std::unique (ApplyAll.Edits .begin (), ApplyAll.Edits .end ()),
354+ ApplyAll.Edits .end ());
355+ // Skip diagnostic categories that don't have multiple fixes to apply
356+ if (ApplyAll.Edits .size () < 2U ) {
357+ return std::nullopt ;
358+ }
359+ ApplyAll.Message = llvm::formatv (" apply all '{0}' fixes" , Name);
360+ return ApplyAll;
361+ }
362+
363+ std::optional<Fix>
364+ generateApplyAllFixesOption (llvm::ArrayRef<Diag> AllDiagnostics) {
365+ Fix ApplyAll;
366+ for (auto const &Diag : AllDiagnostics) {
367+ for (const auto &Fix : Diag.Fixes )
368+ ApplyAll.Edits .insert (ApplyAll.Edits .end (), Fix.Edits .begin (),
369+ Fix.Edits .end ());
370+ }
371+ llvm::sort (ApplyAll.Edits );
372+ ApplyAll.Edits .erase (
373+ std::unique (ApplyAll.Edits .begin (), ApplyAll.Edits .end ()),
374+ ApplyAll.Edits .end ());
375+ if (ApplyAll.Edits .size () < 2U ) {
376+ return std::nullopt ;
377+ }
378+ ApplyAll.Message = " apply all clangd fixes" ;
379+ return ApplyAll;
380+ }
381+
382+ void appendApplyAlls (std::vector<Diag> &AllDiagnostics) {
383+ llvm::DenseMap<llvm::StringRef, std::vector<Diag *>> CategorizedFixes;
384+
385+ for (auto &Diag : AllDiagnostics) {
386+ // Keep track of fixable diagnostics for generating "apply all fixes"
387+ if (!Diag.Fixes .empty ()) {
388+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace (
389+ Diag.Name , std::vector<struct Diag *>{&Diag});
390+ !DidEmplace)
391+ It->second .emplace_back (&Diag);
392+ }
393+ }
394+
395+ auto FixAllClangd = generateApplyAllFixesOption (AllDiagnostics);
396+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
397+ auto FixAllForCategory =
398+ generateApplyAllFromOption (Name, DiagsForThisCategory);
399+ for (auto *Diag : DiagsForThisCategory) {
400+ if (DiagsForThisCategory.size () >= 2U && FixAllForCategory.has_value ())
401+ Diag->Fixes .emplace_back (*FixAllForCategory);
402+ if (CategorizedFixes.size () >= 2U && FixAllClangd.has_value ())
403+ Diag->Fixes .emplace_back (*FixAllClangd);
404+ }
405+ }
406+ }
407+
342408void setTags (clangd::Diag &D) {
343409 static const auto *DeprecatedDiags = new llvm::DenseSet<unsigned >{
344410 diag::warn_access_decl_deprecated,
@@ -575,7 +641,8 @@ std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
575641 // Do not forget to emit a pending diagnostic if there is one.
576642 flushLastDiag ();
577643
578- // Fill in name/source now that we have all the context needed to map them.
644+ // Fill in name/source now that we have all the context needed to map
645+ // them.
579646 for (auto &Diag : Output) {
580647 if (const char *ClangDiag = getDiagnosticCode (Diag.ID )) {
581648 // Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +686,9 @@ std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
619686 llvm::erase_if (Output, [&](const Diag &D) {
620687 return !SeenDiags.emplace (D.Range , D.Message ).second ;
621688 });
689+
690+ appendApplyAlls (Output);
691+
622692 return std::move (Output);
623693}
624694
0 commit comments