@@ -1093,6 +1093,87 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {
10931093 }
10941094 return false ;
10951095}
1096+ // Result of analyzing the Path for GSLPointer.
1097+ enum AnalysisResult {
1098+ // Path does not correspond to a GSLPointer.
1099+ NotGSLPointer,
1100+
1101+ // A relevant case was identified.
1102+ Report,
1103+ // Stop the entire traversal.
1104+ Abandon,
1105+ // Skip this step and continue traversing inner AST nodes.
1106+ Skip,
1107+ };
1108+ // Analyze cases where a GSLPointer is initialized or assigned from a
1109+ // temporary owner object.
1110+ static AnalysisResult analyzePathForGSLPointer (const IndirectLocalPath &Path,
1111+ Local L) {
1112+ if (!pathOnlyHandlesGslPointer (Path))
1113+ return NotGSLPointer;
1114+
1115+ // At this point, Path represents a series of operations involving a
1116+ // GSLPointer, either in the process of initialization or assignment.
1117+
1118+ // Note: A LifetimeBoundCall can appear interleaved in this sequence.
1119+ // For example:
1120+ // const std::string& Ref(const std::string& a [[clang::lifetimebound]]);
1121+ // string_view abc = Ref(std::string());
1122+ // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the
1123+ // temporary "std::string()" object. We need to check if the function with the
1124+ // lifetimebound attribute returns a "owner" type.
1125+ if (Path.back ().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
1126+ // The lifetimebound applies to the implicit object parameter of a method.
1127+ if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Path.back ().D )) {
1128+ if (Method->getReturnType ()->isReferenceType () &&
1129+ isRecordWithAttr<OwnerAttr>(
1130+ Method->getReturnType ()->getPointeeType ()))
1131+ return Report;
1132+ return Abandon;
1133+ }
1134+ // The lifetimebound applies to a function parameter.
1135+ const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back ().D );
1136+ if (const auto *FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext ())) {
1137+ if (isa<CXXConstructorDecl>(FD)) {
1138+ // Constructor case: the parameter is annotated with lifetimebound
1139+ // e.g., GSLPointer(const S& s [[clang::lifetimebound]])
1140+ // We still respect this case even the type S is not an owner.
1141+ return Report;
1142+ }
1143+ // For regular functions, check if the return type has an Owner attribute.
1144+ // e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
1145+ if (FD->getReturnType ()->isReferenceType () &&
1146+ isRecordWithAttr<OwnerAttr>(FD->getReturnType ()->getPointeeType ()))
1147+ return Report;
1148+ }
1149+ return Abandon;
1150+ }
1151+
1152+ if (isa<DeclRefExpr>(L)) {
1153+ // We do not want to follow the references when returning a pointer
1154+ // originating from a local owner to avoid the following false positive:
1155+ // int &p = *localUniquePtr;
1156+ // someContainer.add(std::move(localUniquePtr));
1157+ // return p;
1158+ if (!pathContainsInit (Path) && isRecordWithAttr<OwnerAttr>(L->getType ()))
1159+ return Report;
1160+ return Abandon;
1161+ }
1162+
1163+ // The GSLPointer is from a temporary object.
1164+ auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
1165+
1166+ bool IsGslPtrValueFromGslTempOwner =
1167+ MTE && !MTE->getExtendingDecl () &&
1168+ isRecordWithAttr<OwnerAttr>(MTE->getType ());
1169+ // Skipping a chain of initializing gsl::Pointer annotated objects.
1170+ // We are looking only for the final source to find out if it was
1171+ // a local or temporary owner or the address of a local
1172+ // variable/param.
1173+ if (!IsGslPtrValueFromGslTempOwner)
1174+ return Skip;
1175+ return Report;
1176+ }
10961177
10971178static bool isAssignmentOperatorLifetimeBound (CXXMethodDecl *CMD) {
10981179 if (!CMD)
@@ -1131,27 +1212,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
11311212
11321213 auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
11331214
1134- bool IsGslPtrValueFromGslTempOwner = false ;
1135- if (pathOnlyHandlesGslPointer (Path)) {
1136- if (isa<DeclRefExpr>(L)) {
1137- // We do not want to follow the references when returning a pointer
1138- // originating from a local owner to avoid the following false positive:
1139- // int &p = *localUniquePtr;
1140- // someContainer.add(std::move(localUniquePtr));
1141- // return p;
1142- if (pathContainsInit (Path) ||
1143- !isRecordWithAttr<OwnerAttr>(L->getType ()))
1144- return false ;
1145- } else {
1146- IsGslPtrValueFromGslTempOwner =
1147- MTE && !MTE->getExtendingDecl () &&
1148- isRecordWithAttr<OwnerAttr>(MTE->getType ());
1149- // Skipping a chain of initializing gsl::Pointer annotated objects.
1150- // We are looking only for the final source to find out if it was
1151- // a local or temporary owner or the address of a local variable/param.
1152- if (!IsGslPtrValueFromGslTempOwner)
1153- return true ;
1154- }
1215+ bool IsGslPtrValueFromGslTempOwner = true ;
1216+ switch (analyzePathForGSLPointer (Path, L)) {
1217+ case Abandon:
1218+ return false ;
1219+ case Skip:
1220+ return true ;
1221+ case NotGSLPointer:
1222+ IsGslPtrValueFromGslTempOwner = false ;
1223+ LLVM_FALLTHROUGH;
1224+ case Report:
1225+ break ;
11551226 }
11561227
11571228 switch (LK) {
0 commit comments