[clang] [clang][ssaf] Add cpp-bounded-buffers source transformation (PR #210457)

Balázs Benics via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 12 06:46:36 PDT 2026


================
@@ -0,0 +1,482 @@
+//===- CppBoundedBuffers.cpp ----------------------------------------------===//
+//
+// 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 "clang/ScalableStaticAnalysis/SourceTransformation/Transformations/CppBoundedBuffers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h"
+#include "clang/ScalableStaticAnalysis/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysis/Core/Model/EntityIdTable.h"
+#include "clang/ScalableStaticAnalysis/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysis/SourceTransformation/TransformationRegistry.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include <cassert>
+#include <map>
+#include <string>
+
+using namespace clang;
+using namespace clang::ssaf;
+
+static constexpr llvm::StringLiteral SkippedRuleId =
+    "cpp-bounded-buffers-skipped";
+
+namespace {
+
+/// A declarator whose type can carry pointer levels.
+bool isCandidateType(QualType T) {
+  QualType U = T.getNonReferenceType();
+  return U->isPointerType() || U->isArrayType();
+}
+
+std::string spell(QualType T, const ASTContext &Ctx) {
+  return T.getAsString(Ctx.getPrintingPolicy());
+}
+
+/// Whether \p T can be re-emitted as written. Anonymous records and lambdas
+/// have no usable spelling.
+bool isReproducible(QualType T) {
+  const auto *RT = T->getAs<RecordType>();
+  if (!RT)
+    return true;
+  const RecordDecl *RD = RT->getDecl();
+  if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+    if (CXXRD->isLambda())
+      return false;
+  return RD->getIdentifier() || RD->getTypedefNameForAnonDecl();
+}
+
+std::string cvPrefix(QualType T) {
+  std::string Prefix;
+  if (T.isLocalConstQualified())
+    Prefix += "const ";
+  if (T.isLocalVolatileQualified())
+    Prefix += "volatile ";
+  return Prefix;
+}
+
+std::string renderNewType(const ClassifyResult &R, QualType T,
+                          const ASTContext &Ctx) {
+  if (*R.NewType == BoundedType::Ptr)
+    return cvPrefix(T) + "bounded_ptr<" + R.InnerSpelling + "> ";
+  const auto *CAT = Ctx.getAsConstantArrayType(T);
+  std::string N = std::to_string(CAT->getSize().getZExtValue());
+  return "bounded_array<" + R.InnerSpelling + ", " + N + "> ";
+}
+
+/// Whether another declarator in \p D's lexical context shares its type
+/// specifier, i.e. \p D is one declarator of a multi-declarator group.
+bool sharesTypeSpecifier(const DeclaratorDecl *D) {
+  const TypeSourceInfo *TSI = D->getTypeSourceInfo();
+  const DeclContext *DC = D->getLexicalDeclContext();
+  if (!TSI || !DC)
+    return false;
+  SourceLocation Begin = TSI->getTypeLoc().getBeginLoc();
+  for (const Decl *Sibling : DC->decls()) {
+    if (Sibling == D)
+      continue;
+    const auto *Other = dyn_cast<DeclaratorDecl>(Sibling);
+    if (Other && Other->getTypeSourceInfo() &&
+        Other->getTypeSourceInfo()->getTypeLoc().getBeginLoc() == Begin)
+      return true;
+  }
+  return false;
+}
+
+bool hasTrailingReturnType(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
+  return FPT && FPT->hasTrailingReturn();
+}
+
+CharSourceRange declTypeRange(const DeclaratorDecl *D) {
+  if (const TypeSourceInfo *TSI = D->getTypeSourceInfo())
+    return CharSourceRange::getTokenRange(TSI->getTypeLoc().getSourceRange());
+  return CharSourceRange::getTokenRange(D->getSourceRange());
+}
+
+/// A leading cv-qualifier keyword (e.g. the `const` in `const char *`) is not
+/// covered by the type-loc's begin location; extend \p TypeBegin left over it.
+SourceLocation extendOverLeadingQualifiers(SourceLocation TypeBegin,
+                                           const ASTContext &Ctx) {
+  const SourceManager &SM = Ctx.getSourceManager();
+  const LangOptions &LangOpts = Ctx.getLangOpts();
+  while (std::optional<Token> Prev = Lexer::findPreviousToken(
+             TypeBegin, SM, LangOpts, /*IncludeComments=*/false)) {
+    // findPreviousToken lexes raw tokens, so keywords arrive as identifiers.
+    if (!Prev->is(tok::raw_identifier))
+      break;
+    StringRef Text = Prev->getRawIdentifier();
+    if (Text != "const" && Text != "volatile")
+      break;
+    TypeBegin = Prev->getLocation();
+  }
+  return TypeBegin;
+}
----------------
steakhal wrote:

I find this difficult to believe.
Anyway, re-lexing is really dangerous in presence of macros because those macros won't be substituted during lexing.

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


More information about the cfe-commits mailing list