<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/76270>76270</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [mlir][docs] Traits should be in code font to escape HTML-style template names
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            mlir
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          scottamain
      </td>
    </tr>
</table>

<pre>
    When generating docs for a dialect, some traits have template arguments that might be interpreted as HTML tags in a Markdown processor, which creates problems. For example, `OpTrait::HasParent<typename ParentOpType>`.

Rather than HTML-escaping these characters, it's easier and more appropriate to just make the names code font, which makes the HTML-looking names harmless. In my case, I only need this for trait names, but the interface names and effect names should also get code font in here:
https://github.com/llvm/llvm-project/blob/88d319a/mlir/tools/mlir-tblgen/OpDocGen.cpp#L126-L179

The following new `addCodeFont()` function works for me, but I'm new here; I'm not sure this is the best solution, follows best practices, or if it has unseen consequences. So, I'd be thrilled if somebody else experienced with this code would take this to the finish line.

```cpp
// This is called for trait and interface names, whereas effects are codified inline
void addCodeFont(std::string &text) {
    text = '`' + text + '`';
}

static void emitOpTraitsDoc(const Operator &op, raw_ostream &os) {
  // TODO: We should link to the trait/documentation of it. That also means we
  // should add descriptions to traits that can be queried.
  // Collect using set to sort effects, interfaces & traits.
 std::set<std::string> effects, interfaces, traits;
  for (auto &trait : op.getTraits()) {
    if (isa<PredTrait>(&trait))
 continue;

    std::string name = trait.getDef().getName().str();
 StringRef ref = name;
    StringRef traitName = trait.getDef().getValueAsString("trait");
 traitName.consume_back("::Trait");
 traitName.consume_back("::Impl");
    if (ref.starts_with("anonymous_"))
      name = traitName.str();
    if (isa<InterfaceTrait>(&trait)) {
      if (trait.getDef().isSubClassOf("SideEffectsTraitBase")) {
        auto effectName = trait.getDef().getValueAsString("baseEffectName");
 effectName.consume_front("::");
 effectName.consume_front("mlir::");
        std::string effectStr;
 llvm::raw_string_ostream os(effectStr);
        os << "`" << effectName << "{";
        auto list = trait.getDef().getValueAsListOfDefs("effects");
 llvm::interleaveComma(list, os, [&](Record *rec) {
          StringRef effect = rec->getValueAsString("effect");
 effect.consume_front("::");
 effect.consume_front("mlir::");
          os << effect << " on " << rec->getValueAsString("resource");
        });
        os << "}`";
        effects.insert(os.str());
 name.append(llvm::formatv(" ({0})", traitName).str());
      }
 addCodeFont(name);
      interfaces.insert(name);
      continue;
 }

    addCodeFont(name);
    traits.insert(name);
  }
  if (!traits.empty()) {
    llvm::interleaveComma(traits, os << "\nTraits: ");
    os << "\n";
  }
  if (!interfaces.empty()) {
 llvm::interleaveComma(interfaces, os << "\nInterfaces: ");
    os << "\n";
  }
  if (!effects.empty()) {
 llvm::interleaveComma(effects, os << "\nEffects: ");
    os << "\n";
  }
}
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysWF1v47gO_TXKCzGBIzcffchDmzR3C8xuF9Pi7uNAkelYW1vyinI7_fcXlOx8NdPBzN2gaGuFpA55jijJisjsLOJSTG_FdD1SXaicX5J2IahGGTvauuJt-VeFFnZo0atg7A4KpwlK50FBYVSNOgi5AnINQvDKBIJKvSAEbNpaBQTld12DNhCESgVozK4KsEUwNqBvPQYsQBH89vT7ZwhqR2AsKPhd-efCvVpovdNI5DzP8loZXYH2qAISf7WtsaExbJwH_KaatkY2E7PsoX1iMCK_EfnNb4r-VB5tEPkqvLVoVYOQRh7ap7cWRX4nZtlYZGuR3aTfX1So0DNmG7F9QtKq5QKECglBV8orHdATz2iCkHMCVGTQg7IFNM4jqLb1rvWG6xAc_N1RgEY9I8cARkGgXYFQOhsO-bEFRZM4ce3cM8-b7CvlmxqJxnBvoXkDrSjmfA_O1m9gEQsIlUkURUKSI9tsuxDDxtKXSg8YGC-WJereFqhyXV2AqsnBDsMBJJNToUeuayxTFUJL_CQ3Qm52JlTddqxdI-Smrl-GP59a7_6OStlsa7cVcrNYFPnkWgm5aWrjhdwE52rqHz-Fbb1DK-TmoV07_R-0Y922QuafJ3L26fNkfn3M1FPF2OravcYq4Svzr4pi5QrcxMIuhLwWswzKzupgnIVX559ThRocCnMv5LyJ7inB22HEBaDOY6qqScRskQKQqzsOxxESAEpftKwMo1PRnQdTgglQKYLOEqIF7SzhPx1ajTSGRxcJFHJe8MIIlTd1jQW78bLiVQhYEwJ-a9Eb9irg1YQqQYrkvEbCQtIWg3QRZ2msoQpqY_FE3WKWpR-uaxqJBMJTn6RWEcJBRKyRM90kxaJHRb18CJRHBmRKwwlYnjjFf3GmgFNWKBRpfVLwTJ2Qs4DfgpDXIOa3yQ0AgAdB5GsQcs6o5RyEvO2H5e1hWOS9l5ivj7OloILRECFgY0LfHGjttJALJiPAQ8sNznlG4VpOzavXr46CR9XEQToDNlTsYf0g8hv4C4dlUxv7PBAQiyfkpnA6dkEV9edYEGN44oYYF1mDyhK84lnsYR0WBRRI2puW3RO7qdfGnqqVZeH807E6ivFZkJWruUtDR1xkwsDu5HwYSIsNbKCWONc--hDpQBRyCz3jTeR3lyPxUwq0JwaiooRcqC64SHgUF9fPteMdhkRMv2TPhWBKdjWkRL7602PRt_i7aD7rS81uvYt2Nhjb4UEX-1Dn2otbAmssRmEkaywTDH74Q3GjiE8UfA9vn9RjjPEFS_BYxigc7ihpODKJE_zx8XT_VXWHN5Sc4rDss5MnE-9jjVnEXYNft0o_J4eU3tOvud03bX3utSfAYzmmoHygr9yFkp-yzr41rqOvvd-ehfg5rW-c-0Ihzzi-H6T0XaJP9TF4Xyiqocduu6oV0UMako-mwLsk2xj-Nm6k8nJcgCjYJPOfJ2-rqJ8rCem0sIeoez5KP2xcPSE_5xP31It-_edc_incY_AHy7h9RxtuhMlu3w-5GS4OThdmcAQiX4l8BQyB27McBk6quDeZ38ZkL5W9NhR-XPDPhsJDucYytQ-570mnFTjkFTtVjeoFV65plJALniju2bF38blYzsR0LeTiC2rnCxDyxqO-KJDjNd4fphiyR_1J5HeXdZHsLnP7s1r4JR0c87QHPVACzsIRbR9m4pFc5_U7bfcf3pB_KJL5OunknVnP5NhYQs-pOTrqHsdxuc2MVduiLZjOPdWl840KLwkrtwgxv816UIx4dehLpz3-Hej90eLsMGOT65n1YTM8gL9s-W6vgrNDTFwNP5yy37g_mOyQQN8thZz0Xti04e07e-9Hy6bf5OPCOSZ0urL9fp7fwAVlvLM-Jf8S0qOCfhftR1BPTyfvANwfvv7XIA_q_SW8Rwerd1P3u9f_ifTwz3AnGBXLvLjOr9UIl5N5lsvFJJNXo2q5mCxQXcltUSzyPJ_leipn8vpqorPtNMvy-cgsZSbziZRSTvJ5djWezGb6OisXcirz2Xx7Ja4ybJSpx5zz2PndyBB1uJzP5Dwb1WqLNcX3EX3vYuTrkV_GS-S225G4yrhJ0yFAMKGO7zCiw3QtpreF0ySma0jiG07R8Z3D0VWW93O-0_fXbApv9dFri3jDGXW-Xv70FTemxHfZmNX_AgAA___XpDXS">