[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:27 PDT 2026


================
@@ -0,0 +1,397 @@
+//===- CppBoundedBuffersTest.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 "FindDecl.h"
+#include "TestFixture.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/Basic/Sarif.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/Core/WholeProgramAnalysis/WPASuite.h"
+#include "clang/ScalableStaticAnalysis/SourceTransformation/SourceEditEmitter.h"
+#include "clang/ScalableStaticAnalysis/SourceTransformation/TransformationReportEmitter.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/Error.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clang::ssaf;
+
+namespace {
+
+class RecordingEditEmitter : public SourceEditEmitter {
+public:
+  std::vector<tooling::Replacement> Replacements;
+
+  void addReplacement(tooling::Replacement R) override {
+    Replacements.push_back(std::move(R));
+  }
+};
+
+class RecordingReportEmitter : public TransformationReportEmitter {
+public:
+  struct Entry {
+    std::string RuleId;
+    SarifResultLevel Level;
+    std::string Message;
+  };
+  std::vector<Entry> Results;
+
+  void addResult(StringRef RuleId, SarifResultLevel Level, CharSourceRange,
+                 StringRef Message) override {
+    Results.push_back({RuleId.str(), Level, Message.str()});
+  }
+};
+
+std::optional<EntityName> varEntity(StringRef Name, ASTContext &Ctx) {
+  return getEntityName(findDeclByName<VarDecl>(Name, Ctx));
+}
+
+std::optional<EntityName> fieldEntity(StringRef Name, ASTContext &Ctx) {
+  return getEntityName(findDeclByName<FieldDecl>(Name, Ctx));
+}
+
+std::optional<EntityName> paramEntity(StringRef Fn, unsigned Idx,
+                                      ASTContext &Ctx) {
+  const FunctionDecl *FD = findFnByName(Fn, Ctx);
+  return FD ? getEntityName(FD->getParamDecl(Idx)) : std::nullopt;
+}
+
+std::optional<EntityName> returnEntity(StringRef Fn, ASTContext &Ctx) {
+  return getEntityNameForReturn(findFnByName(Fn, Ctx));
+}
+
+struct Captured {
+  std::string Rewritten;
+  std::vector<RecordingReportEmitter::Entry> Reports;
+};
+
+class CppBoundedBuffersTest : public TestFixture {
+protected:
+  using EntityFn = llvm::function_ref<std::optional<EntityName>(ASTContext &)>;
+  using MarkFn = llvm::function_ref<void(
+      ASTContext &, WPASuite &, UnsafeBufferReachableAnalysisResult &)>;
+
+  // Marks the entity \p Name reachable at \p Levels in \p Result.
+  static void markReachable(WPASuite &Suite,
+                            UnsafeBufferReachableAnalysisResult &Result,
+                            std::optional<EntityName> Name,
+                            ArrayRef<unsigned> Levels) {
+    if (!Name || Levels.empty())
+      return;
+    EntityId Id = getIdTable(Suite).getId(*Name);
+    EntityPointerLevelSet Set;
+    for (unsigned Level : Levels)
+      Set.insert(buildEntityPointerLevel(Id, Level));
+    Result.Reachables[Id] = std::move(Set);
+  }
+
+  // Parses \p Code, lets \p Mark populate the reachable result, runs the
+  // transformation, and returns the rewritten source and report entries.
+  Captured runMarked(StringRef Code, MarkFn Mark) {
+    std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCode(Code);
+    ASTContext &Ctx = AST->getASTContext();
+
+    WPASuite Suite = makeWPASuite();
+    auto Result = std::make_unique<UnsafeBufferReachableAnalysisResult>();
+    Mark(Ctx, Suite, *Result);
+    getData(Suite)[UnsafeBufferReachableAnalysisResult::analysisName()] =
+        std::move(Result);
+
+    RecordingEditEmitter Edits;
+    RecordingReportEmitter Report;
+    CppBoundedBuffers(Suite, Edits, Report).HandleTranslationUnit(Ctx);
+
+    tooling::Replacements Replacements;
+    for (const tooling::Replacement &R : Edits.Replacements)
+      cantFail(Replacements.add(R));
+    return {cantFail(tooling::applyAllReplacements(Code, Replacements)),
+            std::move(Report.Results)};
+  }
+
+  Captured run(StringRef Code, EntityFn EntityOf, ArrayRef<unsigned> Levels) {
+    return runMarked(Code, [&](ASTContext &Ctx, WPASuite &Suite,
+                               UnsafeBufferReachableAnalysisResult &Result) {
+      markReachable(Suite, Result, EntityOf(Ctx), Levels);
+    });
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// Rewrites: assert the rewritten source and that nothing is reported.
+//===----------------------------------------------------------------------===//
+
+TEST_F(CppBoundedBuffersTest, PointerLocal) {
----------------
steakhal wrote:

I don't think we had tests covering multiple declarations in a declcontext.
For example: https://godbolt.org/z/MEdPP5Kv3
```c++
extern int *const p, *const q, *volatile *pp; // DeclStmt

struct Tup {
    int *const p, *const q, *volatile *pp; // FieldDecls
};

void test() {
    // More DeclStmt but inside a ForStmt
    for (int *const p = {}, *const q = {}, *volatile *pp = {}; true;) {
        return;
    }
}
```

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


More information about the cfe-commits mailing list