[clang] [clang] Fix the post-filtering heuristic for GSLPointer. (PR #114044)
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 12 11:07:22 PST 2024
https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/114044
>From 15b7806b933b0964d004c9d13565bc834c59fb01 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Fri, 1 Nov 2024 16:51:03 +0100
Subject: [PATCH 1/6] [clang] Fix the post-filtering heuristics for GSLPointer
case.
---
clang/docs/ReleaseNotes.rst | 2 +
clang/lib/Sema/CheckExprLifetime.cpp | 113 ++++++++++++++----
.../Sema/warn-lifetime-analysis-nocfg.cpp | 48 +++++++-
3 files changed, 139 insertions(+), 24 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 26fa986810a4b8..befa411e882b4c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -614,6 +614,8 @@ Improvements to Clang's diagnostics
- Clang now diagnoses ``[[deprecated]]`` attribute usage on local variables (#GH90073).
+- Fix false positives when `[[gsl::Owner/Pointer]]` and `[[clang::lifetimebound]]` are used together.
+
- Improved diagnostic message for ``__builtin_bit_cast`` size mismatch (#GH115870).
- Clang now omits shadow warnings for enum constants in separate class scopes (#GH62588).
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 868081292bc32c..07610f243f9169 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1152,6 +1152,87 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {
}
return false;
}
+// Result of analyzing the Path for GSLPointer.
+enum AnalysisResult {
+ // Path does not correspond to a GSLPointer.
+ NotGSLPointer,
+
+ // A relevant case was identified.
+ Report,
+ // Stop the entire traversal.
+ Abandon,
+ // Skip this step and continue traversing inner AST nodes.
+ Skip,
+};
+// Analyze cases where a GSLPointer is initialized or assigned from a
+// temporary owner object.
+static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
+ Local L) {
+ if (!pathOnlyHandlesGslPointer(Path))
+ return NotGSLPointer;
+
+ // At this point, Path represents a series of operations involving a
+ // GSLPointer, either in the process of initialization or assignment.
+
+ // Note: A LifetimeBoundCall can appear interleaved in this sequence.
+ // For example:
+ // const std::string& Ref(const std::string& a [[clang::lifetimebound]]);
+ // string_view abc = Ref(std::string());
+ // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the
+ // temporary "std::string()" object. We need to check if the function with the
+ // lifetimebound attribute returns a "owner" type.
+ if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
+ // The lifetimebound applies to the implicit object parameter of a method.
+ if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Path.back().D)) {
+ if (Method->getReturnType()->isReferenceType() &&
+ isRecordWithAttr<OwnerAttr>(
+ Method->getReturnType()->getPointeeType()))
+ return Report;
+ return Abandon;
+ }
+ // The lifetimebound applies to a function parameter.
+ const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D);
+ if (const auto *FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext())) {
+ if (isa<CXXConstructorDecl>(FD)) {
+ // Constructor case: the parameter is annotated with lifetimebound
+ // e.g., GSLPointer(const S& s [[clang::lifetimebound]])
+ // We still respect this case even the type S is not an owner.
+ return Report;
+ }
+ // For regular functions, check if the return type has an Owner attribute.
+ // e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
+ if (FD->getReturnType()->isReferenceType() &&
+ isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+ return Report;
+ }
+ return Abandon;
+ }
+
+ if (isa<DeclRefExpr>(L)) {
+ // We do not want to follow the references when returning a pointer
+ // originating from a local owner to avoid the following false positive:
+ // int &p = *localUniquePtr;
+ // someContainer.add(std::move(localUniquePtr));
+ // return p;
+ if (!pathContainsInit(Path) && isRecordWithAttr<OwnerAttr>(L->getType()))
+ return Report;
+ return Abandon;
+ }
+
+ // The GSLPointer is from a temporary object.
+ auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
+
+ bool IsGslPtrValueFromGslTempOwner =
+ MTE && !MTE->getExtendingDecl() &&
+ isRecordWithAttr<OwnerAttr>(MTE->getType());
+ // Skipping a chain of initializing gsl::Pointer annotated objects.
+ // We are looking only for the final source to find out if it was
+ // a local or temporary owner or the address of a local
+ // variable/param.
+ if (!IsGslPtrValueFromGslTempOwner)
+ return Skip;
+ return Report;
+}
static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) {
return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
@@ -1189,27 +1270,17 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
- bool IsGslPtrValueFromGslTempOwner = false;
- if (pathOnlyHandlesGslPointer(Path)) {
- if (isa<DeclRefExpr>(L)) {
- // We do not want to follow the references when returning a pointer
- // originating from a local owner to avoid the following false positive:
- // int &p = *localUniquePtr;
- // someContainer.add(std::move(localUniquePtr));
- // return p;
- if (pathContainsInit(Path) ||
- !isRecordWithAttr<OwnerAttr>(L->getType()))
- return false;
- } else {
- IsGslPtrValueFromGslTempOwner =
- MTE && !MTE->getExtendingDecl() &&
- isRecordWithAttr<OwnerAttr>(MTE->getType());
- // Skipping a chain of initializing gsl::Pointer annotated objects.
- // We are looking only for the final source to find out if it was
- // a local or temporary owner or the address of a local variable/param.
- if (!IsGslPtrValueFromGslTempOwner)
- return true;
- }
+ bool IsGslPtrValueFromGslTempOwner = true;
+ switch (analyzePathForGSLPointer(Path, L)) {
+ case Abandon:
+ return false;
+ case Skip:
+ return true;
+ case NotGSLPointer:
+ IsGslPtrValueFromGslTempOwner = false;
+ LLVM_FALLTHROUGH;
+ case Report:
+ break;
}
switch (LK) {
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index fc876926ba2e63..624ed8d64c79f0 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -604,8 +604,9 @@ struct [[gsl::Pointer]] Span {
// Pointer from Owner<Pointer>
std::string_view test5() {
- std::string_view a = StatusOr<std::string_view>().valueLB(); // expected-warning {{object backing the pointer will be dest}}
- return StatusOr<std::string_view>().valueLB(); // expected-warning {{returning address of local temporary}}
+ // The Owner<Pointer> doesn't own the object which its inner pointer points to.
+ std::string_view a = StatusOr<std::string_view>().valueLB(); // OK
+ return StatusOr<std::string_view>().valueLB(); // OK
// No dangling diagnostics on non-lifetimebound methods.
std::string_view b = StatusOr<std::string_view>().valueNoLB();
@@ -652,7 +653,7 @@ Span<std::string> test10(StatusOr<std::vector<std::string>> aa) {
// Pointer<Owner>> from Owner<Pointer<Owner>>
Span<std::string> test11(StatusOr<Span<std::string>> aa) {
- return aa.valueLB(); // expected-warning {{address of stack memory}}
+ return aa.valueLB(); // OK
return aa.valueNoLB(); // OK.
}
@@ -693,3 +694,44 @@ void test() {
auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}}
}
} // namespace GH118064
+
+namespace LifetimeboundInterleave {
+
+const std::string& Ref(const std::string& abc [[clang::lifetimebound]]);
+std::string_view test1() {
+ std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}}
+ t1 = Ref(std::string()); // expected-warning {{object backing}}
+ return Ref(std::string()); // expected-warning {{returning address}}
+}
+
+template <typename T>
+struct Foo {
+ const T& get() const [[clang::lifetimebound]];
+ const T& getNoLB() const;
+};
+std::string_view test2(Foo<std::string> r1, Foo<std::string_view> r2) {
+ std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}}
+ t1 = Foo<std::string>().get(); // expected-warning {{object backing}}
+ return r1.get(); // expected-warning {{address of stack}}
+
+ std::string_view t2 = Foo<std::string_view>().get();
+ t2 = Foo<std::string_view>().get();
+ return r2.get();
+
+ // no warning on no-LB-annotated method.
+ std::string_view t3 = Foo<std::string>().getNoLB();
+ t3 = Foo<std::string>().getNoLB();
+ return r1.getNoLB();
+}
+
+struct Bar {};
+struct [[gsl::Pointer]] Pointer {
+ Pointer(const Bar & bar [[clang::lifetimebound]]);
+};
+Pointer test3(Bar bar) {
+ Pointer p = Pointer(Bar()); // expected-warning {{temporary}}
+ p = Pointer(Bar()); // expected-warning {{object backing}}
+ return bar; // expected-warning {{address of stack}}
+}
+
+} // namespace LifetimeboundInterleave
>From 52d3387ad64edb89399562c9fc7fd6607c559b4b Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Mon, 4 Nov 2024 16:36:48 +0100
Subject: [PATCH 2/6] Address review comments
---
clang/lib/Sema/CheckExprLifetime.cpp | 39 ++++++++++++----------------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 07610f243f9169..fbe87c3df69158 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1183,28 +1183,23 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// lifetimebound attribute returns a "owner" type.
if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
// The lifetimebound applies to the implicit object parameter of a method.
- if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Path.back().D)) {
- if (Method->getReturnType()->isReferenceType() &&
- isRecordWithAttr<OwnerAttr>(
- Method->getReturnType()->getPointeeType()))
- return Report;
- return Abandon;
- }
+ const FunctionDecl* FD = llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);
// The lifetimebound applies to a function parameter.
- const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D);
- if (const auto *FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext())) {
- if (isa<CXXConstructorDecl>(FD)) {
- // Constructor case: the parameter is annotated with lifetimebound
- // e.g., GSLPointer(const S& s [[clang::lifetimebound]])
- // We still respect this case even the type S is not an owner.
- return Report;
- }
- // For regular functions, check if the return type has an Owner attribute.
- // e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
- if (FD->getReturnType()->isReferenceType() &&
- isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
- return Report;
+ if (const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D))
+ FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext());
+
+ if (isa_and_present<CXXConstructorDecl>(FD)) {
+ // Constructor case: the parameter is annotated with lifetimebound
+ // e.g., GSLPointer(const S& s [[clang::lifetimebound]])
+ // We still respect this case even the type S is not an owner.
+ return Report;
}
+ // Check if the return type has an Owner attribute.
+ // e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
+ if (FD && FD->getReturnType()->isReferenceType() &&
+ isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+ return Report;
+
return Abandon;
}
@@ -1273,9 +1268,9 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
bool IsGslPtrValueFromGslTempOwner = true;
switch (analyzePathForGSLPointer(Path, L)) {
case Abandon:
- return false;
+ return false;
case Skip:
- return true;
+ return true;
case NotGSLPointer:
IsGslPtrValueFromGslTempOwner = false;
LLVM_FALLTHROUGH;
>From 8cb609ce14e6e2d6b88e9629de4906464d8ab096 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 5 Nov 2024 13:10:39 +0100
Subject: [PATCH 3/6] clang-format
---
clang/lib/Sema/CheckExprLifetime.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index fbe87c3df69158..f52e69e9ac1e67 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1183,7 +1183,8 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// lifetimebound attribute returns a "owner" type.
if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
// The lifetimebound applies to the implicit object parameter of a method.
- const FunctionDecl* FD = llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);
+ const FunctionDecl *FD =
+ llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);
// The lifetimebound applies to a function parameter.
if (const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D))
FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext());
@@ -1197,7 +1198,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// Check if the return type has an Owner attribute.
// e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
if (FD && FD->getReturnType()->isReferenceType() &&
- isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+ isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
return Report;
return Abandon;
>From 1666c4f0b4f99a6fca43b1b449dfe440a7b9f9ac Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 5 Nov 2024 17:18:32 +0100
Subject: [PATCH 4/6] Add more tests, and a minor fix for a function returning
gsl pointer.
---
clang/lib/Sema/CheckExprLifetime.cpp | 15 +++++++++------
.../test/Sema/warn-lifetime-analysis-nocfg.cpp | 18 ++++++++++++++++++
2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index f52e69e9ac1e67..9f07b8e7343eb4 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1179,8 +1179,8 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// const std::string& Ref(const std::string& a [[clang::lifetimebound]]);
// string_view abc = Ref(std::string());
// The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the
- // temporary "std::string()" object. We need to check if the function with the
- // lifetimebound attribute returns a "owner" type.
+ // temporary "std::string()" object. We need to check the return type of the
+ // function with the lifetimebound attribute.
if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
// The lifetimebound applies to the implicit object parameter of a method.
const FunctionDecl *FD =
@@ -1195,10 +1195,13 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// We still respect this case even the type S is not an owner.
return Report;
}
- // Check if the return type has an Owner attribute.
- // e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
- if (FD && FD->getReturnType()->isReferenceType() &&
- isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+ // Check the return type, e.g.
+ // const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
+ // GSLPointer func(const Foo& foo [[clang::lifetimebound]])
+ if (FD &&
+ ((FD->getReturnType()->isReferenceType() &&
+ isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) ||
+ isRecordWithAttr<PointerAttr>(FD->getReturnType())))
return Report;
return Abandon;
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 624ed8d64c79f0..3845cb32b37f6e 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -698,10 +698,28 @@ void test() {
namespace LifetimeboundInterleave {
const std::string& Ref(const std::string& abc [[clang::lifetimebound]]);
+
+std::string_view TakeSv(std::string_view abc [[clang::lifetimebound]]);
+std::string_view TakeStrRef(const std::string& abc [[clang::lifetimebound]]);
+std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
+
std::string_view test1() {
std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}}
t1 = Ref(std::string()); // expected-warning {{object backing}}
return Ref(std::string()); // expected-warning {{returning address}}
+
+ std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}}
+ t2 = TakeSv(std::string()); // expected-warning {{object backing}}
+ return TakeSv(std::string()); // expected-warning {{returning address}}
+
+ std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}}
+ t3 = TakeStrRef(std::string()); // expected-warning {{object backing}}
+ return TakeStrRef(std::string()); // expected-warning {{returning address}}
+
+
+ std::string_view t4 = TakeStr(std::string());
+ t4 = TakeStr(std::string());
+ return TakeStr(std::string());
}
template <typename T>
>From 352d3424c5b677a883f832ce1a06ec7ff1fff377 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 19 Nov 2024 11:37:52 +0100
Subject: [PATCH 5/6] Fix a newly-found false positive.
---
clang/lib/Sema/CheckExprLifetime.cpp | 2 ++
clang/test/Sema/Inputs/lifetime-analysis.h | 5 ++++
.../Sema/warn-lifetime-analysis-nocfg.cpp | 24 +++++++++++++++++++
3 files changed, 31 insertions(+)
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 9f07b8e7343eb4..894a6d4949d243 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -367,6 +367,8 @@ static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
if (Callee->getReturnType()->isReferenceType()) {
if (!Callee->getIdentifier()) {
auto OO = Callee->getOverloadedOperator();
+ if (!Callee->getParent()->hasAttr<OwnerAttr>())
+ return false;
return OO == OverloadedOperatorKind::OO_Subscript ||
OO == OverloadedOperatorKind::OO_Star;
}
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 5c151385b1fe5a..f888e6ab94bb6a 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -128,6 +128,11 @@ struct reference_wrapper {
template<typename T>
reference_wrapper<T> ref(T& t) noexcept;
+template <typename T>
+struct [[gsl::Pointer]] iterator {
+ T& operator*() const;
+};
+
struct false_type {
static constexpr bool value = false;
constexpr operator bool() const noexcept { return value; }
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 3845cb32b37f6e..45b4dc838f44ed 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -752,4 +752,28 @@ Pointer test3(Bar bar) {
return bar; // expected-warning {{address of stack}}
}
+template<typename T>
+struct MySpan {
+ MySpan(const std::vector<T>& v);
+ using iterator = std::iterator<T>;
+ iterator begin() const [[clang::lifetimebound]];
+};
+template <typename T>
+typename MySpan<T>::iterator ReturnFirstIt(const MySpan<T>& v [[clang::lifetimebound]]);
+
+void test4() {
+ std::vector<int> v{1};
+ // MySpan<T> doesn't own any underlying T objects, the pointee object of
+ // the MySpan iterator is still alive when the whole span is destroyed, thus
+ // no diagnostic.
+ const int& t1 = *MySpan<int>(v).begin();
+ const int& t2 = *ReturnFirstIt(MySpan<int>(v));
+ // Ideally, we would diagnose the following case, but due to implementation
+ // constraints, we do not.
+ const int& t4 = *MySpan<int>(std::vector<int>{}).begin();
+
+ auto it1 = MySpan<int>(v).begin(); // expected-warning {{temporary whose address is use}}
+ auto it2 = ReturnFirstIt(MySpan<int>(v)); // expected-warning {{temporary whose address is used}}
+}
+
} // namespace LifetimeboundInterleave
>From 80a50d19b4df9c0a7ad983280de18fdeba98e711 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Wed, 11 Dec 2024 14:25:05 +0100
Subject: [PATCH 6/6] Fix some broken capture-by tests.
---
clang/lib/Sema/CheckExprLifetime.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 894a6d4949d243..843fdb4a65cd73 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1203,7 +1203,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
if (FD &&
((FD->getReturnType()->isReferenceType() &&
isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) ||
- isRecordWithAttr<PointerAttr>(FD->getReturnType())))
+ isPointerLikeType(FD->getReturnType())))
return Report;
return Abandon;
More information about the cfe-commits
mailing list