@@ -1152,6 +1152,87 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {
1152
1152
}
1153
1153
return false ;
1154
1154
}
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
+ }
1155
1236
1156
1237
static bool isAssignmentOperatorLifetimeBound (CXXMethodDecl *CMD) {
1157
1238
return CMD && isNormalAssignmentOperator (CMD) && CMD->param_size () == 1 &&
@@ -1189,27 +1270,17 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
1189
1270
1190
1271
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
1191
1272
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 ;
1213
1284
}
1214
1285
1215
1286
switch (LK) {
0 commit comments