@@ -1152,6 +1152,87 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {
11521152 }
11531153 return false ;
11541154}
1155+ // Result of analyzing the Path for GSLPointer.
1156+ enum AnalysisResult {
1157+ // Path does not correspond to a GSLPointer.
1158+ NotGSLPointer,
1159+
1160+ // A relevant case was identified.
1161+ Report,
1162+ // Stop the entire traversal.
1163+ Abandon,
1164+ // Skip this step and continue traversing inner AST nodes.
1165+ Skip,
1166+ };
1167+ // Analyze cases where a GSLPointer is initialized or assigned from a
1168+ // temporary owner object.
1169+ static AnalysisResult analyzePathForGSLPointer (const IndirectLocalPath &Path,
1170+ Local L) {
1171+ if (!pathOnlyHandlesGslPointer (Path))
1172+ return NotGSLPointer;
1173+
1174+ // At this point, Path represents a series of operations involving a
1175+ // GSLPointer, either in the process of initialization or assignment.
1176+
1177+ // Note: A LifetimeBoundCall can appear interleaved in this sequence.
1178+ // For example:
1179+ // const std::string& Ref(const std::string& a [[clang::lifetimebound]]);
1180+ // string_view abc = Ref(std::string());
1181+ // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the
1182+ // temporary "std::string()" object. We need to check if the function with the
1183+ // lifetimebound attribute returns a "owner" type.
1184+ if (Path.back ().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
1185+ // The lifetimebound applies to the implicit object parameter of a method.
1186+ if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Path.back ().D )) {
1187+ if (Method->getReturnType ()->isReferenceType () &&
1188+ isRecordWithAttr<OwnerAttr>(
1189+ Method->getReturnType ()->getPointeeType ()))
1190+ return Report;
1191+ return Abandon;
1192+ }
1193+ // The lifetimebound applies to a function parameter.
1194+ const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back ().D );
1195+ if (const auto *FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext ())) {
1196+ if (isa<CXXConstructorDecl>(FD)) {
1197+ // Constructor case: the parameter is annotated with lifetimebound
1198+ // e.g., GSLPointer(const S& s [[clang::lifetimebound]])
1199+ // We still respect this case even the type S is not an owner.
1200+ return Report;
1201+ }
1202+ // For regular functions, check if the return type has an Owner attribute.
1203+ // e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
1204+ if (FD->getReturnType ()->isReferenceType () &&
1205+ isRecordWithAttr<OwnerAttr>(FD->getReturnType ()->getPointeeType ()))
1206+ return Report;
1207+ }
1208+ return Abandon;
1209+ }
1210+
1211+ if (isa<DeclRefExpr>(L)) {
1212+ // We do not want to follow the references when returning a pointer
1213+ // originating from a local owner to avoid the following false positive:
1214+ // int &p = *localUniquePtr;
1215+ // someContainer.add(std::move(localUniquePtr));
1216+ // return p;
1217+ if (!pathContainsInit (Path) && isRecordWithAttr<OwnerAttr>(L->getType ()))
1218+ return Report;
1219+ return Abandon;
1220+ }
1221+
1222+ // The GSLPointer is from a temporary object.
1223+ auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
1224+
1225+ bool IsGslPtrValueFromGslTempOwner =
1226+ MTE && !MTE->getExtendingDecl () &&
1227+ isRecordWithAttr<OwnerAttr>(MTE->getType ());
1228+ // Skipping a chain of initializing gsl::Pointer annotated objects.
1229+ // We are looking only for the final source to find out if it was
1230+ // a local or temporary owner or the address of a local
1231+ // variable/param.
1232+ if (!IsGslPtrValueFromGslTempOwner)
1233+ return Skip;
1234+ return Report;
1235+ }
11551236
11561237static bool isAssignmentOperatorLifetimeBound (CXXMethodDecl *CMD) {
11571238 return CMD && isNormalAssignmentOperator (CMD) && CMD->param_size () == 1 &&
@@ -1189,27 +1270,17 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
11891270
11901271 auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
11911272
1192- bool IsGslPtrValueFromGslTempOwner = false ;
1193- if (pathOnlyHandlesGslPointer (Path)) {
1194- if (isa<DeclRefExpr>(L)) {
1195- // We do not want to follow the references when returning a pointer
1196- // originating from a local owner to avoid the following false positive:
1197- // int &p = *localUniquePtr;
1198- // someContainer.add(std::move(localUniquePtr));
1199- // return p;
1200- if (pathContainsInit (Path) ||
1201- !isRecordWithAttr<OwnerAttr>(L->getType ()))
1202- return false ;
1203- } else {
1204- IsGslPtrValueFromGslTempOwner =
1205- MTE && !MTE->getExtendingDecl () &&
1206- isRecordWithAttr<OwnerAttr>(MTE->getType ());
1207- // Skipping a chain of initializing gsl::Pointer annotated objects.
1208- // We are looking only for the final source to find out if it was
1209- // a local or temporary owner or the address of a local variable/param.
1210- if (!IsGslPtrValueFromGslTempOwner)
1211- return true ;
1212- }
1273+ bool IsGslPtrValueFromGslTempOwner = true ;
1274+ switch (analyzePathForGSLPointer (Path, L)) {
1275+ case Abandon:
1276+ return false ;
1277+ case Skip:
1278+ return true ;
1279+ case NotGSLPointer:
1280+ IsGslPtrValueFromGslTempOwner = false ;
1281+ LLVM_FALLTHROUGH;
1282+ case Report:
1283+ break ;
12131284 }
12141285
12151286 switch (LK) {
0 commit comments