| // Part of the Crubit project, under the Apache License v2.0 with LLVM | |
| // Exceptions. See /LICENSE for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| #include "lifetime_analysis/points_to_map.h" | |
| #include <optional> | |
| #include "gtest/gtest.h" | |
| #include "lifetime_analysis/object.h" | |
| #include "lifetime_analysis/object_set.h" | |
| #include "lifetime_annotations/lifetime.h" | |
| #include "lifetime_annotations/lifetime_annotations.h" | |
| #include "lifetime_annotations/test/run_on_code.h" | |
| #include "clang/AST/ASTContext.h" | |
| #include "clang/AST/Expr.h" | |
| #include "clang/ASTMatchers/ASTMatchFinder.h" | |
| #include "clang/ASTMatchers/ASTMatchers.h" | |
| namespace clang { | |
| namespace tidy { | |
| namespace lifetimes { | |
| namespace { | |
| const clang::CallExpr* getFirstCallExpr(const clang::ASTContext& ast_context) { | |
| using clang::ast_matchers::callExpr; | |
| using clang::ast_matchers::match; | |
| using clang::ast_matchers::selectFirst; | |
| return selectFirst<clang::CallExpr>( | |
| "call", match(callExpr().bind("call"), | |
| const_cast<clang::ASTContext&>(ast_context))); | |
| } | |
| TEST(PointsToMapTest, Equality) { | |
| runOnCodeWithLifetimeHandlers( | |
| "int *return_int_ptr();" | |
| "int* p = return_int_ptr();", | |
| [](const clang::ASTContext& ast_context, | |
| const LifetimeAnnotationContext&) { | |
| Object p1(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p2(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p3(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| const clang::CallExpr* expr = getFirstCallExpr(ast_context); | |
| { | |
| PointsToMap map1, map2; | |
| map1.SetPointerPointsToSet(&p1, {&p2}); | |
| map2.SetPointerPointsToSet(&p1, {&p3}); | |
| EXPECT_EQ(map1, PointsToMap(map1)); | |
| EXPECT_NE(map1, PointsToMap()); | |
| EXPECT_NE(map1, map2); | |
| } | |
| { | |
| PointsToMap map1, map2; | |
| map1.SetExprObjectSet(expr, {&p1}); | |
| map2.SetExprObjectSet(expr, {&p2}); | |
| EXPECT_EQ(map1, PointsToMap(map1)); | |
| EXPECT_NE(map1, PointsToMap()); | |
| EXPECT_NE(map1, map2); | |
| } | |
| }, | |
| {}); | |
| } | |
| TEST(PointsToMapTest, Union) { | |
| runOnCodeWithLifetimeHandlers( | |
| "int *return_int_ptr();" | |
| "int* p = return_int_ptr();", | |
| [](const clang::ASTContext& ast_context, | |
| const LifetimeAnnotationContext&) { | |
| Object p1(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p2(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p3(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| const clang::CallExpr* expr = getFirstCallExpr(ast_context); | |
| PointsToMap map1, map2; | |
| map1.SetPointerPointsToSet(&p1, {&p2}); | |
| map2.SetPointerPointsToSet(&p1, {&p3}); | |
| map1.SetExprObjectSet(expr, {&p2}); | |
| map2.SetExprObjectSet(expr, {&p3}); | |
| PointsToMap union_map = map1.Union(map2); | |
| EXPECT_EQ(union_map.GetPointerPointsToSet(&p1), ObjectSet({&p2, &p3})); | |
| EXPECT_EQ(union_map.GetExprObjectSet(expr), ObjectSet({&p2, &p3})); | |
| }, | |
| {}); | |
| } | |
| TEST(PointsToMapTest, GetPointerPointsToSet) { | |
| runOnCodeWithLifetimeHandlers( | |
| "", | |
| [](const clang::ASTContext& ast_context, | |
| const LifetimeAnnotationContext&) { | |
| Object p1(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p2(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p3(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p4(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| PointsToMap map; | |
| EXPECT_EQ(map.GetPointerPointsToSet(&p1), ObjectSet()); | |
| map.SetPointerPointsToSet(&p1, {&p3}); | |
| map.SetPointerPointsToSet(&p2, {&p4}); | |
| EXPECT_EQ(map.GetPointerPointsToSet(&p1), ObjectSet({&p3})); | |
| EXPECT_EQ(map.GetPointerPointsToSet({&p1, &p2}), ObjectSet({&p3, &p4})); | |
| }, | |
| {}); | |
| } | |
| TEST(PointsToMapTest, ExtendPointerPointsToSet) { | |
| runOnCodeWithLifetimeHandlers( | |
| "", | |
| [](const clang::ASTContext& ast_context, | |
| const LifetimeAnnotationContext&) { | |
| Object p1(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p2(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| Object p3(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| PointsToMap map; | |
| EXPECT_EQ(map.GetPointerPointsToSet(&p1), ObjectSet()); | |
| map.ExtendPointerPointsToSet(&p1, {&p2}); | |
| EXPECT_EQ(map.GetPointerPointsToSet(&p1), ObjectSet({&p2})); | |
| map.ExtendPointerPointsToSet(&p1, {&p3}); | |
| EXPECT_EQ(map.GetPointerPointsToSet(&p1), ObjectSet({&p2, &p3})); | |
| }, | |
| {}); | |
| } | |
| TEST(PointsToMapTest, GetExprObjectSet) { | |
| runOnCodeWithLifetimeHandlers( | |
| "int *return_int_ptr();" | |
| "int* p = return_int_ptr();", | |
| [](const clang::ASTContext& ast_context, | |
| const LifetimeAnnotationContext&) { | |
| Object p1(Lifetime::CreateLocal(), ast_context.IntTy, std::nullopt); | |
| const clang::CallExpr* expr = getFirstCallExpr(ast_context); | |
| PointsToMap map; | |
| map.SetExprObjectSet(expr, {&p1}); | |
| EXPECT_EQ(map.GetExprObjectSet(expr), ObjectSet({&p1})); | |
| }, | |
| {}); | |
| } | |
| } // namespace | |
| } // namespace lifetimes | |
| } // namespace tidy | |
| } // namespace clang |