[clang] [clang][ssaf] Introduce entity abstraction for SSAF (PR #169131)
Jan Korous via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 8 15:50:35 PST 2025
https://github.com/jkorous-apple updated https://github.com/llvm/llvm-project/pull/169131
>From 61f84a4c4dfa70a4c4367c07075ac9a392cf70b4 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 21 Nov 2025 15:53:11 -0800
Subject: [PATCH 01/29] [clang][ssaf] Introduce entity abstraction for SSAF
Add core abstractions for identifying program entities across compilation
and link unit boundaries in the Scalable Static Analysis Framework (SSAF).
Introduces three key components:
- BuildNamespace: Represents build artifacts (compilation units, link units)
- EntityName: Globally unique entity identifiers across compilation boundaries
- AST mapping: Functions to map Clang AST declarations to EntityNames
Entity identification uses Unified Symbol Resolution (USR) as the underlying
mechanism, with extensions for sub-entities (parameters, return values) via
suffixes. The abstraction allows whole-program analysis by providing stable
identifiers that persist across separately compiled translation units.
---
.../Analysis/Scalable/ASTEntityMapping.h | 46 +++
.../Analysis/Scalable/Model/BuildNamespace.h | 84 +++++
.../Analysis/Scalable/Model/EntityName.h | 47 +++
clang/lib/Analysis/CMakeLists.txt | 1 +
.../Analysis/Scalable/ASTEntityMapping.cpp | 85 +++++
clang/lib/Analysis/Scalable/CMakeLists.txt | 19 +
.../Scalable/Model/BuildNamespace.cpp | 72 ++++
.../Analysis/Scalable/Model/EntityName.cpp | 44 +++
clang/unittests/Analysis/CMakeLists.txt | 1 +
.../Scalable/ASTEntityMappingTest.cpp | 343 ++++++++++++++++++
.../Analysis/Scalable/BuildNamespaceTest.cpp | 99 +++++
.../Analysis/Scalable/CMakeLists.txt | 18 +
.../Analysis/Scalable/EntityNameTest.cpp | 62 ++++
13 files changed, 921 insertions(+)
create mode 100644 clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
create mode 100644 clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
create mode 100644 clang/include/clang/Analysis/Scalable/Model/EntityName.h
create mode 100644 clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
create mode 100644 clang/lib/Analysis/Scalable/CMakeLists.txt
create mode 100644 clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
create mode 100644 clang/lib/Analysis/Scalable/Model/EntityName.cpp
create mode 100644 clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
create mode 100644 clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
create mode 100644 clang/unittests/Analysis/Scalable/CMakeLists.txt
create mode 100644 clang/unittests/Analysis/Scalable/EntityNameTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
new file mode 100644
index 0000000000000..a137e8b741821
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -0,0 +1,46 @@
+//===- ASTMapping.h - AST to SSAF Entity mapping ----------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
+
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringRef.h"
+#include <optional>
+
+namespace clang {
+namespace ssaf {
+
+/// Maps a declaration to an EntityName.
+///
+/// Supported declaration types for entity mapping:
+/// - Functions and methods
+/// - Global Variables
+/// - Function parameters
+/// - Struct/class/union type definitions
+/// - Struct/class/union fields
+///
+/// Implicit declarations and compiler builtins are not mapped.
+///
+/// \param D The declaration to map. Must not be null.
+///
+/// \return An EntityName if the declaration can be mapped, std::nullopt otherwise.
+std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D);
+
+/// Maps a function return type to an EntityName.
+///
+/// \param FD The function declaration. Must not be null.
+///
+/// \return An EntityName for the function's return type.
+std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD);
+
+} // namespace ssaf
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
new file mode 100644
index 0000000000000..c4bf7146e461f
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -0,0 +1,84 @@
+//===- BuildNamespace.h -----------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
+
+#include "llvm/ADT/StringRef.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace ssaf {
+
+enum class BuildNamespaceKind : unsigned short {
+ CompilationUnit,
+ LinkUnit
+};
+
+std::string toString(BuildNamespaceKind BNK);
+
+std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str);
+
+/// Represents a single step in the build process.
+class BuildNamespace {
+ BuildNamespaceKind Kind;
+ std::string Name;
+public:
+ BuildNamespace(BuildNamespaceKind Kind, llvm::StringRef Name)
+ : Kind(Kind), Name(Name.str()) {}
+
+ static BuildNamespace makeTU(llvm::StringRef CompilationId);
+
+ bool operator==(const BuildNamespace& Other) const;
+ bool operator!=(const BuildNamespace& Other) const;
+ bool operator<(const BuildNamespace& Other) const;
+
+ friend class SerializationFormat;
+};
+
+/// Represents a sequence of steps in the build process.
+class NestedBuildNamespace {
+ friend class SerializationFormat;
+
+ std::vector<BuildNamespace> Namespaces;
+
+public:
+ NestedBuildNamespace() = default;
+
+ explicit NestedBuildNamespace(const std::vector<BuildNamespace>& Namespaces)
+ : Namespaces(Namespaces) {}
+
+ explicit NestedBuildNamespace(const BuildNamespace& N) {
+ Namespaces.push_back(N);
+ }
+
+ static NestedBuildNamespace makeTU(llvm::StringRef CompilationId);
+
+ NestedBuildNamespace makeQualified(NestedBuildNamespace Namespace) {
+ auto Copy = *this;
+ for (const auto& N : Namespace.Namespaces)
+ Copy.Namespaces.push_back(N);
+ return Copy;
+ }
+
+ bool empty() const;
+
+ bool operator==(const NestedBuildNamespace& Other) const;
+ bool operator!=(const NestedBuildNamespace& Other) const;
+ bool operator<(const NestedBuildNamespace& Other) const;
+
+ friend class JSONWriter;
+ friend class LinkUnitResolution;
+};
+
+} // namespace ssaf
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
new file mode 100644
index 0000000000000..7f11ef0589bf5
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -0,0 +1,47 @@
+//===- EntityName.h ---------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
+
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace ssaf {
+
+/// Uniquely identifies an entity in a program.
+///
+/// EntityName provides a globally unique identifier for program entities that remains
+/// stable across compilation boundaries. This enables whole-program analysis to track
+/// and relate entities across separately compiled translation units.
+class EntityName {
+ std::string USR;
+ llvm::SmallString<16> Suffix;
+ NestedBuildNamespace Namespace;
+
+public:
+ EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
+ NestedBuildNamespace Namespace);
+
+ bool operator==(const EntityName& Other) const;
+ bool operator!=(const EntityName& Other) const;
+ bool operator<(const EntityName& Other) const;
+
+ EntityName makeQualified(NestedBuildNamespace Namespace);
+
+ friend class LinkUnitResolution;
+ friend class SerializationFormat;
+};
+
+} // namespace ssaf
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt
index 1dbd4153d856f..99a2ec684e149 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -50,3 +50,4 @@ add_clang_library(clangAnalysis
add_subdirectory(plugins)
add_subdirectory(FlowSensitive)
add_subdirectory(LifetimeSafety)
+add_subdirectory(Scalable)
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
new file mode 100644
index 0000000000000..87d05e8aa5dc3
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -0,0 +1,85 @@
+//===- ASTMapping.cpp - AST to SSAF Entity mapping --------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements utilities for mapping AST declarations to SSAF entities.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/ASTEntityMapping.h"
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/SmallString.h"
+
+namespace clang {
+namespace ssaf {
+
+std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D) {
+ if (!D)
+ return std::nullopt;
+
+ if (D->isImplicit())
+ return std::nullopt;
+
+ if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->getBuiltinID())
+ return std::nullopt;
+
+ if (!isa<FunctionDecl>(D) && !isa<ParmVarDecl>(D) && !isa<VarDecl>(D) &&
+ !isa<FieldDecl>(D) && !isa<RecordDecl>(D))
+ return std::nullopt;
+
+ llvm::SmallString<16> Suffix;
+ const Decl *USRDecl = D;
+
+ // For parameters, use the parent function's USR with parameter index as suffix
+ if (const auto * PVD = dyn_cast<ParmVarDecl>(D)) {
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(PVD->getParentFunctionOrMethod());
+ if (!FD)
+ return std::nullopt;
+ USRDecl = FD;
+
+ const auto ParamIdx = PVD->getFunctionScopeIndex();
+ llvm::raw_svector_ostream OS(Suffix);
+ // Parameter uses function's USR with 1-based index as suffix
+ OS << (ParamIdx + 1);
+ }
+
+ llvm::SmallString<128> USRBuf;
+ if (clang::index::generateUSRForDecl(USRDecl, USRBuf)) {
+ return std::nullopt;
+ }
+
+ if (USRBuf.empty())
+ return std::nullopt;
+
+ return EntityName(USRBuf.str(), Suffix, {});
+}
+
+std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD) {
+ if (!FD)
+ return std::nullopt;
+
+ if (FD->isImplicit())
+ return std::nullopt;
+
+ if (FD->getBuiltinID())
+ return std::nullopt;
+
+ llvm::SmallString<128> USRBuf;
+ if (clang::index::generateUSRForDecl(FD, USRBuf)) {
+ return std::nullopt;
+ }
+
+ if (USRBuf.empty())
+ return std::nullopt;
+
+ return EntityName(USRBuf.str(), "0", {});
+}
+
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
new file mode 100644
index 0000000000000..ea4693f102cb2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangAnalysisScalable
+ ASTEntityMapping.cpp
+ Model/BuildNamespace.cpp
+ Model/EntityName.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangIndex
+ clangLex
+ clangFrontend
+
+ DEPENDS
+ )
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
new file mode 100644
index 0000000000000..5284a9a87a33a
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -0,0 +1,72 @@
+//===- BuildNamespace.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+namespace ssaf {
+
+std::string toString(BuildNamespaceKind BNK) {
+ switch(BNK) {
+ case BuildNamespaceKind::CompilationUnit: return "compilation_unit";
+ case BuildNamespaceKind::LinkUnit: return "link_unit";
+ }
+ llvm_unreachable("Unknown BuildNamespaceKind");
+}
+
+std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str) {
+ if (Str == "compilation_unit")
+ return BuildNamespaceKind::CompilationUnit;
+ if (Str == "link_unit")
+ return BuildNamespaceKind::LinkUnit;
+ return std::nullopt;
+}
+
+BuildNamespace BuildNamespace::makeTU(llvm::StringRef CompilationId) {
+ return BuildNamespace{BuildNamespaceKind::CompilationUnit, CompilationId.str()};
+}
+
+bool BuildNamespace::operator==(const BuildNamespace& Other) const {
+ return Kind == Other.Kind && Name == Other.Name;
+}
+
+bool BuildNamespace::operator!=(const BuildNamespace& Other) const {
+ return !(*this == Other);
+}
+
+bool BuildNamespace::operator<(const BuildNamespace& Other) const {
+ if (Kind != Other.Kind)
+ return Kind < Other.Kind;
+ return Name < Other.Name;
+}
+
+NestedBuildNamespace NestedBuildNamespace::makeTU(llvm::StringRef CompilationId) {
+ NestedBuildNamespace Result;
+ Result.Namespaces.push_back(BuildNamespace::makeTU(CompilationId));
+ return Result;
+}
+
+bool NestedBuildNamespace::empty() const {
+ return Namespaces.empty();
+}
+
+bool NestedBuildNamespace::operator==(const NestedBuildNamespace& Other) const {
+ return Namespaces == Other.Namespaces;
+}
+
+bool NestedBuildNamespace::operator!=(const NestedBuildNamespace& Other) const {
+ return !(*this == Other);
+}
+
+bool NestedBuildNamespace::operator<(const NestedBuildNamespace& Other) const {
+ return Namespaces < Other.Namespaces;
+}
+
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/lib/Analysis/Scalable/Model/EntityName.cpp b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
new file mode 100644
index 0000000000000..3404ecc58fac2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
@@ -0,0 +1,44 @@
+//===- EntityName.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+
+namespace clang {
+namespace ssaf {
+
+EntityName::EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
+ NestedBuildNamespace Namespace)
+ : USR(USR.str()), Suffix(Suffix), Namespace(std::move(Namespace)) {}
+
+bool EntityName::operator==(const EntityName& Other) const {
+ return USR == Other.USR &&
+ Suffix == Other.Suffix &&
+ Namespace == Other.Namespace;
+}
+
+bool EntityName::operator!=(const EntityName& Other) const {
+ return !(*this == Other);
+}
+
+bool EntityName::operator<(const EntityName& Other) const {
+ if (USR != Other.USR)
+ return USR < Other.USR;
+ if (Suffix != Other.Suffix)
+ return Suffix.str() < Other.Suffix.str();
+ return Namespace < Other.Namespace;
+}
+
+EntityName EntityName::makeQualified(NestedBuildNamespace Namespace) {
+ auto Copy = *this;
+ Copy.Namespace = Copy.Namespace.makeQualified(Namespace);
+
+ return Copy;
+}
+
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/unittests/Analysis/CMakeLists.txt b/clang/unittests/Analysis/CMakeLists.txt
index e0acf436b37c7..97e768b11db69 100644
--- a/clang/unittests/Analysis/CMakeLists.txt
+++ b/clang/unittests/Analysis/CMakeLists.txt
@@ -26,3 +26,4 @@ add_clang_unittest(ClangAnalysisTests
)
add_subdirectory(FlowSensitive)
+add_subdirectory(Scalable)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
new file mode 100644
index 0000000000000..8de0df246cb65
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -0,0 +1,343 @@
+//===- unittests/Analysis/Scalable/ASTEntityMappingTest.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/Analysis/Scalable/ASTEntityMapping.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace ssaf {
+namespace {
+
+// Helper function to find a declaration by name
+template <typename DeclType>
+const DeclType *findDecl(ASTContext &Ctx, StringRef Name) {
+ auto Matcher = namedDecl(hasName(Name)).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ if (Matches.empty())
+ return nullptr;
+ return Matches[0].getNodeAs<DeclType>("decl");
+}
+
+TEST(ASTEntityMappingTest, FunctionDecl) {
+ auto AST = tooling::buildASTFromCode("void foo() {}");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+ ASSERT_NE(FD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(FD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, VarDecl) {
+ auto AST = tooling::buildASTFromCode("int x = 42;");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *VD = findDecl<VarDecl>(Ctx, "x");
+ ASSERT_NE(VD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(VD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, ParmVarDecl) {
+ auto AST = tooling::buildASTFromCode("void foo(int x) {}");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+ ASSERT_NE(FD, nullptr);
+ ASSERT_GT(FD->param_size(), 0u);
+
+ const auto *PVD = FD->getParamDecl(0);
+ ASSERT_NE(PVD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(PVD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, RecordDecl) {
+ auto AST = tooling::buildASTFromCode("struct S {};");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *RD = findDecl<RecordDecl>(Ctx, "S");
+ ASSERT_NE(RD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(RD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FieldDecl) {
+ auto AST = tooling::buildASTFromCode("struct S { int field; };");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FieldDecl>(Ctx, "field");
+ ASSERT_NE(FD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(FD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, NullDecl) {
+ auto EntityName = getLocalEntityNameForDecl(nullptr);
+ EXPECT_FALSE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, ImplicitDecl) {
+ auto AST = tooling::buildASTFromCode(R"(
+ struct S {
+ S() = default;
+ };
+ )", "test.cpp", std::make_shared<PCHContainerOperations>());
+ auto &Ctx = AST->getASTContext();
+
+ const auto *RD = findDecl<CXXRecordDecl>(Ctx, "S");
+ ASSERT_NE(RD, nullptr);
+
+ // Find the implicitly-declared copy constructor
+ for (const auto *Ctor : RD->ctors()) {
+ if (Ctor->isCopyConstructor() && Ctor->isImplicit()) {
+ auto EntityName = getLocalEntityNameForDecl(Ctor);
+ EXPECT_FALSE(EntityName.has_value());
+ return;
+ }
+ }
+}
+
+TEST(ASTEntityMappingTest, BuiltinFunction) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void test() {
+ __builtin_memcpy(0, 0, 0);
+ }
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ // Find the builtin call
+ auto Matcher = callExpr().bind("call");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_FALSE(Matches.empty());
+
+ const auto *CE = Matches[0].getNodeAs<CallExpr>("call");
+ ASSERT_NE(CE, nullptr);
+
+ const auto *Callee = CE->getDirectCallee();
+ if (Callee && Callee->getBuiltinID()) {
+ auto EntityName = getLocalEntityNameForDecl(Callee);
+ EXPECT_FALSE(EntityName.has_value());
+ }
+}
+
+TEST(ASTEntityMappingTest, UnsupportedDecl) {
+ auto AST = tooling::buildASTFromCode("namespace N {}");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *ND = findDecl<NamespaceDecl>(Ctx, "N");
+ ASSERT_NE(ND, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(ND);
+ EXPECT_FALSE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FunctionReturn) {
+ auto AST = tooling::buildASTFromCode("int foo() { return 42; }");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+ ASSERT_NE(FD, nullptr);
+
+ auto EntityName = getLocalEntityNameForFunctionReturn(FD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FunctionReturnNull) {
+ auto EntityName = getLocalEntityNameForFunctionReturn(nullptr);
+ EXPECT_FALSE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FunctionReturnBuiltin) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void test() {
+ __builtin_memcpy(0, 0, 0);
+ }
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ // Find the builtin call
+ auto Matcher = callExpr().bind("call");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_FALSE(Matches.empty());
+
+ const auto *CE = Matches[0].getNodeAs<CallExpr>("call");
+ ASSERT_NE(CE, nullptr);
+
+ const auto *Callee = CE->getDirectCallee();
+ if (Callee && Callee->getBuiltinID()) {
+ auto EntityName = getLocalEntityNameForFunctionReturn(Callee);
+ EXPECT_FALSE(EntityName.has_value());
+ }
+}
+
+TEST(ASTEntityMappingTest, DifferentFunctionsDifferentNames) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void foo() {}
+ void bar() {}
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *Foo = findDecl<FunctionDecl>(Ctx, "foo");
+ const auto *Bar = findDecl<FunctionDecl>(Ctx, "bar");
+ ASSERT_NE(Foo, nullptr);
+ ASSERT_NE(Bar, nullptr);
+
+ auto FooName = getLocalEntityNameForDecl(Foo);
+ auto BarName = getLocalEntityNameForDecl(Bar);
+ ASSERT_TRUE(FooName.has_value());
+ ASSERT_TRUE(BarName.has_value());
+
+ EXPECT_NE(*FooName, *BarName);
+}
+
+// Redeclaration tests
+
+TEST(ASTEntityMappingTest, FunctionRedeclaration) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void foo();
+ void foo() {}
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ auto Matcher = functionDecl(hasName("foo")).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_EQ(Matches.size(), 2u);
+
+ const auto *Decl1 = Matches[0].getNodeAs<FunctionDecl>("decl");
+ const auto *Decl2 = Matches[1].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(Decl1, nullptr);
+ ASSERT_NE(Decl2, nullptr);
+
+ auto Name1 = getLocalEntityNameForDecl(Decl1);
+ auto Name2 = getLocalEntityNameForDecl(Decl2);
+ ASSERT_TRUE(Name1.has_value());
+ ASSERT_TRUE(Name2.has_value());
+
+ EXPECT_EQ(*Name1, *Name2);
+}
+
+TEST(ASTEntityMappingTest, VarRedeclaration) {
+ auto AST = tooling::buildASTFromCode(R"(
+ extern int x;
+ int x = 42;
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ auto Matcher = varDecl(hasName("x")).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_EQ(Matches.size(), 2u);
+
+ const auto *Decl1 = Matches[0].getNodeAs<VarDecl>("decl");
+ const auto *Decl2 = Matches[1].getNodeAs<VarDecl>("decl");
+ ASSERT_NE(Decl1, nullptr);
+ ASSERT_NE(Decl2, nullptr);
+
+ auto Name1 = getLocalEntityNameForDecl(Decl1);
+ auto Name2 = getLocalEntityNameForDecl(Decl2);
+ ASSERT_TRUE(Name1.has_value());
+ ASSERT_TRUE(Name2.has_value());
+
+ EXPECT_EQ(*Name1, *Name2);
+}
+
+TEST(ASTEntityMappingTest, RecordRedeclaration) {
+ auto AST = tooling::buildASTFromCode(R"(
+ struct S;
+ struct S {};
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ // Use recordDecl(isStruct()) to avoid matching implicit typedefs
+ auto Matcher = recordDecl(hasName("S"), isStruct()).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_GE(Matches.size(), 2u);
+
+ const auto *Decl1 = Matches[0].getNodeAs<RecordDecl>("decl");
+ const auto *Decl2 = Matches[1].getNodeAs<RecordDecl>("decl");
+ ASSERT_NE(Decl1, nullptr);
+ ASSERT_NE(Decl2, nullptr);
+
+ auto Name1 = getLocalEntityNameForDecl(Decl1);
+ auto Name2 = getLocalEntityNameForDecl(Decl2);
+ ASSERT_TRUE(Name1.has_value());
+ ASSERT_TRUE(Name2.has_value());
+
+ EXPECT_EQ(*Name1, *Name2);
+}
+
+TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void foo(int x);
+ void foo(int x) {}
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ auto Matcher = functionDecl(hasName("foo")).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_EQ(Matches.size(), 2u);
+
+ const auto *Func1 = Matches[0].getNodeAs<FunctionDecl>("decl");
+ const auto *Func2 = Matches[1].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(Func1, nullptr);
+ ASSERT_NE(Func2, nullptr);
+ ASSERT_GT(Func1->param_size(), 0u);
+ ASSERT_GT(Func2->param_size(), 0u);
+
+ const auto *Param1 = Func1->getParamDecl(0);
+ const auto *Param2 = Func2->getParamDecl(0);
+ ASSERT_NE(Param1, nullptr);
+ ASSERT_NE(Param2, nullptr);
+
+ auto Name1 = getLocalEntityNameForDecl(Param1);
+ auto Name2 = getLocalEntityNameForDecl(Param2);
+ ASSERT_TRUE(Name1.has_value());
+ ASSERT_TRUE(Name2.has_value());
+
+ EXPECT_EQ(*Name1, *Name2);
+}
+
+TEST(ASTEntityMappingTest, FunctionReturnRedeclaration) {
+ auto AST = tooling::buildASTFromCode(R"(
+ int foo();
+ int foo() { return 42; }
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ auto Matcher = functionDecl(hasName("foo")).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_EQ(Matches.size(), 2u);
+
+ const auto *Decl1 = Matches[0].getNodeAs<FunctionDecl>("decl");
+ const auto *Decl2 = Matches[1].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(Decl1, nullptr);
+ ASSERT_NE(Decl2, nullptr);
+
+ auto Name1 = getLocalEntityNameForFunctionReturn(Decl1);
+ auto Name2 = getLocalEntityNameForFunctionReturn(Decl2);
+ ASSERT_TRUE(Name1.has_value());
+ ASSERT_TRUE(Name2.has_value());
+
+ EXPECT_EQ(*Name1, *Name2);
+}
+
+} // namespace
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp b/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
new file mode 100644
index 0000000000000..aa4155faa30f8
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
@@ -0,0 +1,99 @@
+//===- unittests/Analysis/Scalable/BuildNamespaceTest.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/Analysis/Scalable/Model/BuildNamespace.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ssaf {
+namespace {
+
+TEST(BuildNamespaceTest, Equality) {
+ auto BN1 = BuildNamespace::makeTU("test.cpp");
+ auto BN2 = BuildNamespace::makeTU("test.cpp");
+ auto BN3 = BuildNamespace::makeTU("other.cpp");
+
+ EXPECT_EQ(BN1, BN2);
+ EXPECT_NE(BN1, BN3);
+}
+
+TEST(BuildNamespaceTest, DifferentKinds) {
+ BuildNamespace CU(BuildNamespaceKind::CompilationUnit, "test");
+ BuildNamespace LU(BuildNamespaceKind::LinkUnit, "test");
+
+ EXPECT_NE(CU, LU);
+}
+
+TEST(BuildNamespaceTest, ToStringRoundtripCompilationUnit) {
+ auto Kind = BuildNamespaceKind::CompilationUnit;
+ auto Str = toString(Kind);
+ auto Parsed = parseBuildNamespaceKind(Str);
+
+ ASSERT_TRUE(Parsed.has_value());
+ EXPECT_EQ(Kind, *Parsed);
+}
+
+TEST(BuildNamespaceTest, ToStringRoundtripLinkUnit) {
+ auto Kind = BuildNamespaceKind::LinkUnit;
+ auto Str = toString(Kind);
+ auto Parsed = parseBuildNamespaceKind(Str);
+
+ ASSERT_TRUE(Parsed.has_value());
+ EXPECT_EQ(Kind, *Parsed);
+}
+
+// NestedBuildNamespace Tests
+
+TEST(NestedBuildNamespaceTest, DefaultConstruction) {
+ NestedBuildNamespace NBN;
+ EXPECT_TRUE(NBN.empty());
+}
+
+TEST(NestedBuildNamespaceTest, SingleNamespaceConstruction) {
+ auto BN = BuildNamespace::makeTU("test.cpp");
+ NestedBuildNamespace NBN(BN);
+
+ EXPECT_FALSE(NBN.empty());
+}
+
+TEST(NestedBuildNamespaceTest, MakeTU) {
+ auto NBN = NestedBuildNamespace::makeTU("test.cpp");
+ EXPECT_FALSE(NBN.empty());
+}
+
+TEST(NestedBuildNamespaceTest, Equality) {
+ auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN2 = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN3 = NestedBuildNamespace::makeTU("other.cpp");
+
+ EXPECT_EQ(NBN1, NBN2);
+ EXPECT_NE(NBN1, NBN3);
+}
+
+TEST(NestedBuildNamespaceTest, MakeQualified) {
+ auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
+ BuildNamespace LinkNS(BuildNamespaceKind::LinkUnit, "app");
+ NestedBuildNamespace NBN2(LinkNS);
+
+ auto Qualified = NBN1.makeQualified(NBN2);
+
+ EXPECT_NE(Qualified, NBN1);
+ EXPECT_NE(Qualified, NBN2);
+}
+
+TEST(NestedBuildNamespaceTest, EmptyQualified) {
+ NestedBuildNamespace Empty;
+ auto NBN = NestedBuildNamespace::makeTU("test.cpp");
+
+ auto Qualified = Empty.makeQualified(NBN);
+ EXPECT_EQ(Qualified, NBN);
+}
+
+} // namespace
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
new file mode 100644
index 0000000000000..95aaa2aea253c
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_distinct_clang_unittest(ClangScalableAnalysisFrameworkTests
+ BuildNamespaceTest.cpp
+ EntityNameTest.cpp
+ ASTEntityMappingTest.cpp
+
+ CLANG_LIBS
+ clangAnalysisScalable
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangSerialization
+ clangTooling
+ )
+
+add_custom_target(ClangScalableAnalysisTests
+ DEPENDS
+ ClangScalableAnalysisFrameworkTests
+ )
diff --git a/clang/unittests/Analysis/Scalable/EntityNameTest.cpp b/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
new file mode 100644
index 0000000000000..f32807f3be1c1
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
@@ -0,0 +1,62 @@
+//===- unittests/Analysis/Scalable/EntityNameTest.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/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ssaf {
+namespace {
+
+TEST(EntityNameTest, Equality) {
+ auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN2 = NestedBuildNamespace::makeTU("test.cpp");
+
+ EntityName EN1("c:@F at foo", "", NBN1);
+ EntityName EN2("c:@F at foo", "", NBN2);
+ EntityName EN3("c:@F at bar", "", NBN1);
+
+ EXPECT_EQ(EN1, EN2);
+ EXPECT_NE(EN1, EN3);
+}
+
+TEST(EntityNameTest, EqualityWithDifferentSuffix) {
+ auto NBN = NestedBuildNamespace::makeTU("test.cpp");
+
+ EntityName EN1("c:@F at foo", "1", NBN);
+ EntityName EN2("c:@F at foo", "2", NBN);
+
+ EXPECT_NE(EN1, EN2);
+}
+
+TEST(EntityNameTest, EqualityWithDifferentNamespace) {
+ auto NBN1 = NestedBuildNamespace::makeTU("test1.cpp");
+ auto NBN2 = NestedBuildNamespace::makeTU("test2.cpp");
+
+ EntityName EN1("c:@F at foo", "", NBN1);
+ EntityName EN2("c:@F at foo", "", NBN2);
+
+ EXPECT_NE(EN1, EN2);
+}
+
+TEST(EntityNameTest, MakeQualified) {
+ auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
+ EntityName EN("c:@F at foo", "", NBN1);
+
+ BuildNamespace LinkNS(BuildNamespaceKind::LinkUnit, "app");
+ NestedBuildNamespace NBN2(LinkNS);
+
+ auto Qualified = EN.makeQualified(NBN2);
+
+ EXPECT_NE(Qualified, EN);
+}
+
+} // namespace
+} // namespace ssaf
+} // namespace clang
>From d1f0e7975c34e5346aa62a9072fdad638fbeac90 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Wed, 3 Dec 2025 14:12:13 -0800
Subject: [PATCH 02/29] [clang][ssaf] Use nested namespace definitions
---
clang/include/clang/Analysis/Scalable/ASTEntityMapping.h | 6 ++----
.../include/clang/Analysis/Scalable/Model/BuildNamespace.h | 6 ++----
clang/include/clang/Analysis/Scalable/Model/EntityName.h | 6 ++----
clang/lib/Analysis/Scalable/ASTEntityMapping.cpp | 6 ++----
clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp | 6 ++----
clang/lib/Analysis/Scalable/Model/EntityName.cpp | 6 ++----
clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 6 ++----
clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp | 6 ++----
clang/unittests/Analysis/Scalable/EntityNameTest.cpp | 6 ++----
9 files changed, 18 insertions(+), 36 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
index a137e8b741821..d1a5b3790ef50 100644
--- a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -14,8 +14,7 @@
#include "llvm/ADT/StringRef.h"
#include <optional>
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
/// Maps a declaration to an EntityName.
///
@@ -40,7 +39,6 @@ std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D);
/// \return An EntityName for the function's return type.
std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD);
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index c4bf7146e461f..f409dac272ea1 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -14,8 +14,7 @@
#include <string>
#include <vector>
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
enum class BuildNamespaceKind : unsigned short {
CompilationUnit,
@@ -78,7 +77,6 @@ class NestedBuildNamespace {
friend class LinkUnitResolution;
};
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 7f11ef0589bf5..f76ac6f99e15a 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -14,8 +14,7 @@
#include "llvm/ADT/StringRef.h"
#include <string>
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
/// Uniquely identifies an entity in a program.
///
@@ -41,7 +40,6 @@ class EntityName {
friend class SerializationFormat;
};
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
index 87d05e8aa5dc3..a82369c1c471e 100644
--- a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -16,8 +16,7 @@
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/SmallString.h"
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D) {
if (!D)
@@ -81,5 +80,4 @@ std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl
return EntityName(USRBuf.str(), "0", {});
}
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
index 5284a9a87a33a..25785cab0236c 100644
--- a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -9,8 +9,7 @@
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "llvm/Support/ErrorHandling.h"
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
std::string toString(BuildNamespaceKind BNK) {
switch(BNK) {
@@ -68,5 +67,4 @@ bool NestedBuildNamespace::operator<(const NestedBuildNamespace& Other) const {
return Namespaces < Other.Namespaces;
}
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
diff --git a/clang/lib/Analysis/Scalable/Model/EntityName.cpp b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
index 3404ecc58fac2..cc157967937ab 100644
--- a/clang/lib/Analysis/Scalable/Model/EntityName.cpp
+++ b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
@@ -8,8 +8,7 @@
#include "clang/Analysis/Scalable/Model/EntityName.h"
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
EntityName::EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
NestedBuildNamespace Namespace)
@@ -40,5 +39,4 @@ EntityName EntityName::makeQualified(NestedBuildNamespace Namespace) {
return Copy;
}
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 8de0df246cb65..555b782e09a06 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -16,8 +16,7 @@
using namespace clang::ast_matchers;
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
namespace {
// Helper function to find a declaration by name
@@ -339,5 +338,4 @@ TEST(ASTEntityMappingTest, FunctionReturnRedeclaration) {
}
} // namespace
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
diff --git a/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp b/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
index aa4155faa30f8..e62e3257d5608 100644
--- a/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
+++ b/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
@@ -9,8 +9,7 @@
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "gtest/gtest.h"
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
namespace {
TEST(BuildNamespaceTest, Equality) {
@@ -95,5 +94,4 @@ TEST(NestedBuildNamespaceTest, EmptyQualified) {
}
} // namespace
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
diff --git a/clang/unittests/Analysis/Scalable/EntityNameTest.cpp b/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
index f32807f3be1c1..0c4ef41a8bf60 100644
--- a/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
+++ b/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
@@ -10,8 +10,7 @@
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "gtest/gtest.h"
-namespace clang {
-namespace ssaf {
+namespace clang::ssaf {
namespace {
TEST(EntityNameTest, Equality) {
@@ -58,5 +57,4 @@ TEST(EntityNameTest, MakeQualified) {
}
} // namespace
-} // namespace ssaf
-} // namespace clang
+} // namespace clang::ssaf
>From 8da5617ac57d620c4b61f26d319facd40c99f4d4 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Wed, 3 Dec 2025 14:32:47 -0800
Subject: [PATCH 03/29] [clang][ssaf] Return StringRef from
toString(BuildNamespaceKind)
---
clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h | 2 +-
clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index f409dac272ea1..2abedea24cc19 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -21,7 +21,7 @@ enum class BuildNamespaceKind : unsigned short {
LinkUnit
};
-std::string toString(BuildNamespaceKind BNK);
+llvm::StringRef toString(BuildNamespaceKind BNK);
std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str);
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
index 25785cab0236c..2d0b8aeb49c9a 100644
--- a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -11,7 +11,7 @@
namespace clang::ssaf {
-std::string toString(BuildNamespaceKind BNK) {
+llvm::StringRef toString(BuildNamespaceKind BNK) {
switch(BNK) {
case BuildNamespaceKind::CompilationUnit: return "compilation_unit";
case BuildNamespaceKind::LinkUnit: return "link_unit";
>From 0859de93b5fff9231e80725abe80437c9567845f Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Wed, 3 Dec 2025 14:49:14 -0800
Subject: [PATCH 04/29] [clang][ssaf] Fix header guards
---
clang/include/clang/Analysis/Scalable/ASTEntityMapping.h | 6 +++---
.../include/clang/Analysis/Scalable/Model/BuildNamespace.h | 6 +++---
clang/include/clang/Analysis/Scalable/Model/EntityName.h | 6 +++---
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
index d1a5b3790ef50..9a2c01573c4ed 100644
--- a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ASTENTITYMAPPING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ASTENTITYMAPPING_H
#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/AST/Decl.h"
@@ -41,4 +41,4 @@ std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ASTENTITYMAPPING_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 2abedea24cc19..0f7ee5da709e8 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
#include "llvm/ADT/StringRef.h"
#include <optional>
@@ -79,4 +79,4 @@ class NestedBuildNamespace {
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index f76ac6f99e15a..31c1ef1a69f8e 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYNAME_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYNAME_H
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "llvm/ADT/SmallString.h"
@@ -42,4 +42,4 @@ class EntityName {
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYNAME_H
>From 3ad2d3d217f325d250ee4c5ff857cde36118104a Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Wed, 3 Dec 2025 15:22:09 -0800
Subject: [PATCH 05/29] [clang][ssaf] Optimize
NestedBuildNamespace::makeQualified
---
clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 0f7ee5da709e8..2a2e090c7fddd 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include <optional>
#include <string>
@@ -62,8 +63,8 @@ class NestedBuildNamespace {
NestedBuildNamespace makeQualified(NestedBuildNamespace Namespace) {
auto Copy = *this;
- for (const auto& N : Namespace.Namespaces)
- Copy.Namespaces.push_back(N);
+ Copy.Namespaces.reserve(Copy.Namespaces.size() + Namespace.Namespaces.size());
+ llvm::append_range(Copy.Namespaces, Namespace.Namespaces);
return Copy;
}
>From 6b840ce6b2523b9f87f45790c9198346c51f3953 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Wed, 3 Dec 2025 15:40:35 -0800
Subject: [PATCH 06/29] [clang][ssaf] Add doc comments to makeQualified methods
---
clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h | 5 ++++-
clang/include/clang/Analysis/Scalable/Model/EntityName.h | 5 ++++-
clang/lib/Analysis/Scalable/Model/EntityName.cpp | 2 +-
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 2a2e090c7fddd..42e8269c352b8 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -61,7 +61,10 @@ class NestedBuildNamespace {
static NestedBuildNamespace makeTU(llvm::StringRef CompilationId);
- NestedBuildNamespace makeQualified(NestedBuildNamespace Namespace) {
+ /// Creates a new NestedBuildNamespace by appending additional namespace.
+ ///
+ /// \param Namespace The namespace to append.
+ NestedBuildNamespace makeQualified(NestedBuildNamespace Namespace) const {
auto Copy = *this;
Copy.Namespaces.reserve(Copy.Namespaces.size() + Namespace.Namespaces.size());
llvm::append_range(Copy.Namespaces, Namespace.Namespaces);
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 31c1ef1a69f8e..61d87f8c5c83d 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -34,7 +34,10 @@ class EntityName {
bool operator!=(const EntityName& Other) const;
bool operator<(const EntityName& Other) const;
- EntityName makeQualified(NestedBuildNamespace Namespace);
+ /// Creates a new EntityName with additional build namespace qualification.
+ ///
+ /// \param Namespace The namespace steps to append to this entity's namespace.
+ EntityName makeQualified(NestedBuildNamespace Namespace) const;
friend class LinkUnitResolution;
friend class SerializationFormat;
diff --git a/clang/lib/Analysis/Scalable/Model/EntityName.cpp b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
index cc157967937ab..14e8d2bb85c9d 100644
--- a/clang/lib/Analysis/Scalable/Model/EntityName.cpp
+++ b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
@@ -32,7 +32,7 @@ bool EntityName::operator<(const EntityName& Other) const {
return Namespace < Other.Namespace;
}
-EntityName EntityName::makeQualified(NestedBuildNamespace Namespace) {
+EntityName EntityName::makeQualified(NestedBuildNamespace Namespace) const {
auto Copy = *this;
Copy.Namespace = Copy.Namespace.makeQualified(Namespace);
>From 7467cf7d7efd8e146919bb3f1038a192e3837fa4 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 16:15:25 -0800
Subject: [PATCH 07/29] [clang][ssaf] Add asTuple helper to EntityName and
BuildNamespace
---
.../clang/Analysis/Scalable/Model/BuildNamespace.h | 3 +++
.../clang/Analysis/Scalable/Model/EntityName.h | 2 ++
clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp | 7 +++----
clang/lib/Analysis/Scalable/Model/EntityName.cpp | 12 +++---------
4 files changed, 11 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 42e8269c352b8..6311d46564985 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -30,6 +30,9 @@ std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str);
class BuildNamespace {
BuildNamespaceKind Kind;
std::string Name;
+
+ auto asTuple() const { return std::tie(Kind, Name); }
+
public:
BuildNamespace(BuildNamespaceKind Kind, llvm::StringRef Name)
: Kind(Kind), Name(Name.str()) {}
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 61d87f8c5c83d..ea26d09f3e250 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -26,6 +26,8 @@ class EntityName {
llvm::SmallString<16> Suffix;
NestedBuildNamespace Namespace;
+ auto asTuple() const { return std::tie(USR, Suffix, Namespace); }
+
public:
EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
NestedBuildNamespace Namespace);
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
index 2d0b8aeb49c9a..7676d56f867e3 100644
--- a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -8,6 +8,7 @@
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "llvm/Support/ErrorHandling.h"
+#include <tuple>
namespace clang::ssaf {
@@ -32,7 +33,7 @@ BuildNamespace BuildNamespace::makeTU(llvm::StringRef CompilationId) {
}
bool BuildNamespace::operator==(const BuildNamespace& Other) const {
- return Kind == Other.Kind && Name == Other.Name;
+ return asTuple() == Other.asTuple();
}
bool BuildNamespace::operator!=(const BuildNamespace& Other) const {
@@ -40,9 +41,7 @@ bool BuildNamespace::operator!=(const BuildNamespace& Other) const {
}
bool BuildNamespace::operator<(const BuildNamespace& Other) const {
- if (Kind != Other.Kind)
- return Kind < Other.Kind;
- return Name < Other.Name;
+ return asTuple() < Other.asTuple();
}
NestedBuildNamespace NestedBuildNamespace::makeTU(llvm::StringRef CompilationId) {
diff --git a/clang/lib/Analysis/Scalable/Model/EntityName.cpp b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
index 14e8d2bb85c9d..b8c1ba8a052ca 100644
--- a/clang/lib/Analysis/Scalable/Model/EntityName.cpp
+++ b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
@@ -15,21 +15,15 @@ EntityName::EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
: USR(USR.str()), Suffix(Suffix), Namespace(std::move(Namespace)) {}
bool EntityName::operator==(const EntityName& Other) const {
- return USR == Other.USR &&
- Suffix == Other.Suffix &&
- Namespace == Other.Namespace;
+ return asTuple() == Other.asTuple();
}
bool EntityName::operator!=(const EntityName& Other) const {
return !(*this == Other);
}
-bool EntityName::operator<(const EntityName& Other) const {
- if (USR != Other.USR)
- return USR < Other.USR;
- if (Suffix != Other.Suffix)
- return Suffix.str() < Other.Suffix.str();
- return Namespace < Other.Namespace;
+bool EntityName::operator<(const EntityName &Other) const {
+ return asTuple() < Other.asTuple();
}
EntityName EntityName::makeQualified(NestedBuildNamespace Namespace) const {
>From 5750baf2d68f3dd3d4d2ed244fa43b130d3edc3d Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 16:19:56 -0800
Subject: [PATCH 08/29] [clang][ssaf] Simplify AST node type check with isa<>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
clang/lib/Analysis/Scalable/ASTEntityMapping.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
index a82369c1c471e..60d44e06e5e12 100644
--- a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -28,8 +28,7 @@ std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D) {
if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->getBuiltinID())
return std::nullopt;
- if (!isa<FunctionDecl>(D) && !isa<ParmVarDecl>(D) && !isa<VarDecl>(D) &&
- !isa<FieldDecl>(D) && !isa<RecordDecl>(D))
+ if (!isa<FunctionDecl, ParmVarDecl, VarDecl, FieldDecl, RecordDecl>(D))
return std::nullopt;
llvm::SmallString<16> Suffix;
>From c25b2bf5afb4d5a5c6662d06f88659947537f974 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 16:24:45 -0800
Subject: [PATCH 09/29] [clang][ssaf] Add default value param names
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
clang/lib/Analysis/Scalable/ASTEntityMapping.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
index 60d44e06e5e12..35ff8fa167d58 100644
--- a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -76,7 +76,7 @@ std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl
if (USRBuf.empty())
return std::nullopt;
- return EntityName(USRBuf.str(), "0", {});
+ return EntityName(USRBuf.str(), /*Suffix=*/"0", /*Namespace=*/{});
}
} // namespace clang::ssaf
>From 325f74d3737ebb2b25a9346dac715e3f09fc9fc5 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 16:46:15 -0800
Subject: [PATCH 10/29] [clang][ssaf][NFC] Make test helper to return canonical
decl
---
clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 555b782e09a06..47226f385597f 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -26,7 +26,9 @@ const DeclType *findDecl(ASTContext &Ctx, StringRef Name) {
auto Matches = match(Matcher, Ctx);
if (Matches.empty())
return nullptr;
- return Matches[0].getNodeAs<DeclType>("decl");
+ if (auto Result = Matches[0].getNodeAs<DeclType>("decl"))
+ return dyn_cast<DeclType>(Result->getCanonicalDecl());
+ return nullptr;
}
TEST(ASTEntityMappingTest, FunctionDecl) {
>From ffc51043a7fbffa0b8c1f742384710200f68cec2 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 17:09:26 -0800
Subject: [PATCH 11/29] [clang][ssaf] Make a test assert more specific
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 47226f385597f..53d3d23b42049 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -59,7 +59,7 @@ TEST(ASTEntityMappingTest, ParmVarDecl) {
const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
ASSERT_NE(FD, nullptr);
- ASSERT_GT(FD->param_size(), 0u);
+ ASSERT_EQ(FD->param_size(), 1u);
const auto *PVD = FD->getParamDecl(0);
ASSERT_NE(PVD, nullptr);
>From d488921101e5af548aa442516eaab5dc5ab0126e Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 17:29:18 -0800
Subject: [PATCH 12/29] [clang][ssaf][NFC] Refactor implicit ctor decl search
in a test
---
.../Analysis/Scalable/ASTEntityMappingTest.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 53d3d23b42049..7420a825650ad 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -9,6 +9,7 @@
#include "clang/Analysis/Scalable/ASTEntityMapping.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
@@ -107,13 +108,16 @@ TEST(ASTEntityMappingTest, ImplicitDecl) {
ASSERT_NE(RD, nullptr);
// Find the implicitly-declared copy constructor
+ const CXXConstructorDecl * ImplCtor = nullptr;
for (const auto *Ctor : RD->ctors()) {
if (Ctor->isCopyConstructor() && Ctor->isImplicit()) {
- auto EntityName = getLocalEntityNameForDecl(Ctor);
- EXPECT_FALSE(EntityName.has_value());
- return;
+ ImplCtor = Ctor;
+ break;
}
}
+
+ auto EntityName = getLocalEntityNameForDecl(ImplCtor);
+ EXPECT_FALSE(EntityName.has_value());
}
TEST(ASTEntityMappingTest, BuiltinFunction) {
>From df0e69658af8b93075bcabea9d87eca3ad3127aa Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 17:32:42 -0800
Subject: [PATCH 13/29] [clang][ssaf][NFC] Make a test assertion more specific
---
clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 7420a825650ad..9049317b1b277 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -131,7 +131,7 @@ TEST(ASTEntityMappingTest, BuiltinFunction) {
// Find the builtin call
auto Matcher = callExpr().bind("call");
auto Matches = match(Matcher, Ctx);
- ASSERT_FALSE(Matches.empty());
+ ASSERT_EQ(Matches.size(), 1ul);
const auto *CE = Matches[0].getNodeAs<CallExpr>("call");
ASSERT_NE(CE, nullptr);
>From f7d033b870e956903def935c5fae690e3ed8c157 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Thu, 4 Dec 2025 17:36:43 -0800
Subject: [PATCH 14/29] [clang][ssaf][NFC] Add assertion to a test
---
.../unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 9049317b1b277..be70ee79e2e60 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -137,10 +137,11 @@ TEST(ASTEntityMappingTest, BuiltinFunction) {
ASSERT_NE(CE, nullptr);
const auto *Callee = CE->getDirectCallee();
- if (Callee && Callee->getBuiltinID()) {
- auto EntityName = getLocalEntityNameForDecl(Callee);
- EXPECT_FALSE(EntityName.has_value());
- }
+ ASSERT_NE(Callee, nullptr);
+ ASSERT_NE(Callee->getBuiltinID(), 0u /* not a built-in */);
+
+ auto EntityName = getLocalEntityNameForDecl(Callee);
+ EXPECT_FALSE(EntityName.has_value());
}
TEST(ASTEntityMappingTest, UnsupportedDecl) {
>From a7c5aa7ff104466b53e3d96af7400fa1975598c8 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 5 Dec 2025 10:27:01 -0800
Subject: [PATCH 15/29] [clang][ssaf][NFC] Improve redeclaration entity name
test
---
.../Scalable/ASTEntityMappingTest.cpp | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index be70ee79e2e60..bdddc95287ae6 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -225,19 +225,22 @@ TEST(ASTEntityMappingTest, FunctionRedeclaration) {
auto Matcher = functionDecl(hasName("foo")).bind("decl");
auto Matches = match(Matcher, Ctx);
- ASSERT_EQ(Matches.size(), 2u);
+ ASSERT_GE(Matches.size(), 2u);
- const auto *Decl1 = Matches[0].getNodeAs<FunctionDecl>("decl");
- const auto *Decl2 = Matches[1].getNodeAs<FunctionDecl>("decl");
- ASSERT_NE(Decl1, nullptr);
- ASSERT_NE(Decl2, nullptr);
+ const auto *FirstDecl = Matches[0].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(FirstDecl, nullptr);
- auto Name1 = getLocalEntityNameForDecl(Decl1);
- auto Name2 = getLocalEntityNameForDecl(Decl2);
- ASSERT_TRUE(Name1.has_value());
- ASSERT_TRUE(Name2.has_value());
+ auto FirstName = getLocalEntityNameForDecl(FirstDecl);
+ ASSERT_TRUE(FirstName.has_value());
- EXPECT_EQ(*Name1, *Name2);
+ for (size_t I = 1; I < Matches.size(); ++I) {
+ const auto *Decl = Matches[I].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(Decl, nullptr);
+
+ auto Name = getLocalEntityNameForDecl(Decl);
+ ASSERT_TRUE(Name.has_value());
+ EXPECT_EQ(*FirstName, *Name);
+ }
}
TEST(ASTEntityMappingTest, VarRedeclaration) {
>From 2c8c699905682ad1f1fdf8183d14b1c426ed4b72 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 5 Dec 2025 14:23:27 -0800
Subject: [PATCH 16/29] [clang][ssaf][NFC] Refactor redeclaration tests
---
.../Scalable/ASTEntityMappingTest.cpp | 86 ++++++++++---------
1 file changed, 45 insertions(+), 41 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index bdddc95287ae6..13903201b659a 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -254,17 +254,20 @@ TEST(ASTEntityMappingTest, VarRedeclaration) {
auto Matches = match(Matcher, Ctx);
ASSERT_EQ(Matches.size(), 2u);
- const auto *Decl1 = Matches[0].getNodeAs<VarDecl>("decl");
- const auto *Decl2 = Matches[1].getNodeAs<VarDecl>("decl");
- ASSERT_NE(Decl1, nullptr);
- ASSERT_NE(Decl2, nullptr);
+ const auto *FirstDecl = Matches[0].getNodeAs<VarDecl>("decl");
+ ASSERT_NE(FirstDecl, nullptr);
- auto Name1 = getLocalEntityNameForDecl(Decl1);
- auto Name2 = getLocalEntityNameForDecl(Decl2);
- ASSERT_TRUE(Name1.has_value());
- ASSERT_TRUE(Name2.has_value());
+ auto FirstName = getLocalEntityNameForDecl(FirstDecl);
+ ASSERT_TRUE(FirstName.has_value());
+
+ for (size_t I = 1; I < Matches.size(); ++I) {
+ const auto *Decl = Matches[I].getNodeAs<VarDecl>("decl");
+ ASSERT_NE(Decl, nullptr);
- EXPECT_EQ(*Name1, *Name2);
+ auto Name = getLocalEntityNameForDecl(Decl);
+ ASSERT_TRUE(Name.has_value());
+ EXPECT_EQ(*FirstName, *Name);
+ }
}
TEST(ASTEntityMappingTest, RecordRedeclaration) {
@@ -274,22 +277,24 @@ TEST(ASTEntityMappingTest, RecordRedeclaration) {
)");
auto &Ctx = AST->getASTContext();
- // Use recordDecl(isStruct()) to avoid matching implicit typedefs
- auto Matcher = recordDecl(hasName("S"), isStruct()).bind("decl");
+ auto Matcher = recordDecl(hasName("S"), unless(isImplicit())).bind("decl");
auto Matches = match(Matcher, Ctx);
ASSERT_GE(Matches.size(), 2u);
- const auto *Decl1 = Matches[0].getNodeAs<RecordDecl>("decl");
- const auto *Decl2 = Matches[1].getNodeAs<RecordDecl>("decl");
- ASSERT_NE(Decl1, nullptr);
- ASSERT_NE(Decl2, nullptr);
+ const auto *FirstDecl = Matches[0].getNodeAs<RecordDecl>("decl");
+ ASSERT_NE(FirstDecl, nullptr);
- auto Name1 = getLocalEntityNameForDecl(Decl1);
- auto Name2 = getLocalEntityNameForDecl(Decl2);
- ASSERT_TRUE(Name1.has_value());
- ASSERT_TRUE(Name2.has_value());
+ auto FirstName = getLocalEntityNameForDecl(FirstDecl);
+ ASSERT_TRUE(FirstName.has_value());
+
+ for (size_t I = 1; I < Matches.size(); ++I) {
+ const auto *Decl = Matches[I].getNodeAs<RecordDecl>("decl");
+ ASSERT_NE(Decl, nullptr);
- EXPECT_EQ(*Name1, *Name2);
+ auto Name = getLocalEntityNameForDecl(Decl);
+ ASSERT_TRUE(Name.has_value());
+ EXPECT_EQ(*FirstName, *Name);
+ }
}
TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
@@ -303,24 +308,21 @@ TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
auto Matches = match(Matcher, Ctx);
ASSERT_EQ(Matches.size(), 2u);
- const auto *Func1 = Matches[0].getNodeAs<FunctionDecl>("decl");
- const auto *Func2 = Matches[1].getNodeAs<FunctionDecl>("decl");
- ASSERT_NE(Func1, nullptr);
- ASSERT_NE(Func2, nullptr);
- ASSERT_GT(Func1->param_size(), 0u);
- ASSERT_GT(Func2->param_size(), 0u);
+ const auto *FirstFuncDecl = Matches[0].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(FirstFuncDecl, nullptr);
+ ASSERT_GT(FirstFuncDecl->param_size(), 0u);
- const auto *Param1 = Func1->getParamDecl(0);
- const auto *Param2 = Func2->getParamDecl(0);
- ASSERT_NE(Param1, nullptr);
- ASSERT_NE(Param2, nullptr);
+ auto ParamEName = getLocalEntityNameForDecl(FirstFuncDecl->getParamDecl(0));
+ ASSERT_TRUE(ParamEName.has_value());
- auto Name1 = getLocalEntityNameForDecl(Param1);
- auto Name2 = getLocalEntityNameForDecl(Param2);
- ASSERT_TRUE(Name1.has_value());
- ASSERT_TRUE(Name2.has_value());
+ for (size_t I = 1; I < Matches.size(); ++I) {
+ const auto *FDecl = Matches[I].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(FDecl, nullptr);
+ ASSERT_GT(FDecl->param_size(), 0u);
- EXPECT_EQ(*Name1, *Name2);
+ auto ParamRedeclEName = getLocalEntityNameForDecl(FDecl->getParamDecl(0));
+ EXPECT_EQ(*ParamEName, *ParamRedeclEName);
+ }
}
TEST(ASTEntityMappingTest, FunctionReturnRedeclaration) {
@@ -335,16 +337,18 @@ TEST(ASTEntityMappingTest, FunctionReturnRedeclaration) {
ASSERT_EQ(Matches.size(), 2u);
const auto *Decl1 = Matches[0].getNodeAs<FunctionDecl>("decl");
- const auto *Decl2 = Matches[1].getNodeAs<FunctionDecl>("decl");
ASSERT_NE(Decl1, nullptr);
- ASSERT_NE(Decl2, nullptr);
-
auto Name1 = getLocalEntityNameForFunctionReturn(Decl1);
- auto Name2 = getLocalEntityNameForFunctionReturn(Decl2);
ASSERT_TRUE(Name1.has_value());
- ASSERT_TRUE(Name2.has_value());
- EXPECT_EQ(*Name1, *Name2);
+ for (size_t I = 1; I < Matches.size(); ++I) {
+ const auto *FDecl = Matches[I].getNodeAs<FunctionDecl>("decl");
+ ASSERT_NE(FDecl, nullptr);
+
+ auto Name = getLocalEntityNameForFunctionReturn(Decl1);
+ ASSERT_TRUE(Name.has_value());
+ EXPECT_EQ(*Name1, *Name);
+ }
}
} // namespace
>From 5b514e940f9343ad4ade643f6f28496c126aca8c Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 5 Dec 2025 14:25:51 -0800
Subject: [PATCH 17/29] [clang][ssaf][NFC] Add new testcases for function
parameter entity name
---
clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 13903201b659a..33f359633f908 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -299,14 +299,16 @@ TEST(ASTEntityMappingTest, RecordRedeclaration) {
TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
auto AST = tooling::buildASTFromCode(R"(
+ void foo(int);
void foo(int x);
+ void foo(int y);
void foo(int x) {}
)");
auto &Ctx = AST->getASTContext();
auto Matcher = functionDecl(hasName("foo")).bind("decl");
auto Matches = match(Matcher, Ctx);
- ASSERT_EQ(Matches.size(), 2u);
+ ASSERT_GE(Matches.size(), 2u);
const auto *FirstFuncDecl = Matches[0].getNodeAs<FunctionDecl>("decl");
ASSERT_NE(FirstFuncDecl, nullptr);
>From ba89f060bf184582bd95aac636f076d7e5993506 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 5 Dec 2025 14:28:36 -0800
Subject: [PATCH 18/29] [clang][ssaf][NFC] Sort test source files in
CMakeLists.txt
---
clang/unittests/Analysis/Scalable/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 95aaa2aea253c..e545e314b49ac 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -1,7 +1,7 @@
add_distinct_clang_unittest(ClangScalableAnalysisFrameworkTests
+ ASTEntityMappingTest.cpp
BuildNamespaceTest.cpp
EntityNameTest.cpp
- ASTEntityMappingTest.cpp
CLANG_LIBS
clangAnalysisScalable
>From 8bde909fdb85b9e212995a0ea03f1e7e846a5c88 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 5 Dec 2025 16:42:14 -0800
Subject: [PATCH 19/29] [clang][ssaf] Add doc comments for BuildNamespace
---
.../Analysis/Scalable/Model/BuildNamespace.h | 30 +++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 6311d46564985..8f002ba2af2b3 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -5,6 +5,15 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+//
+// This file defines BuildNamespace and NestedBuildNamespace classes that
+// represent build namespaces in the Scalable Static Analysis Framework.
+//
+// Build namespaces provide an abstraction for grouping program entities (such
+// as those in a shared library or compilation unit) to enable analysis of
+// software projects constructed from individual components.
+//
+//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_BUILDNAMESPACE_H
@@ -26,7 +35,16 @@ llvm::StringRef toString(BuildNamespaceKind BNK);
std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str);
-/// Represents a single step in the build process.
+/// Represents a single namespace in the build process.
+///
+/// A BuildNamespace groups program entities, such as those belonging to a
+/// compilation unit or link unit (e.g., a shared library). Each namespace has a
+/// kind (CompilationUnit or LinkUnit) and a unique identifier name within that
+/// kind.
+///
+/// BuildNamespaces can be composed into NestedBuildNamespace to represent
+/// hierarchical namespace structures that model how software is constructed from
+/// its components.
class BuildNamespace {
BuildNamespaceKind Kind;
std::string Name;
@@ -46,7 +64,15 @@ class BuildNamespace {
friend class SerializationFormat;
};
-/// Represents a sequence of steps in the build process.
+/// Represents a hierarchical sequence of build namespaces.
+///
+/// A NestedBuildNamespace captures namespace qualification for program entities
+/// by maintaining an ordered sequence of BuildNamespace steps. This models how
+/// entities are organized through multiple steps of the build process, such as
+/// first being part of a compilation unit, then incorporated into a link unit.
+///
+/// For example, an entity might be qualified by a compilation unit namespace
+/// followed by a shared library namespace.
class NestedBuildNamespace {
friend class SerializationFormat;
>From 4cab5a1ac0551fb882ee788f704bf1f878d5767e Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 10:09:56 -0800
Subject: [PATCH 20/29] [clang][ssaf][NFC] Improve doc for
getLocalEntityNameForFunctionReturn
---
clang/include/clang/Analysis/Scalable/ASTEntityMapping.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
index 9a2c01573c4ed..9e17bf9dfb26a 100644
--- a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -32,11 +32,12 @@ namespace clang::ssaf {
/// \return An EntityName if the declaration can be mapped, std::nullopt otherwise.
std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D);
-/// Maps a function return type to an EntityName.
+/// Maps return entity of a function to an EntityName.
+/// The returned name uniquely identifies the return value of function \param FD.
///
/// \param FD The function declaration. Must not be null.
///
-/// \return An EntityName for the function's return type.
+/// \return An EntityName for the function's return entity.
std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD);
} // namespace clang::ssaf
>From 30ac699d8967d9c30d36e633c9344c622d0a2965 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 11:41:07 -0800
Subject: [PATCH 21/29] [clang][ssaf] Shorten names of EntityName factory
functions
---
.../Analysis/Scalable/ASTEntityMapping.h | 4 +-
.../Analysis/Scalable/ASTEntityMapping.cpp | 4 +-
.../Scalable/ASTEntityMappingTest.cpp | 48 +++++++++----------
3 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
index 9e17bf9dfb26a..9569ffc4ebb7c 100644
--- a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -30,7 +30,7 @@ namespace clang::ssaf {
/// \param D The declaration to map. Must not be null.
///
/// \return An EntityName if the declaration can be mapped, std::nullopt otherwise.
-std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D);
+std::optional<EntityName> getEntityName(const Decl* D);
/// Maps return entity of a function to an EntityName.
/// The returned name uniquely identifies the return value of function \param FD.
@@ -38,7 +38,7 @@ std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D);
/// \param FD The function declaration. Must not be null.
///
/// \return An EntityName for the function's return entity.
-std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD);
+std::optional<EntityName> getEntityNameForReturn(const FunctionDecl* FD);
} // namespace clang::ssaf
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
index 35ff8fa167d58..799bca8ab317b 100644
--- a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -18,7 +18,7 @@
namespace clang::ssaf {
-std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D) {
+std::optional<EntityName> getEntityName(const Decl* D) {
if (!D)
return std::nullopt;
@@ -58,7 +58,7 @@ std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D) {
return EntityName(USRBuf.str(), Suffix, {});
}
-std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD) {
+std::optional<EntityName> getEntityNameForReturn(const FunctionDecl* FD) {
if (!FD)
return std::nullopt;
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 33f359633f908..76a126106d239 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -39,7 +39,7 @@ TEST(ASTEntityMappingTest, FunctionDecl) {
const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
ASSERT_NE(FD, nullptr);
- auto EntityName = getLocalEntityNameForDecl(FD);
+ auto EntityName = getEntityName(FD);
EXPECT_TRUE(EntityName.has_value());
}
@@ -50,7 +50,7 @@ TEST(ASTEntityMappingTest, VarDecl) {
const auto *VD = findDecl<VarDecl>(Ctx, "x");
ASSERT_NE(VD, nullptr);
- auto EntityName = getLocalEntityNameForDecl(VD);
+ auto EntityName = getEntityName(VD);
EXPECT_TRUE(EntityName.has_value());
}
@@ -65,7 +65,7 @@ TEST(ASTEntityMappingTest, ParmVarDecl) {
const auto *PVD = FD->getParamDecl(0);
ASSERT_NE(PVD, nullptr);
- auto EntityName = getLocalEntityNameForDecl(PVD);
+ auto EntityName = getEntityName(PVD);
EXPECT_TRUE(EntityName.has_value());
}
@@ -76,7 +76,7 @@ TEST(ASTEntityMappingTest, RecordDecl) {
const auto *RD = findDecl<RecordDecl>(Ctx, "S");
ASSERT_NE(RD, nullptr);
- auto EntityName = getLocalEntityNameForDecl(RD);
+ auto EntityName = getEntityName(RD);
EXPECT_TRUE(EntityName.has_value());
}
@@ -87,12 +87,12 @@ TEST(ASTEntityMappingTest, FieldDecl) {
const auto *FD = findDecl<FieldDecl>(Ctx, "field");
ASSERT_NE(FD, nullptr);
- auto EntityName = getLocalEntityNameForDecl(FD);
+ auto EntityName = getEntityName(FD);
EXPECT_TRUE(EntityName.has_value());
}
TEST(ASTEntityMappingTest, NullDecl) {
- auto EntityName = getLocalEntityNameForDecl(nullptr);
+ auto EntityName = getEntityName(nullptr);
EXPECT_FALSE(EntityName.has_value());
}
@@ -116,7 +116,7 @@ TEST(ASTEntityMappingTest, ImplicitDecl) {
}
}
- auto EntityName = getLocalEntityNameForDecl(ImplCtor);
+ auto EntityName = getEntityName(ImplCtor);
EXPECT_FALSE(EntityName.has_value());
}
@@ -140,7 +140,7 @@ TEST(ASTEntityMappingTest, BuiltinFunction) {
ASSERT_NE(Callee, nullptr);
ASSERT_NE(Callee->getBuiltinID(), 0u /* not a built-in */);
- auto EntityName = getLocalEntityNameForDecl(Callee);
+ auto EntityName = getEntityName(Callee);
EXPECT_FALSE(EntityName.has_value());
}
@@ -151,7 +151,7 @@ TEST(ASTEntityMappingTest, UnsupportedDecl) {
const auto *ND = findDecl<NamespaceDecl>(Ctx, "N");
ASSERT_NE(ND, nullptr);
- auto EntityName = getLocalEntityNameForDecl(ND);
+ auto EntityName = getEntityName(ND);
EXPECT_FALSE(EntityName.has_value());
}
@@ -162,12 +162,12 @@ TEST(ASTEntityMappingTest, FunctionReturn) {
const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
ASSERT_NE(FD, nullptr);
- auto EntityName = getLocalEntityNameForFunctionReturn(FD);
+ auto EntityName = getEntityNameForReturn(FD);
EXPECT_TRUE(EntityName.has_value());
}
TEST(ASTEntityMappingTest, FunctionReturnNull) {
- auto EntityName = getLocalEntityNameForFunctionReturn(nullptr);
+ auto EntityName = getEntityNameForReturn(nullptr);
EXPECT_FALSE(EntityName.has_value());
}
@@ -189,7 +189,7 @@ TEST(ASTEntityMappingTest, FunctionReturnBuiltin) {
const auto *Callee = CE->getDirectCallee();
if (Callee && Callee->getBuiltinID()) {
- auto EntityName = getLocalEntityNameForFunctionReturn(Callee);
+ auto EntityName = getEntityNameForReturn(Callee);
EXPECT_FALSE(EntityName.has_value());
}
}
@@ -206,8 +206,8 @@ TEST(ASTEntityMappingTest, DifferentFunctionsDifferentNames) {
ASSERT_NE(Foo, nullptr);
ASSERT_NE(Bar, nullptr);
- auto FooName = getLocalEntityNameForDecl(Foo);
- auto BarName = getLocalEntityNameForDecl(Bar);
+ auto FooName = getEntityName(Foo);
+ auto BarName = getEntityName(Bar);
ASSERT_TRUE(FooName.has_value());
ASSERT_TRUE(BarName.has_value());
@@ -230,14 +230,14 @@ TEST(ASTEntityMappingTest, FunctionRedeclaration) {
const auto *FirstDecl = Matches[0].getNodeAs<FunctionDecl>("decl");
ASSERT_NE(FirstDecl, nullptr);
- auto FirstName = getLocalEntityNameForDecl(FirstDecl);
+ auto FirstName = getEntityName(FirstDecl);
ASSERT_TRUE(FirstName.has_value());
for (size_t I = 1; I < Matches.size(); ++I) {
const auto *Decl = Matches[I].getNodeAs<FunctionDecl>("decl");
ASSERT_NE(Decl, nullptr);
- auto Name = getLocalEntityNameForDecl(Decl);
+ auto Name = getEntityName(Decl);
ASSERT_TRUE(Name.has_value());
EXPECT_EQ(*FirstName, *Name);
}
@@ -257,14 +257,14 @@ TEST(ASTEntityMappingTest, VarRedeclaration) {
const auto *FirstDecl = Matches[0].getNodeAs<VarDecl>("decl");
ASSERT_NE(FirstDecl, nullptr);
- auto FirstName = getLocalEntityNameForDecl(FirstDecl);
+ auto FirstName = getEntityName(FirstDecl);
ASSERT_TRUE(FirstName.has_value());
for (size_t I = 1; I < Matches.size(); ++I) {
const auto *Decl = Matches[I].getNodeAs<VarDecl>("decl");
ASSERT_NE(Decl, nullptr);
- auto Name = getLocalEntityNameForDecl(Decl);
+ auto Name = getEntityName(Decl);
ASSERT_TRUE(Name.has_value());
EXPECT_EQ(*FirstName, *Name);
}
@@ -284,14 +284,14 @@ TEST(ASTEntityMappingTest, RecordRedeclaration) {
const auto *FirstDecl = Matches[0].getNodeAs<RecordDecl>("decl");
ASSERT_NE(FirstDecl, nullptr);
- auto FirstName = getLocalEntityNameForDecl(FirstDecl);
+ auto FirstName = getEntityName(FirstDecl);
ASSERT_TRUE(FirstName.has_value());
for (size_t I = 1; I < Matches.size(); ++I) {
const auto *Decl = Matches[I].getNodeAs<RecordDecl>("decl");
ASSERT_NE(Decl, nullptr);
- auto Name = getLocalEntityNameForDecl(Decl);
+ auto Name = getEntityName(Decl);
ASSERT_TRUE(Name.has_value());
EXPECT_EQ(*FirstName, *Name);
}
@@ -314,7 +314,7 @@ TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
ASSERT_NE(FirstFuncDecl, nullptr);
ASSERT_GT(FirstFuncDecl->param_size(), 0u);
- auto ParamEName = getLocalEntityNameForDecl(FirstFuncDecl->getParamDecl(0));
+ auto ParamEName = getEntityName(FirstFuncDecl->getParamDecl(0));
ASSERT_TRUE(ParamEName.has_value());
for (size_t I = 1; I < Matches.size(); ++I) {
@@ -322,7 +322,7 @@ TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
ASSERT_NE(FDecl, nullptr);
ASSERT_GT(FDecl->param_size(), 0u);
- auto ParamRedeclEName = getLocalEntityNameForDecl(FDecl->getParamDecl(0));
+ auto ParamRedeclEName = getEntityName(FDecl->getParamDecl(0));
EXPECT_EQ(*ParamEName, *ParamRedeclEName);
}
}
@@ -340,14 +340,14 @@ TEST(ASTEntityMappingTest, FunctionReturnRedeclaration) {
const auto *Decl1 = Matches[0].getNodeAs<FunctionDecl>("decl");
ASSERT_NE(Decl1, nullptr);
- auto Name1 = getLocalEntityNameForFunctionReturn(Decl1);
+ auto Name1 = getEntityNameForReturn(Decl1);
ASSERT_TRUE(Name1.has_value());
for (size_t I = 1; I < Matches.size(); ++I) {
const auto *FDecl = Matches[I].getNodeAs<FunctionDecl>("decl");
ASSERT_NE(FDecl, nullptr);
- auto Name = getLocalEntityNameForFunctionReturn(Decl1);
+ auto Name = getEntityNameForReturn(Decl1);
ASSERT_TRUE(Name.has_value());
EXPECT_EQ(*Name1, *Name);
}
>From 97c31ee2378f3f4433c74d0bb699b4a5a99a976f Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 14:31:10 -0800
Subject: [PATCH 22/29] [clang][ssaf][NFC] Fix test for entity name of implicit
declaration
---
.../Scalable/ASTEntityMappingTest.cpp | 23 +++++++------------
1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index 76a126106d239..f0c76ec6e5027 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -96,27 +96,20 @@ TEST(ASTEntityMappingTest, NullDecl) {
EXPECT_FALSE(EntityName.has_value());
}
-TEST(ASTEntityMappingTest, ImplicitDecl) {
+TEST(ASTEntityMappingTest, ImplicitDeclLambda) {
auto AST = tooling::buildASTFromCode(R"(
- struct S {
- S() = default;
- };
+ auto L = [](){};
)", "test.cpp", std::make_shared<PCHContainerOperations>());
auto &Ctx = AST->getASTContext();
- const auto *RD = findDecl<CXXRecordDecl>(Ctx, "S");
- ASSERT_NE(RD, nullptr);
+ auto Matcher = cxxRecordDecl(isImplicit()).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_GT(Matches.size(), 0u);
- // Find the implicitly-declared copy constructor
- const CXXConstructorDecl * ImplCtor = nullptr;
- for (const auto *Ctor : RD->ctors()) {
- if (Ctor->isCopyConstructor() && Ctor->isImplicit()) {
- ImplCtor = Ctor;
- break;
- }
- }
+ const auto * ImplCXXRD = Matches[0].getNodeAs<CXXRecordDecl>("decl");
+ ASSERT_NE(ImplCXXRD, nullptr);
- auto EntityName = getEntityName(ImplCtor);
+ auto EntityName = getEntityName(ImplCXXRD);
EXPECT_FALSE(EntityName.has_value());
}
>From 140e84f11cf3fa6783036004250794fbb871f918 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 14:49:58 -0800
Subject: [PATCH 23/29] [clang][ssaf][NFC] Rename and document makeTU in
BuildNamespace
---
.../Analysis/Scalable/Model/BuildNamespace.h | 13 ++++++++++--
.../Scalable/Model/BuildNamespace.cpp | 6 +++---
.../Analysis/Scalable/BuildNamespaceTest.cpp | 20 +++++++++----------
.../Analysis/Scalable/EntityNameTest.cpp | 12 +++++------
4 files changed, 30 insertions(+), 21 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 8f002ba2af2b3..6c7d50392867f 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -55,7 +55,11 @@ class BuildNamespace {
BuildNamespace(BuildNamespaceKind Kind, llvm::StringRef Name)
: Kind(Kind), Name(Name.str()) {}
- static BuildNamespace makeTU(llvm::StringRef CompilationId);
+ /// Creates a BuildNamespace representing a compilation unit.
+ ///
+ /// \param CompilationId The unique identifier for the compilation unit.
+ /// \returns A BuildNamespace with CompilationUnit kind.
+ static BuildNamespace makeCompilationUnit(llvm::StringRef CompilationId);
bool operator==(const BuildNamespace& Other) const;
bool operator!=(const BuildNamespace& Other) const;
@@ -88,7 +92,12 @@ class NestedBuildNamespace {
Namespaces.push_back(N);
}
- static NestedBuildNamespace makeTU(llvm::StringRef CompilationId);
+ /// Creates a NestedBuildNamespace representing a compilation unit.
+ ///
+ /// \param CompilationId The unique identifier for the compilation unit.
+ /// \returns A NestedBuildNamespace containing a single CompilationUnit
+ /// BuildNamespace.
+ static NestedBuildNamespace makeCompilationUnit(llvm::StringRef CompilationId);
/// Creates a new NestedBuildNamespace by appending additional namespace.
///
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
index 7676d56f867e3..9a0f60a6bd8db 100644
--- a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -28,7 +28,7 @@ std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str) {
return std::nullopt;
}
-BuildNamespace BuildNamespace::makeTU(llvm::StringRef CompilationId) {
+BuildNamespace BuildNamespace::makeCompilationUnit(llvm::StringRef CompilationId) {
return BuildNamespace{BuildNamespaceKind::CompilationUnit, CompilationId.str()};
}
@@ -44,9 +44,9 @@ bool BuildNamespace::operator<(const BuildNamespace& Other) const {
return asTuple() < Other.asTuple();
}
-NestedBuildNamespace NestedBuildNamespace::makeTU(llvm::StringRef CompilationId) {
+NestedBuildNamespace NestedBuildNamespace::makeCompilationUnit(llvm::StringRef CompilationId) {
NestedBuildNamespace Result;
- Result.Namespaces.push_back(BuildNamespace::makeTU(CompilationId));
+ Result.Namespaces.push_back(BuildNamespace::makeCompilationUnit(CompilationId));
return Result;
}
diff --git a/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp b/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
index e62e3257d5608..80d8a40738be3 100644
--- a/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
+++ b/clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp
@@ -13,9 +13,9 @@ namespace clang::ssaf {
namespace {
TEST(BuildNamespaceTest, Equality) {
- auto BN1 = BuildNamespace::makeTU("test.cpp");
- auto BN2 = BuildNamespace::makeTU("test.cpp");
- auto BN3 = BuildNamespace::makeTU("other.cpp");
+ auto BN1 = BuildNamespace::makeCompilationUnit("test.cpp");
+ auto BN2 = BuildNamespace::makeCompilationUnit("test.cpp");
+ auto BN3 = BuildNamespace::makeCompilationUnit("other.cpp");
EXPECT_EQ(BN1, BN2);
EXPECT_NE(BN1, BN3);
@@ -54,28 +54,28 @@ TEST(NestedBuildNamespaceTest, DefaultConstruction) {
}
TEST(NestedBuildNamespaceTest, SingleNamespaceConstruction) {
- auto BN = BuildNamespace::makeTU("test.cpp");
+ auto BN = BuildNamespace::makeCompilationUnit("test.cpp");
NestedBuildNamespace NBN(BN);
EXPECT_FALSE(NBN.empty());
}
TEST(NestedBuildNamespaceTest, MakeTU) {
- auto NBN = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN = NestedBuildNamespace::makeCompilationUnit("test.cpp");
EXPECT_FALSE(NBN.empty());
}
TEST(NestedBuildNamespaceTest, Equality) {
- auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
- auto NBN2 = NestedBuildNamespace::makeTU("test.cpp");
- auto NBN3 = NestedBuildNamespace::makeTU("other.cpp");
+ auto NBN1 = NestedBuildNamespace::makeCompilationUnit("test.cpp");
+ auto NBN2 = NestedBuildNamespace::makeCompilationUnit("test.cpp");
+ auto NBN3 = NestedBuildNamespace::makeCompilationUnit("other.cpp");
EXPECT_EQ(NBN1, NBN2);
EXPECT_NE(NBN1, NBN3);
}
TEST(NestedBuildNamespaceTest, MakeQualified) {
- auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN1 = NestedBuildNamespace::makeCompilationUnit("test.cpp");
BuildNamespace LinkNS(BuildNamespaceKind::LinkUnit, "app");
NestedBuildNamespace NBN2(LinkNS);
@@ -87,7 +87,7 @@ TEST(NestedBuildNamespaceTest, MakeQualified) {
TEST(NestedBuildNamespaceTest, EmptyQualified) {
NestedBuildNamespace Empty;
- auto NBN = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN = NestedBuildNamespace::makeCompilationUnit("test.cpp");
auto Qualified = Empty.makeQualified(NBN);
EXPECT_EQ(Qualified, NBN);
diff --git a/clang/unittests/Analysis/Scalable/EntityNameTest.cpp b/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
index 0c4ef41a8bf60..be0c2a9d52132 100644
--- a/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
+++ b/clang/unittests/Analysis/Scalable/EntityNameTest.cpp
@@ -14,8 +14,8 @@ namespace clang::ssaf {
namespace {
TEST(EntityNameTest, Equality) {
- auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
- auto NBN2 = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN1 = NestedBuildNamespace::makeCompilationUnit("test.cpp");
+ auto NBN2 = NestedBuildNamespace::makeCompilationUnit("test.cpp");
EntityName EN1("c:@F at foo", "", NBN1);
EntityName EN2("c:@F at foo", "", NBN2);
@@ -26,7 +26,7 @@ TEST(EntityNameTest, Equality) {
}
TEST(EntityNameTest, EqualityWithDifferentSuffix) {
- auto NBN = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN = NestedBuildNamespace::makeCompilationUnit("test.cpp");
EntityName EN1("c:@F at foo", "1", NBN);
EntityName EN2("c:@F at foo", "2", NBN);
@@ -35,8 +35,8 @@ TEST(EntityNameTest, EqualityWithDifferentSuffix) {
}
TEST(EntityNameTest, EqualityWithDifferentNamespace) {
- auto NBN1 = NestedBuildNamespace::makeTU("test1.cpp");
- auto NBN2 = NestedBuildNamespace::makeTU("test2.cpp");
+ auto NBN1 = NestedBuildNamespace::makeCompilationUnit("test1.cpp");
+ auto NBN2 = NestedBuildNamespace::makeCompilationUnit("test2.cpp");
EntityName EN1("c:@F at foo", "", NBN1);
EntityName EN2("c:@F at foo", "", NBN2);
@@ -45,7 +45,7 @@ TEST(EntityNameTest, EqualityWithDifferentNamespace) {
}
TEST(EntityNameTest, MakeQualified) {
- auto NBN1 = NestedBuildNamespace::makeTU("test.cpp");
+ auto NBN1 = NestedBuildNamespace::makeCompilationUnit("test.cpp");
EntityName EN("c:@F at foo", "", NBN1);
BuildNamespace LinkNS(BuildNamespaceKind::LinkUnit, "app");
>From a6807eef7103b534c02e5798d9dccde277bd900c Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 15:17:48 -0800
Subject: [PATCH 24/29] [clang][ssaf][NFC] Add comment on implementation of
EntityName being opaque
---
.../clang/Analysis/Scalable/Model/EntityName.h | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index ea26d09f3e250..2d5f93e0d3efc 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -15,12 +15,15 @@
#include <string>
namespace clang::ssaf {
-
/// Uniquely identifies an entity in a program.
///
-/// EntityName provides a globally unique identifier for program entities that remains
-/// stable across compilation boundaries. This enables whole-program analysis to track
-/// and relate entities across separately compiled translation units.
+/// EntityName provides a globally unique identifier for program entities that
+/// remains stable across compilation boundaries. This enables whole-program
+/// analysis to track and relate entities across separately compiled translation
+/// units.
+///
+/// Client code should not make assumptions about the implementation details,
+/// such as USRs.
class EntityName {
std::string USR;
llvm::SmallString<16> Suffix;
@@ -29,6 +32,9 @@ class EntityName {
auto asTuple() const { return std::tie(USR, Suffix, Namespace); }
public:
+ /// Client code should not use this constructor directly.
+ /// Use getEntityName and other functions in ASTEntityMapping.h to get
+ /// entity names.
EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
NestedBuildNamespace Namespace);
>From 2fb3d3775f07b6061cc36b7e4b40a04fdc380469 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 15:18:36 -0800
Subject: [PATCH 25/29] [clang][ssaf][NFC] Fix header comment
---
clang/include/clang/Analysis/Scalable/ASTEntityMapping.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
index 9569ffc4ebb7c..1182bad5340ce 100644
--- a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -1,4 +1,4 @@
-//===- ASTMapping.h - AST to SSAF Entity mapping ----------------*- C++ -*-===//
+//===- ASTEntityMapping.h - AST to SSAF Entity mapping ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From db34258f6dbf547af5f2fafcfe611137fdc2c934 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 15:19:15 -0800
Subject: [PATCH 26/29] [clang][ssaf] Remove braces from single-line if block
---
clang/lib/Analysis/Scalable/ASTEntityMapping.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
index 799bca8ab317b..10e61348a3a47 100644
--- a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -48,9 +48,8 @@ std::optional<EntityName> getEntityName(const Decl* D) {
}
llvm::SmallString<128> USRBuf;
- if (clang::index::generateUSRForDecl(USRDecl, USRBuf)) {
+ if (clang::index::generateUSRForDecl(USRDecl, USRBuf))
return std::nullopt;
- }
if (USRBuf.empty())
return std::nullopt;
>From 691482cd630e358e6a4fd98f0bacb5bb11ff9f49 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 15:28:06 -0800
Subject: [PATCH 27/29] [clang][ssaf] Apply clang-format
---
.../Analysis/Scalable/ASTEntityMapping.h | 12 ++++---
.../Analysis/Scalable/Model/BuildNamespace.h | 35 +++++++++---------
.../Analysis/Scalable/Model/EntityName.h | 6 ++--
.../Analysis/Scalable/ASTEntityMapping.cpp | 12 ++++---
.../Scalable/Model/BuildNamespace.cpp | 36 ++++++++++---------
.../Analysis/Scalable/Model/EntityName.cpp | 6 ++--
.../Scalable/ASTEntityMappingTest.cpp | 2 +-
7 files changed, 58 insertions(+), 51 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
index 1182bad5340ce..0e5fc204c5f1b 100644
--- a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -9,8 +9,8 @@
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ASTENTITYMAPPING_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_ASTENTITYMAPPING_H
-#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/AST/Decl.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "llvm/ADT/StringRef.h"
#include <optional>
@@ -29,16 +29,18 @@ namespace clang::ssaf {
///
/// \param D The declaration to map. Must not be null.
///
-/// \return An EntityName if the declaration can be mapped, std::nullopt otherwise.
-std::optional<EntityName> getEntityName(const Decl* D);
+/// \return An EntityName if the declaration can be mapped, std::nullopt
+/// otherwise.
+std::optional<EntityName> getEntityName(const Decl *D);
/// Maps return entity of a function to an EntityName.
-/// The returned name uniquely identifies the return value of function \param FD.
+/// The returned name uniquely identifies the return value of function \param
+/// FD.
///
/// \param FD The function declaration. Must not be null.
///
/// \return An EntityName for the function's return entity.
-std::optional<EntityName> getEntityNameForReturn(const FunctionDecl* FD);
+std::optional<EntityName> getEntityNameForReturn(const FunctionDecl *FD);
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 6c7d50392867f..5ca26df1e9252 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -26,10 +26,7 @@
namespace clang::ssaf {
-enum class BuildNamespaceKind : unsigned short {
- CompilationUnit,
- LinkUnit
-};
+enum class BuildNamespaceKind : unsigned short { CompilationUnit, LinkUnit };
llvm::StringRef toString(BuildNamespaceKind BNK);
@@ -43,8 +40,8 @@ std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str);
/// kind.
///
/// BuildNamespaces can be composed into NestedBuildNamespace to represent
-/// hierarchical namespace structures that model how software is constructed from
-/// its components.
+/// hierarchical namespace structures that model how software is constructed
+/// from its components.
class BuildNamespace {
BuildNamespaceKind Kind;
std::string Name;
@@ -53,7 +50,7 @@ class BuildNamespace {
public:
BuildNamespace(BuildNamespaceKind Kind, llvm::StringRef Name)
- : Kind(Kind), Name(Name.str()) {}
+ : Kind(Kind), Name(Name.str()) {}
/// Creates a BuildNamespace representing a compilation unit.
///
@@ -61,9 +58,9 @@ class BuildNamespace {
/// \returns A BuildNamespace with CompilationUnit kind.
static BuildNamespace makeCompilationUnit(llvm::StringRef CompilationId);
- bool operator==(const BuildNamespace& Other) const;
- bool operator!=(const BuildNamespace& Other) const;
- bool operator<(const BuildNamespace& Other) const;
+ bool operator==(const BuildNamespace &Other) const;
+ bool operator!=(const BuildNamespace &Other) const;
+ bool operator<(const BuildNamespace &Other) const;
friend class SerializationFormat;
};
@@ -85,10 +82,10 @@ class NestedBuildNamespace {
public:
NestedBuildNamespace() = default;
- explicit NestedBuildNamespace(const std::vector<BuildNamespace>& Namespaces)
- : Namespaces(Namespaces) {}
+ explicit NestedBuildNamespace(const std::vector<BuildNamespace> &Namespaces)
+ : Namespaces(Namespaces) {}
- explicit NestedBuildNamespace(const BuildNamespace& N) {
+ explicit NestedBuildNamespace(const BuildNamespace &N) {
Namespaces.push_back(N);
}
@@ -97,23 +94,25 @@ class NestedBuildNamespace {
/// \param CompilationId The unique identifier for the compilation unit.
/// \returns A NestedBuildNamespace containing a single CompilationUnit
/// BuildNamespace.
- static NestedBuildNamespace makeCompilationUnit(llvm::StringRef CompilationId);
+ static NestedBuildNamespace
+ makeCompilationUnit(llvm::StringRef CompilationId);
/// Creates a new NestedBuildNamespace by appending additional namespace.
///
/// \param Namespace The namespace to append.
NestedBuildNamespace makeQualified(NestedBuildNamespace Namespace) const {
auto Copy = *this;
- Copy.Namespaces.reserve(Copy.Namespaces.size() + Namespace.Namespaces.size());
+ Copy.Namespaces.reserve(Copy.Namespaces.size() +
+ Namespace.Namespaces.size());
llvm::append_range(Copy.Namespaces, Namespace.Namespaces);
return Copy;
}
bool empty() const;
- bool operator==(const NestedBuildNamespace& Other) const;
- bool operator!=(const NestedBuildNamespace& Other) const;
- bool operator<(const NestedBuildNamespace& Other) const;
+ bool operator==(const NestedBuildNamespace &Other) const;
+ bool operator!=(const NestedBuildNamespace &Other) const;
+ bool operator<(const NestedBuildNamespace &Other) const;
friend class JSONWriter;
friend class LinkUnitResolution;
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 2d5f93e0d3efc..23890ab7bea43 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -38,9 +38,9 @@ class EntityName {
EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
NestedBuildNamespace Namespace);
- bool operator==(const EntityName& Other) const;
- bool operator!=(const EntityName& Other) const;
- bool operator<(const EntityName& Other) const;
+ bool operator==(const EntityName &Other) const;
+ bool operator!=(const EntityName &Other) const;
+ bool operator<(const EntityName &Other) const;
/// Creates a new EntityName with additional build namespace qualification.
///
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
index 10e61348a3a47..0a25e75e01631 100644
--- a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -18,7 +18,7 @@
namespace clang::ssaf {
-std::optional<EntityName> getEntityName(const Decl* D) {
+std::optional<EntityName> getEntityName(const Decl *D) {
if (!D)
return std::nullopt;
@@ -34,9 +34,11 @@ std::optional<EntityName> getEntityName(const Decl* D) {
llvm::SmallString<16> Suffix;
const Decl *USRDecl = D;
- // For parameters, use the parent function's USR with parameter index as suffix
- if (const auto * PVD = dyn_cast<ParmVarDecl>(D)) {
- const auto *FD = dyn_cast_or_null<FunctionDecl>(PVD->getParentFunctionOrMethod());
+ // For parameters, use the parent function's USR with parameter index as
+ // suffix
+ if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
+ const auto *FD =
+ dyn_cast_or_null<FunctionDecl>(PVD->getParentFunctionOrMethod());
if (!FD)
return std::nullopt;
USRDecl = FD;
@@ -57,7 +59,7 @@ std::optional<EntityName> getEntityName(const Decl* D) {
return EntityName(USRBuf.str(), Suffix, {});
}
-std::optional<EntityName> getEntityNameForReturn(const FunctionDecl* FD) {
+std::optional<EntityName> getEntityNameForReturn(const FunctionDecl *FD) {
if (!FD)
return std::nullopt;
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
index 9a0f60a6bd8db..040cfe9926be2 100644
--- a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -13,9 +13,11 @@
namespace clang::ssaf {
llvm::StringRef toString(BuildNamespaceKind BNK) {
- switch(BNK) {
- case BuildNamespaceKind::CompilationUnit: return "compilation_unit";
- case BuildNamespaceKind::LinkUnit: return "link_unit";
+ switch (BNK) {
+ case BuildNamespaceKind::CompilationUnit:
+ return "compilation_unit";
+ case BuildNamespaceKind::LinkUnit:
+ return "link_unit";
}
llvm_unreachable("Unknown BuildNamespaceKind");
}
@@ -28,41 +30,43 @@ std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str) {
return std::nullopt;
}
-BuildNamespace BuildNamespace::makeCompilationUnit(llvm::StringRef CompilationId) {
- return BuildNamespace{BuildNamespaceKind::CompilationUnit, CompilationId.str()};
+BuildNamespace
+BuildNamespace::makeCompilationUnit(llvm::StringRef CompilationId) {
+ return BuildNamespace{BuildNamespaceKind::CompilationUnit,
+ CompilationId.str()};
}
-bool BuildNamespace::operator==(const BuildNamespace& Other) const {
+bool BuildNamespace::operator==(const BuildNamespace &Other) const {
return asTuple() == Other.asTuple();
}
-bool BuildNamespace::operator!=(const BuildNamespace& Other) const {
+bool BuildNamespace::operator!=(const BuildNamespace &Other) const {
return !(*this == Other);
}
-bool BuildNamespace::operator<(const BuildNamespace& Other) const {
+bool BuildNamespace::operator<(const BuildNamespace &Other) const {
return asTuple() < Other.asTuple();
}
-NestedBuildNamespace NestedBuildNamespace::makeCompilationUnit(llvm::StringRef CompilationId) {
+NestedBuildNamespace
+NestedBuildNamespace::makeCompilationUnit(llvm::StringRef CompilationId) {
NestedBuildNamespace Result;
- Result.Namespaces.push_back(BuildNamespace::makeCompilationUnit(CompilationId));
+ Result.Namespaces.push_back(
+ BuildNamespace::makeCompilationUnit(CompilationId));
return Result;
}
-bool NestedBuildNamespace::empty() const {
- return Namespaces.empty();
-}
+bool NestedBuildNamespace::empty() const { return Namespaces.empty(); }
-bool NestedBuildNamespace::operator==(const NestedBuildNamespace& Other) const {
+bool NestedBuildNamespace::operator==(const NestedBuildNamespace &Other) const {
return Namespaces == Other.Namespaces;
}
-bool NestedBuildNamespace::operator!=(const NestedBuildNamespace& Other) const {
+bool NestedBuildNamespace::operator!=(const NestedBuildNamespace &Other) const {
return !(*this == Other);
}
-bool NestedBuildNamespace::operator<(const NestedBuildNamespace& Other) const {
+bool NestedBuildNamespace::operator<(const NestedBuildNamespace &Other) const {
return Namespaces < Other.Namespaces;
}
diff --git a/clang/lib/Analysis/Scalable/Model/EntityName.cpp b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
index b8c1ba8a052ca..7e66476d6ce0e 100644
--- a/clang/lib/Analysis/Scalable/Model/EntityName.cpp
+++ b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
@@ -12,13 +12,13 @@ namespace clang::ssaf {
EntityName::EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
NestedBuildNamespace Namespace)
- : USR(USR.str()), Suffix(Suffix), Namespace(std::move(Namespace)) {}
+ : USR(USR.str()), Suffix(Suffix), Namespace(std::move(Namespace)) {}
-bool EntityName::operator==(const EntityName& Other) const {
+bool EntityName::operator==(const EntityName &Other) const {
return asTuple() == Other.asTuple();
}
-bool EntityName::operator!=(const EntityName& Other) const {
+bool EntityName::operator!=(const EntityName &Other) const {
return !(*this == Other);
}
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index f0c76ec6e5027..bbf7101c8afde 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -106,7 +106,7 @@ TEST(ASTEntityMappingTest, ImplicitDeclLambda) {
auto Matches = match(Matcher, Ctx);
ASSERT_GT(Matches.size(), 0u);
- const auto * ImplCXXRD = Matches[0].getNodeAs<CXXRecordDecl>("decl");
+ const auto *ImplCXXRD = Matches[0].getNodeAs<CXXRecordDecl>("decl");
ASSERT_NE(ImplCXXRD, nullptr);
auto EntityName = getEntityName(ImplCXXRD);
>From 0bf64a424b4b9ecd9d6e172b98597dd59eda2b65 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 15:44:00 -0800
Subject: [PATCH 28/29] [clang][ssaf][NFC] Label C++ snippets in unit tests
---
.../Scalable/ASTEntityMappingTest.cpp | 52 ++++++++++---------
1 file changed, 27 insertions(+), 25 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index bbf7101c8afde..a18992ec7bde9 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -33,7 +33,7 @@ const DeclType *findDecl(ASTContext &Ctx, StringRef Name) {
}
TEST(ASTEntityMappingTest, FunctionDecl) {
- auto AST = tooling::buildASTFromCode("void foo() {}");
+ auto AST = tooling::buildASTFromCode(R"cpp(void foo() {})cpp");
auto &Ctx = AST->getASTContext();
const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
@@ -44,7 +44,7 @@ TEST(ASTEntityMappingTest, FunctionDecl) {
}
TEST(ASTEntityMappingTest, VarDecl) {
- auto AST = tooling::buildASTFromCode("int x = 42;");
+ auto AST = tooling::buildASTFromCode(R"cpp(int x = 42;)cpp");
auto &Ctx = AST->getASTContext();
const auto *VD = findDecl<VarDecl>(Ctx, "x");
@@ -55,7 +55,7 @@ TEST(ASTEntityMappingTest, VarDecl) {
}
TEST(ASTEntityMappingTest, ParmVarDecl) {
- auto AST = tooling::buildASTFromCode("void foo(int x) {}");
+ auto AST = tooling::buildASTFromCode(R"cpp(void foo(int x) {})cpp");
auto &Ctx = AST->getASTContext();
const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
@@ -70,7 +70,7 @@ TEST(ASTEntityMappingTest, ParmVarDecl) {
}
TEST(ASTEntityMappingTest, RecordDecl) {
- auto AST = tooling::buildASTFromCode("struct S {};");
+ auto AST = tooling::buildASTFromCode(R"cpp(struct S {};)cpp");
auto &Ctx = AST->getASTContext();
const auto *RD = findDecl<RecordDecl>(Ctx, "S");
@@ -81,7 +81,7 @@ TEST(ASTEntityMappingTest, RecordDecl) {
}
TEST(ASTEntityMappingTest, FieldDecl) {
- auto AST = tooling::buildASTFromCode("struct S { int field; };");
+ auto AST = tooling::buildASTFromCode(R"cpp(struct S { int field; };)cpp");
auto &Ctx = AST->getASTContext();
const auto *FD = findDecl<FieldDecl>(Ctx, "field");
@@ -97,9 +97,11 @@ TEST(ASTEntityMappingTest, NullDecl) {
}
TEST(ASTEntityMappingTest, ImplicitDeclLambda) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(
+ R"cpp(
auto L = [](){};
- )", "test.cpp", std::make_shared<PCHContainerOperations>());
+ )cpp",
+ "test.cpp", std::make_shared<PCHContainerOperations>());
auto &Ctx = AST->getASTContext();
auto Matcher = cxxRecordDecl(isImplicit()).bind("decl");
@@ -114,11 +116,11 @@ TEST(ASTEntityMappingTest, ImplicitDeclLambda) {
}
TEST(ASTEntityMappingTest, BuiltinFunction) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
void test() {
__builtin_memcpy(0, 0, 0);
}
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
// Find the builtin call
@@ -138,7 +140,7 @@ TEST(ASTEntityMappingTest, BuiltinFunction) {
}
TEST(ASTEntityMappingTest, UnsupportedDecl) {
- auto AST = tooling::buildASTFromCode("namespace N {}");
+ auto AST = tooling::buildASTFromCode(R"cpp(namespace N {})cpp");
auto &Ctx = AST->getASTContext();
const auto *ND = findDecl<NamespaceDecl>(Ctx, "N");
@@ -149,7 +151,7 @@ TEST(ASTEntityMappingTest, UnsupportedDecl) {
}
TEST(ASTEntityMappingTest, FunctionReturn) {
- auto AST = tooling::buildASTFromCode("int foo() { return 42; }");
+ auto AST = tooling::buildASTFromCode(R"cpp(int foo() { return 42; })cpp");
auto &Ctx = AST->getASTContext();
const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
@@ -165,11 +167,11 @@ TEST(ASTEntityMappingTest, FunctionReturnNull) {
}
TEST(ASTEntityMappingTest, FunctionReturnBuiltin) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
void test() {
__builtin_memcpy(0, 0, 0);
}
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
// Find the builtin call
@@ -188,10 +190,10 @@ TEST(ASTEntityMappingTest, FunctionReturnBuiltin) {
}
TEST(ASTEntityMappingTest, DifferentFunctionsDifferentNames) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
void foo() {}
void bar() {}
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
const auto *Foo = findDecl<FunctionDecl>(Ctx, "foo");
@@ -210,10 +212,10 @@ TEST(ASTEntityMappingTest, DifferentFunctionsDifferentNames) {
// Redeclaration tests
TEST(ASTEntityMappingTest, FunctionRedeclaration) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
void foo();
void foo() {}
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
auto Matcher = functionDecl(hasName("foo")).bind("decl");
@@ -237,10 +239,10 @@ TEST(ASTEntityMappingTest, FunctionRedeclaration) {
}
TEST(ASTEntityMappingTest, VarRedeclaration) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
extern int x;
int x = 42;
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
auto Matcher = varDecl(hasName("x")).bind("decl");
@@ -264,10 +266,10 @@ TEST(ASTEntityMappingTest, VarRedeclaration) {
}
TEST(ASTEntityMappingTest, RecordRedeclaration) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
struct S;
struct S {};
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
auto Matcher = recordDecl(hasName("S"), unless(isImplicit())).bind("decl");
@@ -291,12 +293,12 @@ TEST(ASTEntityMappingTest, RecordRedeclaration) {
}
TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
void foo(int);
void foo(int x);
void foo(int y);
void foo(int x) {}
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
auto Matcher = functionDecl(hasName("foo")).bind("decl");
@@ -321,10 +323,10 @@ TEST(ASTEntityMappingTest, ParmVarDeclRedeclaration) {
}
TEST(ASTEntityMappingTest, FunctionReturnRedeclaration) {
- auto AST = tooling::buildASTFromCode(R"(
+ auto AST = tooling::buildASTFromCode(R"cpp(
int foo();
int foo() { return 42; }
- )");
+ )cpp");
auto &Ctx = AST->getASTContext();
auto Matcher = functionDecl(hasName("foo")).bind("decl");
>From e8d88050e9a24f6b5940f7561b516f146b9979d1 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Mon, 8 Dec 2025 15:45:31 -0800
Subject: [PATCH 29/29] [clang][ssaf][NFC] clang-format tests
---
clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
index a18992ec7bde9..dd41864da3c55 100644
--- a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -98,7 +98,7 @@ TEST(ASTEntityMappingTest, NullDecl) {
TEST(ASTEntityMappingTest, ImplicitDeclLambda) {
auto AST = tooling::buildASTFromCode(
- R"cpp(
+ R"cpp(
auto L = [](){};
)cpp",
"test.cpp", std::make_shared<PCHContainerOperations>());
More information about the cfe-commits
mailing list