[clang-tidy] How to get replacements properly de-duplicated by clang-apply-replacements?

modernize-macro-to-enum creates a bunch of little insertions around a cluster of macros to turn this:

#define FOO1 1
#define FOO2 2
#define FOO3 3

into this

enum {
FOO1 = 1,
FOO2 = 2,
FOO3 = 3
};

The problem is that when the fixes are exported, the insertions of the = are not de-duplicated by clang-apply-replacements. So if clang-tidy is run with header-filter set to apply to application header files, the header files that have fixits issued in the course of analyzing multiple source files end up with invalid syntax llike this:

enum {
FOO1 = = 1,
FOO2 = = 2,
FOO3 = = 3
};
};

I think I remember something about fixits issued by clang-tidy that there is a way to mark them as belonging to a single comprehensive set of changes around a source range, or something like that?

Currently I’m appending all the changes to a single instance of a DiagnosticBuilder (MacroToEnumCheck.cpp:468)

void MacroToEnumCallbacks::fixEnumMacro(const MacroList &MacroList) const {
  SourceLocation Begin =
      MacroList.front().Directive->getMacroInfo()->getDefinitionLoc();
  Begin = SM.translateLineCol(SM.getFileID(Begin),
                              SM.getSpellingLineNumber(Begin), 1);
  DiagnosticBuilder Diagnostic =
      Check->diag(Begin, "replace macro with enum")
      << FixItHint::CreateInsertion(Begin, "enum {\n");

  for (size_t I = 0u; I < MacroList.size(); ++I) {
    const EnumMacro &Macro = MacroList[I];
    SourceLocation DefineEnd =
        Macro.Directive->getMacroInfo()->getDefinitionLoc();
    SourceLocation DefineBegin = SM.translateLineCol(
        SM.getFileID(DefineEnd), SM.getSpellingLineNumber(DefineEnd), 1);
    CharSourceRange DefineRange;
    DefineRange.setBegin(DefineBegin);
    DefineRange.setEnd(DefineEnd);
    Diagnostic << FixItHint::CreateRemoval(DefineRange);

    SourceLocation NameEnd = Lexer::getLocForEndOfToken(
        Macro.Directive->getMacroInfo()->getDefinitionLoc(), 0, SM, LangOpts);
    Diagnostic << FixItHint::CreateInsertion(NameEnd, " =");

    SourceLocation ValueEnd = Lexer::getLocForEndOfToken(
        Macro.Directive->getMacroInfo()->getDefinitionEndLoc(), 0, SM,
        LangOpts);
    if (I < MacroList.size() - 1)
      Diagnostic << FixItHint::CreateInsertion(ValueEnd, ",");
  }

  SourceLocation End = Lexer::getLocForEndOfToken(
      MacroList.back().Directive->getMacroInfo()->getDefinitionEndLoc(), 0, SM,
      LangOpts);
  End = SM.translateLineCol(SM.getFileID(End),
                            SM.getSpellingLineNumber(End) + 1, 1);
  Diagnostic << FixItHint::CreateInsertion(End, "};\n");
}