]> granicus.if.org Git - clang/commitdiff
[analyzer] C++ objects returned on the stack may be wrapped in ExprWithCleanups.
authorJordan Rose <jordan_rose@apple.com>
Wed, 29 Aug 2012 01:11:59 +0000 (01:11 +0000)
committerJordan Rose <jordan_rose@apple.com>
Wed, 29 Aug 2012 01:11:59 +0000 (01:11 +0000)
In C++, objects being returned on the stack are actually copy-constructed into
the return value. That means that when a temporary is returned, it still has
to be destroyed, i.e. the returned expression will be wrapped in an
ExprWithCleanups node. Our "returning stack memory" checker needs to look
through this node to see if we really are returning an object by value.

PR13722

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162817 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
test/Analysis/temporaries.cpp [new file with mode: 0644]

index b1f4f623e29b00e2d7f54aeef407accf09a787aa..6a4b2d9ef8fbea2a09237e25a5a7dbb191b9c191 100644 (file)
@@ -118,6 +118,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
   const Expr *RetE = RS->getRetValue();
   if (!RetE)
     return;
+  RetE = RetE->IgnoreParens();
 
   const LocationContext *LCtx = C.getLocationContext();
   SVal V = C.getState()->getSVal(RetE, LCtx);
@@ -144,7 +145,10 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
     return;
 
   // Returning a record by value is fine. (In this case, the returned
-  // expression will be a copy-constructor.)
+  // expression will be a copy-constructor, possibly wrapped in an
+  // ExprWithCleanups node.)
+  if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
+    RetE = Cleanup->getSubExpr();
   if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
     return;
 
diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp
new file mode 100644 (file)
index 0000000..df1ab5a
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -verify -w %s
+
+struct Trivial {
+  Trivial(int x) : value(x) {}
+  int value;
+};
+
+struct NonTrivial : public Trivial {
+  NonTrivial(int x) : Trivial(x) {}
+  ~NonTrivial();
+};
+
+
+Trivial getTrivial() {
+  return Trivial(42); // no-warning
+}
+
+const Trivial &getTrivialRef() {
+  return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'struct Trivial' returned to caller}}
+}
+
+
+NonTrivial getNonTrivial() {
+  return NonTrivial(42); // no-warning
+}
+
+const NonTrivial &getNonTrivialRef() {
+  return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'struct NonTrivial' returned to caller}}
+}
+