diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp --- a/clang/test/Analysis/NewDeleteLeaks.cpp +++ b/clang/test/Analysis/NewDeleteLeaks.cpp @@ -192,3 +192,29 @@ // expected-note@-1 {{Potential leak of memory pointed to by 'v'}} } // namespace refkind_from_unoallocated_to_allocated + +// Check that memory leak is reported against a symbol if the last place it's +// mentioned is a base region of a lazy compound value, as the program cannot +// possibly free that memory. +namespace symbol_reaper_lifetime { +struct Nested { + int buf[2]; +}; +struct Wrapping { + Nested data; +}; + +Nested allocateWrappingAndReturnNested() { + // expected-note@+1 {{Memory is allocated}} + Wrapping const* p = new Wrapping(); + // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}} + // expected-note@+1 {{Potential leak of memory pointed to by 'p'}} + return p->data; +} + +void caller() { + // expected-note@+1 {{Calling 'allocateWrappingAndReturnNested'}} + Nested n = allocateWrappingAndReturnNested(); + (void)n; +} // no-warning: No potential memory leak here, because that's been already reported. +} // namespace symbol_reaper_lifetime diff --git a/clang/test/Analysis/symbol-reaper-lambda.cpp b/clang/test/Analysis/symbol-reaper-lambda.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/symbol-reaper-lambda.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s +// expected-no-diagnostics + +template +void escape(Ts&...); +struct Dummy {}; + +int strange(Dummy param) { + Dummy local_pre_lambda; + int ref_captured = 0; + + // LambdaExpr is modeled as lazyCompoundVal of tempRegion, that contains + // all captures. In this instance, this region contains a pointer/reference + // to ref_captured variable. + auto fn = [&] { + escape(param, local_pre_lambda); + return ref_captured; // no-warning: The value is not garbage. + }; + + int local_defined_after_lambda; // Unused, but necessary! Important that it's before the call. + + // The ref_captured binding should not be pruned at this point, as it is still + // accessed via reference captured in operator() of fn. + return fn(); +} + diff --git a/clang/test/Analysis/trivial-copy-struct.cpp b/clang/test/Analysis/trivial-copy-struct.cpp --- a/clang/test/Analysis/trivial-copy-struct.cpp +++ b/clang/test/Analysis/trivial-copy-struct.cpp @@ -1,6 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s template void clang_analyzer_dump(T); +template void clang_analyzer_value(T); void clang_analyzer_warnIfReached(); struct Node { int* ptr; }; @@ -34,3 +35,25 @@ (void)(n1->ptr); (void)(n2->ptr); } + +struct List { + List* next; + int value; + int padding; +}; + +void deadCode(List orig) { + List c = orig; + clang_analyzer_dump(c.value); + // expected-warning-re@-1 {{reg_${{[0-9]+}}}} + if (c.value == 42) + return; + clang_analyzer_value(c.value); + // expected-warning@-1 {{32s:{ [-2147483648, 2147483647] }}} + // The symbol was garbage collected too early, hence we lose the constraints. + if (c.value != 42) + return; + + // Dead code should be unreachable + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} +}