| // 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 |
| |
| // Tests for initialization. |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "lifetime_analysis/test/lifetime_analysis_test.h" |
| |
| namespace clang { |
| namespace tidy { |
| namespace lifetimes { |
| namespace { |
| |
| // TODO(danakj): Crashes trying to find the initializer expression under |
| // MaterializeTemporaryExpr. Should be improved by cl/414032764. |
| TEST_F(LifetimeAnalysisTest, DISABLED_VarDeclReferenceToRecordTemporary) { |
| EXPECT_THAT(GetLifetimes(R"( |
| template <typename T> |
| struct S { |
| T a; |
| }; |
| int* target(int* a) { |
| const S<int*>& s = S<int*>{a}; |
| return s.a; |
| } |
| )"), |
| LifetimesAre({{"target", "a -> a"}})); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, VarDeclReferenceToRecordTemplate) { |
| EXPECT_THAT(GetLifetimes(R"( |
| template <typename T> |
| struct S { |
| T a; |
| }; |
| S<int*>* target(S<int*>* a) { |
| S<int*>& b = *a; |
| return &b; |
| } |
| )"), |
| LifetimesAre({{"target", "(a, b) -> (a, b)"}})); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, VarDeclReferenceToRecordNoTemplate) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct [[clang::annotate("lifetime_params", "a")]] S { |
| [[clang::annotate("member_lifetimes", "a")]] |
| int* a; |
| }; |
| S* target(S* a) { |
| S& b = *a; |
| return &b; |
| } |
| )"), |
| LifetimesAre({{"target", "(a, b) -> (a, b)"}})); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, ConstructorInitReferenceToRecord) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct [[clang::annotate("lifetime_params", "a")]] S { |
| [[clang::annotate("member_lifetimes", "a")]] |
| int* a; |
| }; |
| template <class Ref> |
| struct R { |
| R(S& s): s(s) {} |
| Ref s; |
| }; |
| int* target(S* a) { |
| R<S&> r(*a); |
| return r.s.a; |
| } |
| )"), |
| LifetimesAre({{"R<S &>::R", "(a, b, c): (a, b)"}, |
| {"target", "(a, b) -> a"}})); |
| } |
| |
| // TODO(danakj): Fails because a nested TransferMemberExpr() ends up looking for |
| // the field from the outer expr on the object of the inner expr. |
| // |
| // The code: |
| // ObjectSet struct_points_to = |
| // points_to_map.GetExprObjectSet(member->getBase()); |
| // |
| // The AST: |
| // MemberExpr 0x4027d3f2628 'int *':'int *' lvalue .p 0x4027d3f7338 |
| // `-MemberExpr 0x4027d3f25f8 'S<int *>':'struct S<int *>' lvalue .s |
| // 0x4027d3f74c0 |
| // `-DeclRefExpr 0x4027d3f25d8 'R<int *>':'struct R<int *>' lvalue Var |
| // 0x4027d3f6cd0 'r' 'R<int *>':'struct R<int *>' |
| // |
| // The p field is on struct S, but the code tries to find it on an object |
| // of type R<int *>. |
| TEST_F(LifetimeAnalysisTest, MemberInitReferenceToRecord) { |
| EXPECT_THAT( |
| GetLifetimes(R"( |
| template <typename P> |
| struct S { |
| P p; |
| }; |
| template<typename P> |
| struct [[clang::annotate("lifetime_params", "a")]] R { |
| R(P p): ss{p} {} |
| S<P> ss; |
| [[clang::annotate("member_lifetimes", "a")]] |
| S<P>& s{ss}; |
| }; |
| int* target(int* a) { |
| R<int*> r(a); |
| return r.s.p; |
| } |
| )"), |
| LifetimesAre({{"R<int *>::R", "(<a> [b], b): a"}, {"target", "a -> a"}})); |
| } |
| |
| } // namespace |
| } // namespace lifetimes |
| } // namespace tidy |
| } // namespace clang |