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