diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h @@ -22,6 +22,8 @@ /// Returns true if the event call is on smart pointer. bool isStdSmartPtrCall(const CallEvent &Call); +bool isStdSmartPtr(const CXXRecordDecl *RD); +bool isStdSmartPtr(const Expr *E); /// Returns whether the smart pointer is null or not. bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion); diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -81,6 +81,20 @@ REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) +// Checks if RD has name in Names and is in std namespace +bool hasStdClassWithName(const CXXRecordDecl *RD, + const SmallVectorImpl &Names) { + if (!RD || !RD->getDeclContext()->isStdNamespace()) + return false; + if (RD->getDeclName().isIdentifier()) { + StringRef Name = RD->getName(); + return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool { + return Name == GivenName; + }); + } + return false; +} + // Define the inter-checker API. namespace clang { namespace ento { @@ -89,16 +103,16 @@ const auto *MethodDecl = dyn_cast_or_null(Call.getDecl()); if (!MethodDecl || !MethodDecl->getParent()) return false; + return isStdSmartPtr(MethodDecl->getParent()); +} - const auto *RecordDecl = MethodDecl->getParent(); - if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) - return false; +bool isStdSmartPtr(const CXXRecordDecl *RD) { + return hasStdClassWithName( + RD, SmallVector{"shared_ptr", "unique_ptr", "weak_ptr"}); +} - if (RecordDecl->getDeclName().isIdentifier()) { - StringRef Name = RecordDecl->getName(); - return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; - } - return false; +bool isStdSmartPtr(const Expr *E) { + return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); } bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { @@ -175,9 +189,35 @@ return CD && CD->getConversionType()->isBooleanType(); } +bool isStdBasicOstream(const Expr *E) { + const auto *RD = E->getType()->getAsCXXRecordDecl(); + return hasStdClassWithName(RD, SmallVector{"basic_ostream"}); +} + +bool isStdOstreamOperatorCall(const CallEvent &Call) { + if (Call.getNumArgs() != 2 || + !Call.getDecl()->getDeclContext()->isStdNamespace()) + return false; + const auto *FC = dyn_cast(&Call); + if (!FC) + return false; + const FunctionDecl *FD = FC->getDecl(); + if (!FD->isOverloadedOperator()) + return false; + const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); + if (OOK != clang::OO_LessLess) + return false; + return smartptr::isStdSmartPtr(Call.getArgExpr(1)) && + isStdBasicOstream(Call.getArgExpr(0)); +} + bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); + + if (isStdOstreamOperatorCall(Call)) + return true; + if (!smartptr::isStdSmartPtrCall(Call)) return false;