diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h index f3aa30120ffe6..38c37e7103b73 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h @@ -45,11 +45,18 @@ llvm::Error makeSawButExpectedError(const JSONTy &Saw, llvm::StringRef Expected, return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...); } -template bool hasPtrOrArrType(const DeclOrExpr *E) { +///\return true iff expression `E` has pointer or array type. +inline bool hasPtrOrArrType(const Expr *E) { return llvm::isa( E->getType().getCanonicalType()); } +///\return true iff Decl `D` has (reference-to) pointer or array type. +inline bool hasPtrOrArrType(const ValueDecl *D) { + return llvm::isa( + D->getType().getNonReferenceType().getCanonicalType()); +} + llvm::Error makeEntityNameErr(clang::ASTContext &Ctx, const clang::NamedDecl *D); diff --git a/clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test b/clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test new file mode 100644 index 0000000000000..ca5df041240aa --- /dev/null +++ b/clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test @@ -0,0 +1,41 @@ +// Test that entities representing references-to-pointers are +// connected through 'fake_move' (my fake 'std::move'). + +// RUN: rm -rf %t && mkdir -p %t +// RUN: split-file %s %t + +// Extract per-TU PointerFlow + UnsafeBufferUsage summaries. +// RUN: %clang_cc1 -fsyntax-only %t/tu.cpp \ +// RUN: --ssaf-extract-summaries=PointerFlow,UnsafeBufferUsage \ +// RUN: --ssaf-tu-summary-file=%t/tu.summary.json \ +// RUN: --ssaf-compilation-unit-id="tu-1" + +// Link into a single LU summary. +// RUN: clang-ssaf-linker %t/tu.summary.json -o %t/lu.json + +// RUN: clang-ssaf-analyzer %t/lu.json -o %t/wpa.json \ +// RUN: -a UnsafeBufferReachableAnalysisResult + +//--- tu.cpp +constexpr int* &&fake_move(int* &arg) noexcept { //FIXME: after #198927, we can use template + return static_cast(arg); +} + +void foo(int *p) { + int *&&rp = fake_move(p); + rp[5] = 0; +} + +// Check that unsafe buffer propagates from 'rp' to 'p' by ensuring +// that both 'rp' and 'p' are in the reachable set. + +// RUN: FileCheck %s --input-file=%t/wpa.json + +// CHECK-DAG: "id": [[RP_ID:[0-9]+]],{{([^]]|[[:space:]])+\],[[:space:]]+"suffix": "",[[:space:]]+"usr": "[^"]*}}@rp" +// CHECK-DAG: "id": [[P_ID:[0-9]+]],{{([^]]|[[:space:]])+\],[[:space:]]+"suffix": "1",[[:space:]]+"usr": }}"c:@F@foo#*I#" + +// CHECK: "analysis_name": "UnsafeBufferReachableAnalysisResult" +// CHECK-DAG: {{\{[[:space:]]+}}"@": [[RP_ID]]{{[[:space:]]+\},[[:space:]]+1[[:space:]]+\]}} +// CHECK-DAG: {{\{[[:space:]]+}}"@": [[P_ID]]{{[[:space:]]+\},[[:space:]]+1[[:space:]]+\]}} +// CHECK: "analysis_name" + diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp index e5512288491c5..875111aa893ad 100644 --- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp +++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp @@ -1306,4 +1306,65 @@ TEST_F(PointerFlowTest, RHSResultsInNoEntityPointerLevel) { ASSERT_FALSE(getEntitySummary("g")); } +////////////////////////////////////////////////////////////// +// Reference-to-pointer Tests // +////////////////////////////////////////////////////////////// + +TEST_F(PointerFlowTest, ArgToRefParam) { + ASSERT_TRUE(setUpTest(R"cpp( + void callee(int *&rp); + void caller(int *p) { + callee(p); + } + )cpp")); + + auto *Sum = getEntitySummary("caller"); + + ASSERT_TRUE(Sum); + EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"rp", 1U}, {"p", 1U}}})); +} + +TEST_F(PointerFlowTest, ArgToRefParamLevel2) { + ASSERT_TRUE(setUpTest(R"cpp( + void callee(int **&rp); + void caller(int **pp) { + callee(pp); + } + )cpp")); + + auto *Sum = getEntitySummary("caller"); + + ASSERT_TRUE(Sum); + EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"rp", 1U}, {"pp", 1U}}})); +} + +TEST_F(PointerFlowTest, InitRefPtr) { + ASSERT_TRUE(setUpTest(R"cpp( + void foo(int *p) { + int *&rp = p; + int * const & crp = p; + } + )cpp")); + + auto *Sum = getEntitySummary("foo"); + + ASSERT_TRUE(Sum); + EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"rp", 1U}, {"p", 1U}}, + {{"crp", 1U}, {"p", 1U}}})); +} + +TEST_F(PointerFlowTest, ReturnRefPtr) { + ASSERT_TRUE(setUpTest(R"cpp( + int *& f(); + int *& foo() { + return f(); + } + )cpp")); + + auto *Sum = getEntitySummary("foo"); + + ASSERT_TRUE(Sum); + EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"f", 1U, true}}})); +} + } // namespace