52
52
#include " clang/AST/DeclTemplate.h"
53
53
#include " clang/AST/Expr.h"
54
54
#include " clang/AST/ExprCXX.h"
55
- #include " clang/AST/TemplateBase.h"
56
- #include " clang/AST/Type.h"
57
55
58
56
#include " clang/AST/ParentMap.h"
59
57
#include " clang/ASTMatchers/ASTMatchFinder.h"
@@ -1113,17 +1111,23 @@ class StopTrackingCallback final : public SymbolVisitor {
1113
1111
// / The visitor traverses reachable symbols from a given set of memory regions
1114
1112
// / (typically smart pointer field regions) and marks any allocated symbols as
1115
1113
// / escaped. Escaped symbols are not reported as leaks by checkDeadSymbols.
1116
- // /
1117
- // / Usage:
1118
- // / auto Scan =
1119
- // / State->scanReachableSymbols<EscapeTrackedCallback>(RootRegions);
1120
- // / ProgramStateRef NewState = Scan.getState();
1121
- // / if (NewState != State) C.addTransition(NewState);
1122
1114
class EscapeTrackedCallback final : public SymbolVisitor {
1123
1115
ProgramStateRef State;
1124
1116
1125
- public:
1126
1117
explicit EscapeTrackedCallback (ProgramStateRef S) : State(std::move(S)) {}
1118
+
1119
+ public:
1120
+ // / Escape tracked regions reachable from the given roots.
1121
+ static ProgramStateRef
1122
+ EscapeTrackedRegionsReachableFrom (ArrayRef<const MemRegion *> Roots,
1123
+ ProgramStateRef State) {
1124
+ EscapeTrackedCallback Visitor (State);
1125
+ for (const MemRegion *R : Roots) {
1126
+ State->scanReachableSymbols (loc::MemRegionVal (R), Visitor);
1127
+ }
1128
+ return Visitor.getState ();
1129
+ }
1130
+
1127
1131
ProgramStateRef getState () const { return State; }
1128
1132
1129
1133
bool VisitSymbol (SymbolRef Sym) override {
@@ -3108,18 +3112,11 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
3108
3112
}
3109
3113
3110
3114
static QualType canonicalStrip (QualType QT) {
3111
- return QT. getCanonicalType (). getUnqualifiedType ();
3115
+ return QT-> getCanonicalTypeUnqualified ();
3112
3116
}
3113
3117
3114
- static bool isInStdNamespace (const DeclContext *DC) {
3115
- while (DC) {
3116
- if (const auto *NS = dyn_cast<NamespaceDecl>(DC))
3117
- if (NS->isStdNamespace ())
3118
- return true ;
3119
- DC = DC->getParent ();
3120
- }
3121
- return false ;
3122
- }
3118
+ // Use isWithinStdNamespace from CheckerHelpers.h instead of custom
3119
+ // implementation
3123
3120
3124
3121
// Allowlist of owning smart pointers we want to recognize.
3125
3122
// Start with unique_ptr and shared_ptr. (intentionally exclude weak_ptr)
@@ -3138,8 +3135,7 @@ static bool isSmartOwningPtrType(QualType QT) {
3138
3135
return false ;
3139
3136
3140
3137
// Check if it's in std namespace
3141
- const DeclContext *DC = ND->getDeclContext ();
3142
- if (!isInStdNamespace (DC))
3138
+ if (!isWithinStdNamespace (ND))
3143
3139
return false ;
3144
3140
3145
3141
StringRef Name = ND->getName ();
@@ -3162,6 +3158,44 @@ static bool isSmartOwningPtrType(QualType QT) {
3162
3158
return false ;
3163
3159
}
3164
3160
3161
+ static bool hasSmartPtrField (const CXXRecordDecl *CRD) {
3162
+ return llvm::any_of (CRD->fields (), [](const FieldDecl *FD) {
3163
+ return isSmartOwningPtrType (FD->getType ());
3164
+ });
3165
+ }
3166
+
3167
+ static bool isRvalueByValueRecord (const Expr *AE) {
3168
+ if (AE->isGLValue ())
3169
+ return false ;
3170
+
3171
+ QualType T = AE->getType ();
3172
+ if (!T->isRecordType () || T->isReferenceType ())
3173
+ return false ;
3174
+
3175
+ // Accept common temp/construct forms but don't overfit.
3176
+ return isa<CXXTemporaryObjectExpr, MaterializeTemporaryExpr, CXXConstructExpr,
3177
+ InitListExpr, ImplicitCastExpr, CXXBindTemporaryExpr>(AE);
3178
+ }
3179
+
3180
+ static bool isRvalueByValueRecordWithSmartPtr (const Expr *AE) {
3181
+ if (!isRvalueByValueRecord (AE))
3182
+ return false ;
3183
+
3184
+ const auto *CRD = AE->getType ()->getAsCXXRecordDecl ();
3185
+ return CRD && hasSmartPtrField (CRD);
3186
+ }
3187
+
3188
+ static ProgramStateRef escapeAllAllocatedSymbols (ProgramStateRef State) {
3189
+ RegionStateTy RS = State->get <RegionState>();
3190
+ ProgramStateRef NewState = State;
3191
+ for (auto [Sym, RefSt] : RS) {
3192
+ if (RefSt.isAllocated () || RefSt.isAllocatedOfSizeZero ()) {
3193
+ NewState = NewState->set <RegionState>(Sym, RefState::getEscaped (&RefSt));
3194
+ }
3195
+ }
3196
+ return NewState;
3197
+ }
3198
+
3165
3199
static void collectDirectSmartOwningPtrFieldRegions (
3166
3200
const MemRegion *Base, QualType RecQT, CheckerContext &C,
3167
3201
SmallVectorImpl<const MemRegion *> &Out) {
@@ -3195,38 +3229,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3195
3229
continue ;
3196
3230
AE = AE->IgnoreParenImpCasts ();
3197
3231
3198
- QualType T = AE->getType ();
3199
-
3200
- // **Relaxation 1**: accept *any rvalue* by-value record (not only strict
3201
- // PRVALUE).
3202
- if (AE->isGLValue ())
3203
- continue ;
3204
-
3205
- // By-value record only (no refs).
3206
- if (!T->isRecordType () || T->isReferenceType ())
3207
- continue ;
3208
-
3209
- // **Relaxation 2**: accept common temp/construct forms but don't overfit.
3210
- const bool LooksLikeTemp =
3211
- isa<CXXTemporaryObjectExpr>(AE) || isa<MaterializeTemporaryExpr>(AE) ||
3212
- isa<CXXConstructExpr>(AE) || isa<InitListExpr>(AE) ||
3213
- isa<ImplicitCastExpr>(AE) || // handle common rvalue materializations
3214
- isa<CXXBindTemporaryExpr>(AE); // handle CXXBindTemporaryExpr
3215
- if (!LooksLikeTemp)
3216
- continue ;
3217
-
3218
- // Require at least one direct smart owning pointer field by type.
3219
- const auto *CRD = T->getAsCXXRecordDecl ();
3220
- if (!CRD)
3221
- continue ;
3222
- bool HasSmartPtrField = false ;
3223
- for (const FieldDecl *FD : CRD->fields ()) {
3224
- if (isSmartOwningPtrType (FD->getType ())) {
3225
- HasSmartPtrField = true ;
3226
- break ;
3227
- }
3228
- }
3229
- if (!HasSmartPtrField)
3232
+ if (!isRvalueByValueRecordWithSmartPtr (AE))
3230
3233
continue ;
3231
3234
3232
3235
// Find a region for the argument.
@@ -3237,32 +3240,26 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3237
3240
3238
3241
const MemRegion *Base = RCall ? RCall : RExpr;
3239
3242
if (!Base) {
3240
- // Fallback: if we have a by-value record with unique_ptr fields but no
3243
+ // Fallback: if we have a by-value record with smart pointer fields but no
3241
3244
// region, mark all allocated symbols as escaped
3242
3245
ProgramStateRef State = C.getState ();
3243
- RegionStateTy RS = State->get <RegionState>();
3244
- ProgramStateRef NewState = State;
3245
- for (auto [Sym, RefSt] : RS) {
3246
- if (RefSt.isAllocated () || RefSt.isAllocatedOfSizeZero ()) {
3247
- NewState =
3248
- NewState->set <RegionState>(Sym, RefState::getEscaped (&RefSt));
3249
- }
3250
- }
3246
+ ProgramStateRef NewState = escapeAllAllocatedSymbols (State);
3251
3247
if (NewState != State)
3252
3248
C.addTransition (NewState);
3253
3249
continue ;
3254
3250
}
3255
3251
3256
3252
// Push direct smart owning pointer field regions only (precise root set).
3257
- collectDirectSmartOwningPtrFieldRegions (Base, T, C, SmartPtrFieldRoots);
3253
+ collectDirectSmartOwningPtrFieldRegions (Base, AE->getType (), C,
3254
+ SmartPtrFieldRoots);
3258
3255
}
3259
3256
3260
3257
// Escape only from those field roots; do nothing if empty.
3261
3258
if (!SmartPtrFieldRoots.empty ()) {
3262
3259
ProgramStateRef State = C.getState ();
3263
- auto Scan =
3264
- State-> scanReachableSymbols < EscapeTrackedCallback>(SmartPtrFieldRoots);
3265
- ProgramStateRef NewState = Scan. getState ( );
3260
+ ProgramStateRef NewState =
3261
+ EscapeTrackedCallback::EscapeTrackedRegionsReachableFrom (
3262
+ SmartPtrFieldRoots, State );
3266
3263
if (NewState != State) {
3267
3264
C.addTransition (NewState);
3268
3265
} else {
@@ -3276,44 +3273,15 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
3276
3273
continue ;
3277
3274
AE = AE->IgnoreParenImpCasts ();
3278
3275
3279
- if (AE->isGLValue ())
3280
- continue ;
3281
- QualType T = AE->getType ();
3282
- if (!T->isRecordType () || T->isReferenceType ())
3283
- continue ;
3284
-
3285
- const bool LooksLikeTemp =
3286
- isa<CXXTemporaryObjectExpr>(AE) ||
3287
- isa<MaterializeTemporaryExpr>(AE) || isa<CXXConstructExpr>(AE) ||
3288
- isa<InitListExpr>(AE) || isa<ImplicitCastExpr>(AE) ||
3289
- isa<CXXBindTemporaryExpr>(AE);
3290
- if (!LooksLikeTemp)
3291
- continue ;
3292
-
3293
- // Check if this record type has smart pointer fields
3294
- const auto *CRD = T->getAsCXXRecordDecl ();
3295
- if (CRD) {
3296
- for (const FieldDecl *FD : CRD->fields ()) {
3297
- if (isSmartOwningPtrType (FD->getType ())) {
3298
- hasByValueRecordWithSmartPtr = true ;
3299
- break ;
3300
- }
3301
- }
3302
- }
3303
- if (hasByValueRecordWithSmartPtr)
3276
+ if (isRvalueByValueRecordWithSmartPtr (AE)) {
3277
+ hasByValueRecordWithSmartPtr = true ;
3304
3278
break ;
3279
+ }
3305
3280
}
3306
3281
3307
3282
if (hasByValueRecordWithSmartPtr) {
3308
3283
ProgramStateRef State = C.getState ();
3309
- RegionStateTy RS = State->get <RegionState>();
3310
- ProgramStateRef NewState = State;
3311
- for (auto [Sym, RefSt] : RS) {
3312
- if (RefSt.isAllocated () || RefSt.isAllocatedOfSizeZero ()) {
3313
- NewState =
3314
- NewState->set <RegionState>(Sym, RefState::getEscaped (&RefSt));
3315
- }
3316
- }
3284
+ ProgramStateRef NewState = escapeAllAllocatedSymbols (State);
3317
3285
if (NewState != State)
3318
3286
C.addTransition (NewState);
3319
3287
}
@@ -3439,7 +3407,6 @@ void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,
3439
3407
if (!Sym)
3440
3408
// If we are returning a field of the allocated struct or an array element,
3441
3409
// the callee could still free the memory.
3442
- // TODO: This logic should be a part of generic symbol escape callback.
3443
3410
if (const MemRegion *MR = RetVal.getAsRegion ())
3444
3411
if (isa<FieldRegion, ElementRegion>(MR))
3445
3412
if (const SymbolicRegion *BMR =
0 commit comments