[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