[clang] [libformat] Implement `TemplateTypeParameterKeyword` fixer (PR #192223)

Björn Schäpers via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 12:55:19 PDT 2026


================
@@ -0,0 +1,180 @@
+//===--- TemplateTypeParameterKeywordFixer.cpp ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements TemplateTypeParameterKeywordFixer, a TokenAnalyzer
+/// that rewrites \c typename and \c class when they introduce type template
+/// parameters or template template parameters, according to
+/// \c FormatStyle::TemplateTypeParameterKeyword.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TemplateTypeParameterKeywordFixer.h"
+#include "FormatToken.h"
+#include "clang/Format/Format.h"
+#include "llvm/Support/Error.h"
+
+using llvm::cantFail;
+
+namespace clang {
+namespace format {
+namespace {
+
+bool angleIntroducesTemplateParameterList(const FormatToken *LT) {
+  const FormatToken *Prev = LT->getPreviousNonComment();
+  if (!Prev)
+    return false;
+  if (Prev->is(tok::kw_template))
+    return true;
+  // Generic lambda
+  return Prev->is(tok::r_square);
+}
+
+bool isStrictlyBetween(const SourceManager &SM, SourceLocation X,
+                       SourceLocation Low, SourceLocation High) {
+  return SM.isBeforeInTranslationUnit(Low, X) &&
+         SM.isBeforeInTranslationUnit(X, High);
+}
+
+bool isInsideMatchingAngleRange(const FormatToken *Kw,
+                                const SourceManager &SM) {
+  SourceLocation KwLoc = Kw->getStartOfNonWhitespace();
+  for (const FormatToken *T = Kw->getPreviousNonComment(); T;
+       T = T->getPreviousNonComment()) {
+    if (!T->is(tok::less))
+      continue;
+    if (!angleIntroducesTemplateParameterList(T))
+      continue;
+    if (!T->MatchingParen)
+      continue;
+    SourceLocation OpenLoc = T->getStartOfNonWhitespace();
+    SourceLocation CloseLoc = T->MatchingParen->getStartOfNonWhitespace();
+    if (isStrictlyBetween(SM, KwLoc, OpenLoc, CloseLoc))
+      return true;
+  }
+  return false;
+}
+
+bool introducesTypeOrTemplateTemplateParameterName(const FormatToken *Kw) {
+  const FormatToken *N = Kw->getNextNonComment();
+  if (!N)
+    return false;
+  if (N->is(tok::ellipsis))
+    N = N->getNextNonComment();
+  if (!N)
+    return false;
+  if (N->isOneOf(tok::comma, tok::greater, tok::equal, tok::colon,
+                  tok::kw_requires))
+    return true;
+  if (!N->Tok.getIdentifierInfo())
+    return false;
+  const FormatToken *AfterName = N->getNextNonComment();
+  return !AfterName || !AfterName->is(tok::coloncolon);
+}
+
+bool prevIsTemplateParameterDelimiter(const FormatToken *Prev) {
+  return Prev && Prev->isOneOf(tok::less, tok::comma, tok::greater);
+}
+
+/// \c true when \p Prev is the closing ``>`` of a nested ``template <...>``
+/// prefix, so \p Kw introduces a template template parameter name (C++17
+/// allows ``typename`` there; before C++17 only ``class`` is permitted).
+bool isTemplateTemplateParameterIntroducer(const FormatToken *Prev) {
+  return Prev && Prev->is(tok::greater);
+}
+
+bool allowsTypenameTemplateTemplateIntroducer(const FormatStyle &Style) {
+  switch (Style.Standard) {
+  case FormatStyle::LS_Auto:
+  case FormatStyle::LS_Cpp11:
+  case FormatStyle::LS_Cpp14:
+    return false;
+  default:
+  return true;
+  }
+}
+
+llvm::StringRef replacementKeyword(FormatStyle::TemplateTypeParameterKeywordOption O) {
+  switch (O) {
+  case FormatStyle::TTPS_UseTypename:
+    return "typename";
+  case FormatStyle::TTPS_UseClass:
+    return "class";
+  case FormatStyle::TTPS_Leave:
+    break;
+  }
+  return {};
+}
+
+void processLine(AnnotatedLine *Line, const SourceManager &SM,
+                 AffectedRangeManager &AffectedRangeMgr,
+                 const FormatStyle &Style,
+                 FormatStyle::TemplateTypeParameterKeywordOption Opt,
+                 tooling::Replacements *Fixes) {
+  if (!Line->Affected || Line->InPPDirective || Line->InMacroBody)
+    return;
+
+  for (FormatToken *Tok = Line->First; Tok; Tok = Tok->Next) {
+    if (Tok->Finalized)
+      continue;
+    if (!Tok->isOneOf(tok::kw_typename, tok::kw_class))
+      continue;
+
+    const FormatToken *Prev = Tok->getPreviousNonComment();
+    if (!prevIsTemplateParameterDelimiter(Prev))
+      continue;
+    if (!isInsideMatchingAngleRange(Tok, SM))
+      continue;
+    if (!introducesTypeOrTemplateTemplateParameterName(Tok))
+      continue;
+    if (isTemplateTemplateParameterIntroducer(Prev) && !allowsTypenameTemplateTemplateIntroducer(Style))
+      continue;
+
+    llvm::StringRef NewText = replacementKeyword(Opt);
+    if (NewText.empty() || NewText == Tok->TokenText)
----------------
HazardyKnusperkeks wrote:

Can't be empty, can it?

https://github.com/llvm/llvm-project/pull/192223


More information about the cfe-commits mailing list