RedDocMD updated this revision to Diff 357724.
RedDocMD added a comment.
Little refactors, one more test
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D105421/new/
https://reviews.llvm.org/D105421
Files:
clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
clang/test/Analysis/Inputs/system-header-simulator-cxx.h
clang/test/Analysis/smart-ptr.cpp
Index: clang/test/Analysis/smart-ptr.cpp
===================================================================
--- clang/test/Analysis/smart-ptr.cpp
+++ clang/test/Analysis/smart-ptr.cpp
@@ -3,6 +3,11 @@
// RUN: -analyzer-config cplusplus.SmartPtrModeling:ModelSmartPtrDereference=true\
// RUN: -std=c++11 -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection\
+// RUN: -analyzer-checker cplusplus.Move,alpha.cplusplus.SmartPtr\
+// RUN: -analyzer-config cplusplus.SmartPtrModeling:ModelSmartPtrDereference=true\
+// RUN: -std=c++20 -verify %s
+
#include "Inputs/system-header-simulator-cxx.h"
void clang_analyzer_warnIfReached();
@@ -457,3 +462,28 @@
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
}
}
+
+#if __cplusplus >= 202002L
+
+void testOstreamOverload(std::unique_ptr<int> P) {
+ auto &Cout = std::cout;
+ auto &PtrCout = std::cout << P;
+ auto &StringCout = std::cout << "hello";
+ // We are testing the fact that in our modelling of
+ // operator<<(basic_ostream<T1> &, const unique_ptr<T2> &)
+ // we set the return SVal to the SVal of the ostream arg.
+ clang_analyzer_eval(&Cout == &PtrCout); // expected-warning {{TRUE}}
+ // FIXME: Technically, they should be equal,
+ // that hasn't been modelled yet.
+ clang_analyzer_eval(&Cout == &StringCout); // expected-warning {{UNKNOWN}}
+}
+
+int glob;
+void testOstreamDoesntInvalidateGlobals(std::unique_ptr<int> P) {
+ int x = glob;
+ std::cout << P;
+ int y = glob;
+ clang_analyzer_eval(x == y); // expected-warning {{TRUE}}
+}
+
+#endif
Index: clang/test/Analysis/Inputs/system-header-simulator-cxx.h
===================================================================
--- clang/test/Analysis/Inputs/system-header-simulator-cxx.h
+++ clang/test/Analysis/Inputs/system-header-simulator-cxx.h
@@ -981,6 +981,22 @@
} // namespace std
#endif
+namespace std {
+template <class CharT>
+class basic_ostream;
+
+using ostream = basic_ostream<char>;
+
+extern std::ostream cout;
+
+ostream &operator<<(ostream &, const string &);
+
+#if __cplusplus >= 202002L
+template <class T>
+ostream &operator<<(ostream &, const std::unique_ptr<T> &);
+#endif
+} // namespace std
+
#ifdef TEST_INLINABLE_ALLOCATORS
namespace std {
void *malloc(size_t);
Index: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -68,6 +68,7 @@
bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
const MemRegion *OtherSmartPtrRegion) const;
void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
+ bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
using SmartPtrMethodHandlerFn =
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
@@ -81,6 +82,31 @@
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
+// Checks if RD has name in Names and is in std namespace
+static bool hasStdClassWithName(const CXXRecordDecl *RD,
+ const ArrayRef<StringRef> &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;
+}
+
+constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
+ "weak_ptr"};
+
+static bool isStdSmartPtr(const CXXRecordDecl *RD) {
+ return hasStdClassWithName(RD, {STD_PTR_NAMES, 3});
+}
+
+static bool isStdSmartPtr(const Expr *E) {
+ return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
+}
+
// Define the inter-checker API.
namespace clang {
namespace ento {
@@ -89,16 +115,7 @@
const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
if (!MethodDecl || !MethodDecl->getParent())
return false;
-
- const auto *RecordDecl = MethodDecl->getParent();
- if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
- return false;
-
- if (RecordDecl->getDeclName().isIdentifier()) {
- StringRef Name = RecordDecl->getName();
- return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
- }
- return false;
+ return isStdSmartPtr(MethodDecl->getParent());
}
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
@@ -175,9 +192,37 @@
return CD && CD->getConversionType()->isBooleanType();
}
+constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
+
+bool isStdBasicOstream(const Expr *E) {
+ const auto *RD = E->getType()->getAsCXXRecordDecl();
+ return hasStdClassWithName(RD, {BASIC_OSTREAM_NAMES, 1});
+}
+
+bool isStdOstreamOperatorCall(const CallEvent &Call) {
+ if (Call.getNumArgs() != 2 ||
+ !Call.getDecl()->getDeclContext()->isStdNamespace())
+ return false;
+ const auto *FC = dyn_cast<SimpleFunctionCall>(&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 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 handleOstreamOperator(Call, C);
+
if (!smartptr::isStdSmartPtrCall(Call))
return false;
@@ -272,6 +317,30 @@
return C.isDifferent();
}
+bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
+ CheckerContext &C) const {
+ // operator<< does not modify the smart pointer.
+ // And we don't really have much of modelling of basic_ostream.
+ // So, we are better off:
+ // 1) Invalidating the mem-region of the ostream object at hand.
+ // 2) Setting the SVal of the basic_ostream as the return value.
+ // Not very satisfying, but it gets the job done, and is better
+ // than the default handling. :)
+
+ ProgramStateRef State = C.getState();
+ const auto StreamVal = Call.getArgSVal(0);
+ const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
+ if (!StreamThisRegion)
+ return false;
+ State =
+ State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
+ C.blockCount(), C.getLocationContext(), false);
+ State =
+ State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
+ C.addTransition(State);
+ return true;
+}
+
void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits