================
@@ -0,0 +1,357 @@
+//===- 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/AST/DeclCXX.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::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;
+  if (auto Result = Matches[0].getNodeAs<DeclType>("decl"))
+    return dyn_cast<DeclType>(Result->getCanonicalDecl());
+  return nullptr;
+}
+
+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_EQ(FD->param_size(), 1u);
+
+  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
+  const CXXConstructorDecl * ImplCtor = nullptr;
+  for (const auto *Ctor : RD->ctors()) {
+    if (Ctor->isCopyConstructor() && Ctor->isImplicit()) {
+      ImplCtor = Ctor;
+      break;
+    }
+  }
+
+  auto EntityName = getLocalEntityNameForDecl(ImplCtor);
+  EXPECT_FALSE(EntityName.has_value());
+}
+
+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_EQ(Matches.size(), 1ul);
+
+  const auto *CE = Matches[0].getNodeAs<CallExpr>("call");
+  ASSERT_NE(CE, nullptr);
+
+  const auto *Callee = CE->getDirectCallee();
+  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) {
+  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_GE(Matches.size(), 2u);
+
+  const auto *FirstDecl = Matches[0].getNodeAs<FunctionDecl>("decl");
+  ASSERT_NE(FirstDecl, nullptr);
+
+  auto FirstName = getLocalEntityNameForDecl(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);
+    ASSERT_TRUE(Name.has_value());
+    EXPECT_EQ(*FirstName, *Name);
+  }
+}
+
+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 *FirstDecl = Matches[0].getNodeAs<VarDecl>("decl");
+  ASSERT_NE(FirstDecl, nullptr);
+
+  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);
+
+    auto Name = getLocalEntityNameForDecl(Decl);
+    ASSERT_TRUE(Name.has_value());
+    EXPECT_EQ(*FirstName, *Name);
+  }
+}
+
+TEST(ASTEntityMappingTest, RecordRedeclaration) {
+  auto AST = tooling::buildASTFromCode(R"(
+    struct S;
+    struct S {};
+  )");
+  auto &Ctx = AST->getASTContext();
+
+  auto Matcher = recordDecl(hasName("S"), unless(isImplicit())).bind("decl");
+  auto Matches = match(Matcher, Ctx);
+  ASSERT_GE(Matches.size(), 2u);
+
+  const auto *FirstDecl = Matches[0].getNodeAs<RecordDecl>("decl");
+  ASSERT_NE(FirstDecl, nullptr);
+
+  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);
+
+    auto Name = getLocalEntityNameForDecl(Decl);
+    ASSERT_TRUE(Name.has_value());
+    EXPECT_EQ(*FirstName, *Name);
+  }
+}
+
+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_GE(Matches.size(), 2u);
+
+  const auto *FirstFuncDecl = Matches[0].getNodeAs<FunctionDecl>("decl");
+  ASSERT_NE(FirstFuncDecl, nullptr);
+  ASSERT_GT(FirstFuncDecl->param_size(), 0u);
+
+  auto ParamEName = getLocalEntityNameForDecl(FirstFuncDecl->getParamDecl(0));
+  ASSERT_TRUE(ParamEName.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);
+
+    auto ParamRedeclEName = getLocalEntityNameForDecl(FDecl->getParamDecl(0));
+    EXPECT_EQ(*ParamEName, *ParamRedeclEName);
+  }
+}
+
+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");
+  ASSERT_NE(Decl1, nullptr);
+  auto Name1 = getLocalEntityNameForFunctionReturn(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);
+    ASSERT_TRUE(Name.has_value());
+    EXPECT_EQ(*Name1, *Name);
+  }
----------------
steakhal wrote:

It's just a nit, but I figure the following would be more idiomatic:

```suggestion
  for (const auto &Match : llvm::drop_begin(Matches)) {
    const auto *FDecl = Match.getNodeAs<FunctionDecl>("decl");
    ASSERT_NE(FDecl, nullptr);

    auto Name = getLocalEntityNameForFunctionReturn(Decl1);
    ASSERT_TRUE(Name.has_value());
    EXPECT_EQ(*Name1, *Name);
  }
```

If you prefer, you could apply this to the rest of the 1-based loops in this 
file. I have no hard feelings though if not.

https://github.com/llvm/llvm-project/pull/169131
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to