@@ -1102,6 +1102,21 @@ class StopTrackingCallback final : public SymbolVisitor {
1102
1102
}
1103
1103
};
1104
1104
1105
+ // / EscapeTrackedCallback - A SymbolVisitor that marks allocated symbols as escaped.
1106
+ // /
1107
+ // / This visitor is used to suppress false positive leak reports when smart pointers
1108
+ // / are nested in temporary objects passed by value to functions. When the analyzer
1109
+ // / can't see the destructor calls for temporary objects, it may incorrectly report
1110
+ // / leaks for memory that will be properly freed by the smart pointer destructors.
1111
+ // /
1112
+ // / The visitor traverses reachable symbols from a given set of memory regions
1113
+ // / (typically smart pointer field regions) and marks any allocated symbols as
1114
+ // / escaped. Escaped symbols are not reported as leaks by checkDeadSymbols.
1115
+ // /
1116
+ // / Usage:
1117
+ // / auto Scan = State->scanReachableSymbols<EscapeTrackedCallback>(RootRegions);
1118
+ // / ProgramStateRef NewState = Scan.getState();
1119
+ // / if (NewState != State) C.addTransition(NewState);
1105
1120
class EscapeTrackedCallback final : public SymbolVisitor {
1106
1121
ProgramStateRef State;
1107
1122
@@ -3104,10 +3119,12 @@ static bool isInStdNamespace(const DeclContext *DC) {
3104
3119
return false ;
3105
3120
}
3106
3121
3107
- static bool isUniquePtrType (QualType QT) {
3122
+ // Allowlist of owning smart pointers we want to recognize.
3123
+ // Start with unique_ptr and shared_ptr. (intentionally exclude weak_ptr)
3124
+ static bool isSmartOwningPtrType (QualType QT) {
3108
3125
QT = canonicalStrip (QT);
3109
3126
3110
- // First try TemplateSpecializationType (for std::unique_ptr )
3127
+ // First try TemplateSpecializationType (for std smart pointers )
3111
3128
const auto *TST = QT->getAs <TemplateSpecializationType>();
3112
3129
if (TST) {
3113
3130
const TemplateDecl *TD = TST->getTemplateName ().getAsTemplateDecl ();
@@ -3116,38 +3133,42 @@ static bool isUniquePtrType(QualType QT) {
3116
3133
const auto *ND = dyn_cast_or_null<NamedDecl>(TD->getTemplatedDecl ());
3117
3134
if (!ND) return false ;
3118
3135
3119
- if (ND->getName () != " unique_ptr" ) return false ;
3120
-
3121
3136
// Check if it's in std namespace
3122
3137
const DeclContext *DC = ND->getDeclContext ();
3123
- if (isInStdNamespace (DC)) return true ;
3138
+ if (!isInStdNamespace (DC)) return false ;
3139
+
3140
+ StringRef Name = ND->getName ();
3141
+ return Name == " unique_ptr" || Name == " shared_ptr" ;
3124
3142
}
3125
3143
3126
- // Also try RecordType (for custom unique_ptr )
3144
+ // Also try RecordType (for custom smart pointer implementations )
3127
3145
const auto *RT = QT->getAs <RecordType>();
3128
3146
if (RT) {
3129
3147
const auto *RD = RT->getDecl ();
3130
- if (RD && RD->getName () == " unique_ptr" ) {
3131
- // Accept any custom unique_ptr implementation
3132
- return true ;
3148
+ if (RD) {
3149
+ StringRef Name = RD->getName ();
3150
+ if (Name == " unique_ptr" || Name == " shared_ptr" ) {
3151
+ // Accept any custom unique_ptr or shared_ptr implementation
3152
+ return true ;
3153
+ }
3133
3154
}
3134
3155
}
3135
3156
3136
3157
return false ;
3137
3158
}
3138
3159
3139
- static void collectDirectUniquePtrFieldRegions (const MemRegion *Base,
3140
- QualType RecQT,
3141
- ProgramStateRef State ,
3142
- SmallVectorImpl<const MemRegion*> &Out) {
3160
+ static void collectDirectSmartOwningPtrFieldRegions (const MemRegion *Base,
3161
+ QualType RecQT,
3162
+ CheckerContext &C ,
3163
+ SmallVectorImpl<const MemRegion*> &Out) {
3143
3164
if (!Base) return ;
3144
3165
const auto *CRD = RecQT->getAsCXXRecordDecl ();
3145
3166
if (!CRD) return ;
3146
3167
3147
3168
for (const FieldDecl *FD : CRD->fields ()) {
3148
- if (!isUniquePtrType (FD->getType ()))
3169
+ if (!isSmartOwningPtrType (FD->getType ()))
3149
3170
continue ;
3150
- SVal L = State ->getLValue (FD, loc::MemRegionVal (Base));
3171
+ SVal L = C. getState () ->getLValue (FD, loc::MemRegionVal (Base));
3151
3172
if (const MemRegion *FR = L.getAsRegion ())
3152
3173
Out.push_back (FR);
3153
3174
}
@@ -3160,7 +3181,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3160
3181
(*PostFN)(this , C.getState (), Call, C);
3161
3182
}
3162
3183
3163
- SmallVector<const MemRegion*, 8 > UniquePtrFieldRoots ;
3184
+ SmallVector<const MemRegion*, 8 > SmartPtrFieldRoots ;
3164
3185
3165
3186
3166
3187
@@ -3187,17 +3208,17 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3187
3208
isa<CXXBindTemporaryExpr>(AE); // handle CXXBindTemporaryExpr
3188
3209
if (!LooksLikeTemp) continue ;
3189
3210
3190
- // Require at least one direct unique_ptr field by type.
3211
+ // Require at least one direct smart owning pointer field by type.
3191
3212
const auto *CRD = T->getAsCXXRecordDecl ();
3192
3213
if (!CRD) continue ;
3193
- bool HasUPtrField = false ;
3214
+ bool HasSmartPtrField = false ;
3194
3215
for (const FieldDecl *FD : CRD->fields ()) {
3195
- if (isUniquePtrType (FD->getType ())) {
3196
- HasUPtrField = true ;
3216
+ if (isSmartOwningPtrType (FD->getType ())) {
3217
+ HasSmartPtrField = true ;
3197
3218
break ;
3198
3219
}
3199
3220
}
3200
- if (!HasUPtrField ) continue ;
3221
+ if (!HasSmartPtrField ) continue ;
3201
3222
3202
3223
// Find a region for the argument.
3203
3224
SVal VCall = Call.getArgSVal (I);
@@ -3222,21 +3243,21 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3222
3243
continue ;
3223
3244
}
3224
3245
3225
- // Push direct unique_ptr field regions only (precise root set).
3226
- collectDirectUniquePtrFieldRegions (Base, T, C. getState (), UniquePtrFieldRoots );
3246
+ // Push direct smart owning pointer field regions only (precise root set).
3247
+ collectDirectSmartOwningPtrFieldRegions (Base, T, C, SmartPtrFieldRoots );
3227
3248
}
3228
3249
3229
3250
// Escape only from those field roots; do nothing if empty.
3230
- if (!UniquePtrFieldRoots .empty ()) {
3251
+ if (!SmartPtrFieldRoots .empty ()) {
3231
3252
ProgramStateRef State = C.getState ();
3232
- auto Scan = State->scanReachableSymbols <EscapeTrackedCallback>(UniquePtrFieldRoots );
3253
+ auto Scan = State->scanReachableSymbols <EscapeTrackedCallback>(SmartPtrFieldRoots );
3233
3254
ProgramStateRef NewState = Scan.getState ();
3234
3255
if (NewState != State) {
3235
3256
C.addTransition (NewState);
3236
3257
} else {
3237
- // Fallback: if we have by-value record arguments but no unique_ptr fields detected,
3238
- // check if any of the arguments are by-value records with unique_ptr fields
3239
- bool hasByValueRecordWithUniquePtr = false ;
3258
+ // Fallback: if we have by-value record arguments but no smart pointer fields detected,
3259
+ // check if any of the arguments are by-value records with smart pointer fields
3260
+ bool hasByValueRecordWithSmartPtr = false ;
3240
3261
for (unsigned I = 0 , E = Call.getNumArgs (); I != E; ++I) {
3241
3262
const Expr *AE = Call.getArgExpr (I);
3242
3263
if (!AE) continue ;
@@ -3255,20 +3276,20 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3255
3276
isa<CXXBindTemporaryExpr>(AE);
3256
3277
if (!LooksLikeTemp) continue ;
3257
3278
3258
- // Check if this record type has unique_ptr fields
3279
+ // Check if this record type has smart pointer fields
3259
3280
const auto *CRD = T->getAsCXXRecordDecl ();
3260
3281
if (CRD) {
3261
3282
for (const FieldDecl *FD : CRD->fields ()) {
3262
- if (isUniquePtrType (FD->getType ())) {
3263
- hasByValueRecordWithUniquePtr = true ;
3283
+ if (isSmartOwningPtrType (FD->getType ())) {
3284
+ hasByValueRecordWithSmartPtr = true ;
3264
3285
break ;
3265
3286
}
3266
3287
}
3267
3288
}
3268
- if (hasByValueRecordWithUniquePtr ) break ;
3289
+ if (hasByValueRecordWithSmartPtr ) break ;
3269
3290
}
3270
3291
3271
- if (hasByValueRecordWithUniquePtr ) {
3292
+ if (hasByValueRecordWithSmartPtr ) {
3272
3293
ProgramStateRef State = C.getState ();
3273
3294
RegionStateTy RS = State->get <RegionState>();
3274
3295
ProgramStateRef NewState = State;
@@ -3346,17 +3367,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
3346
3367
if (!FD)
3347
3368
return ;
3348
3369
3349
- // If we won't inline this call, conservatively treat by-value record
3350
- // arguments as escaping any tracked pointers they contain.
3351
- const bool WillNotInline = !FD || !FD->hasBody ();
3352
- if (WillNotInline) {
3353
- // TODO: Implement proper escape logic for by-value record arguments
3354
- // The issue is that when a record type is passed by value to a non-inlined
3355
- // function, the analyzer doesn't see the destructor calls for the temporary
3356
- // object, leading to false positive leaks. We need to mark contained
3357
- // pointers as escaped in such cases.
3358
- // For now, just skip this to avoid crashes
3359
- }
3370
+
3360
3371
3361
3372
// FIXME: I suspect we should remove `MallocChecker.isEnabled() &&` because
3362
3373
// it's fishy that the enabled/disabled state of one frontend may influence
0 commit comments