================ @@ -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); ---------------- steakhal wrote:
Why don't we check exact parameter counts here with EQ? https://github.com/llvm/llvm-project/pull/169131 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
