[clang] [analyzer] MallocChecker – Fix false positive leak for smart pointers in temporary objects (PR #152751)
Pavel Skripkin via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 15 05:14:18 PDT 2025
================
@@ -3068,12 +3125,235 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
C.addTransition(state->set<RegionState>(RS), N);
}
+// Allowlist of owning smart pointers we want to recognize.
+// Start with unique_ptr and shared_ptr. (intentionally exclude weak_ptr)
+static bool isSmartOwningPtrType(QualType QT) {
+ QT = QT->getCanonicalTypeUnqualified();
+
+ auto isSmartPtrName = [](StringRef Name) {
+ return Name == "unique_ptr" || Name == "shared_ptr";
+ };
+
+ // First try TemplateSpecializationType (for std smart pointers)
+ if (const auto *TST = QT->getAs<TemplateSpecializationType>()) {
+ const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();
+ if (!TD)
+ return false;
+
+ const auto *ND = dyn_cast_or_null<NamedDecl>(TD->getTemplatedDecl());
+ if (!ND)
+ return false;
+
+ // Check if it's in std namespace
+ if (!isWithinStdNamespace(ND))
+ return false;
+
+ return isSmartPtrName(ND->getName());
+ }
+
+ // Also try RecordType (for custom smart pointer implementations)
+ if (const auto *RD = QT->getAsCXXRecordDecl()) {
+ // Accept any custom unique_ptr or shared_ptr implementation
+ return isSmartPtrName(RD->getName());
+ }
+
+ return false;
+}
+
+static bool hasSmartPtrField(const CXXRecordDecl *CRD) {
+ // Check direct fields
+ if (llvm::any_of(CRD->fields(), [](const FieldDecl *FD) {
+ return isSmartOwningPtrType(FD->getType());
+ }))
+ return true;
+
+ // Check fields from base classes
+ for (const CXXBaseSpecifier &Base : CRD->bases()) {
+ if (const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl()) {
+ if (hasSmartPtrField(BaseDecl))
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool isRvalueByValueRecord(const Expr *AE) {
+ if (AE->isGLValue())
+ return false;
+
+ QualType T = AE->getType();
+ if (!T->isRecordType() || T->isReferenceType())
+ return false;
+
+ // Accept common temp/construct forms but don't overfit.
+ return isa<CXXTemporaryObjectExpr, MaterializeTemporaryExpr, CXXConstructExpr,
+ InitListExpr, ImplicitCastExpr, CXXBindTemporaryExpr>(AE);
+}
+
+static bool isRvalueByValueRecordWithSmartPtr(const Expr *AE) {
+ if (!isRvalueByValueRecord(AE))
+ return false;
+
+ const auto *CRD = AE->getType()->getAsCXXRecordDecl();
+ return CRD && hasSmartPtrField(CRD);
+}
+
+/// Check if a CXXRecordDecl represents a smart owning pointer type.
+static bool isSmartOwningPtrRecord(const CXXRecordDecl *RD) {
+ if (!RD)
+ return false;
+
+ auto isSmartPtrName = [](StringRef Name) {
+ return Name == "unique_ptr" || Name == "shared_ptr";
+ };
+
+ // Check the record name directly
+ if (isSmartPtrName(RD->getName())) {
+ // Accept both std and custom smart pointer implementations
+ return true;
+ }
+
+ return false;
+}
+
+/// Check if a call is a smart pointer constructor call that takes ownership
+/// of pointer arguments.
+static bool isSmartPtrCall(const CallEvent &Call) {
+ // Only check for smart pointer constructor calls
+ if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(Call.getDecl())) {
----------------
pskrgag wrote:
nit: I think, early returns would make function more readable.
https://github.com/llvm/llvm-project/pull/152751
More information about the cfe-commits
mailing list