@@ -1093,6 +1093,87 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {
1093
1093
}
1094
1094
return false ;
1095
1095
}
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
+ }
1096
1177
1097
1178
static bool isAssignmentOperatorLifetimeBound (CXXMethodDecl *CMD) {
1098
1179
if (!CMD)
@@ -1131,27 +1212,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
1131
1212
1132
1213
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
1133
1214
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 ;
1155
1226
}
1156
1227
1157
1228
switch (LK) {
0 commit comments