[clang-tools-extra] [clang-tidy] Add `llvm-formatv-string` (PR #195974)
Victor Chernyakin via cfe-commits
cfe-commits at lists.llvm.org
Fri May 8 18:31:28 PDT 2026
================
@@ -0,0 +1,198 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "FormatvStringCheck.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::llvm_check {
+
+namespace {
+
+struct ParseResult {
+ llvm::SmallVector<unsigned, 4> Indices;
+ unsigned MaxIndex = 0;
+};
+
+} // namespace
+
+static llvm::Expected<ParseResult> parseFormatvString(llvm::StringRef Fmt) {
+ ParseResult Result;
+ unsigned NextAutoIndex = 0;
+ bool HasAutomatic = false;
+ bool HasExplicit = false;
+
+ while (!Fmt.empty()) {
+ const size_t OpenBrace = Fmt.find('{');
+ if (OpenBrace == llvm::StringRef::npos)
+ break;
+
+ Fmt = Fmt.drop_front(OpenBrace);
+
+ // Handle escaped braces '{{'.
+ if (Fmt.size() > 1 && Fmt[1] == '{') {
+ Fmt = Fmt.drop_front(2);
+ continue;
+ }
+
+ // Find the closing '}'.
+ const size_t CloseBrace = Fmt.find('}');
+ if (CloseBrace == llvm::StringRef::npos)
+ return llvm::createStringError("unterminated brace in format string");
+
+ // Extract the content between braces.
+ const llvm::StringRef Content = Fmt.substr(1, CloseBrace - 1);
+ Fmt = Fmt.drop_front(CloseBrace + 1);
+
+ // Parse the replacement field: [index] ["," layout] [":" format]
+ llvm::StringRef IndexStr = Content;
+
+ // Strip layout and format parts for index parsing.
+ const size_t CommaPos = Content.find(',');
+ const size_t ColonPos = Content.find(':');
+ if (CommaPos != llvm::StringRef::npos)
+ IndexStr = Content.substr(0, CommaPos);
+ else if (ColonPos != llvm::StringRef::npos)
+ IndexStr = Content.substr(0, ColonPos);
+
+ IndexStr = IndexStr.trim();
+
+ unsigned Index = 0;
+ if (IndexStr.empty()) {
+ Index = NextAutoIndex++;
+ HasAutomatic = true;
+ } else {
+ if (IndexStr.getAsInteger(10, Index))
+ return llvm::createStringError(
+ "invalid replacement index in format string");
+ HasExplicit = true;
+ }
+
+ Result.Indices.push_back(Index);
+ Result.MaxIndex = std::max(Result.MaxIndex, Index);
+ }
+
+ if (HasAutomatic && HasExplicit)
+ return llvm::createStringError(
+ "format string mixes automatic and explicit indices");
+
+ return Result;
+}
+
+FormatvStringCheck::FormatvStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AdditionalFunctions(Options.get("AdditionalFunctions", "")) {
+ Functions.insert("llvm::formatv");
+ Functions.insert("llvm::createStringErrorV");
+
+ // Parse semicolon-separated function names from AdditionalFunctions.
+ const llvm::StringRef Input(AdditionalFunctions);
+ llvm::SmallVector<llvm::StringRef, 8> Entries;
+ Input.split(Entries, ';', -1, false);
+ for (llvm::StringRef Entry : Entries) {
+ Entry = Entry.trim();
+ if (!Entry.empty())
+ Functions.insert(Entry);
+ }
----------------
localspook wrote:
There's a function for this, `utils::options::parseStringList` (it's in `"../utils/OptionsUtils.h"`).
https://github.com/llvm/llvm-project/pull/195974
More information about the cfe-commits
mailing list