// RUN: %clang_analyze_cc1 -verify %s -analyzer-output=text \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=unix.Stream #include "Inputs/system-header-simulator.h" char *logDump(); bool coin(); [[noreturn]] void halt(); void assert(bool b) { if (!b) halt(); } //===----------------------------------------------------------------------===// // Report for which we expect NoOwnershipChangeVisitor to add a new note. //===----------------------------------------------------------------------===// namespace stream_opened_in_fn_call { // TODO: AST analysis of sink would reveal that it doesn't intent to free the // allocated memory, but in this instance, its also the only function with // the ability to do so, we should see a note here. void sink(FILE *f) { } void f() { sink(fopen("input.txt", "w")); // expected-note@-1{{Stream opened here}} } // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } // namespace stream_opened_in_fn_call namespace stream_passed_to_fn_call { void expectedClose(FILE *f) { if (char *log = logDump()) { // expected-note{{Assuming 'log' is null}} // expected-note@-1{{Taking false branch}} printf("%s", log); fclose(f); } } // expected-note{{Returning without closing stream object or storing it for later release}} void f() { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} if (!f) // expected-note{{'f' is non-null}} // expected-note@-1{{Taking false branch}} return; if (coin()) { // expected-note{{Assuming the condition is true}} // expected-note@-1{{Taking true branch}} expectedClose(f); // expected-note{{Calling 'expectedClose'}} // expected-note@-1{{Returning from 'expectedClose'}} return; // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } fclose(f); } } // namespace stream_passed_to_fn_call namespace stream_shared_with_ptr_of_shorter_lifetime { void sink(FILE *f) { FILE *Q = f; if (coin()) // expected-note {{Assuming the condition is false}} // expected-note@-1 {{Taking false branch}} fclose(f); (void)Q; } // expected-note{{Returning without closing stream object or storing it for later release}} void foo() { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} if (!f) // expected-note{{'f' is non-null}} // expected-note@-1{{Taking false branch}} return; sink(f); // expected-note {{Calling 'sink'}} // expected-note@-1 {{Returning from 'sink'}} } // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } // namespace stream_shared_with_ptr_of_shorter_lifetime //===----------------------------------------------------------------------===// // Report for which we *do not* expect NoOwnershipChangeVisitor add a new note, // nor do we want it to. //===----------------------------------------------------------------------===// namespace stream_not_passed_to_fn_call { void expectedClose(FILE *f) { if (char *log = logDump()) { printf("%s", log); fclose(f); } } void f(FILE *p) { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} if (!f) // expected-note{{'f' is non-null}} // expected-note@-1{{Taking false branch}} return; expectedClose(p); // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } } // namespace stream_not_passed_to_fn_call namespace stream_shared_with_ptr_of_same_lifetime { void expectedClose(FILE *f, FILE **p) { // NOTE: Not a job of NoOwnershipChangeVisitor, but maybe this could be // highlighted still? *p = f; } void f() { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} FILE *p = NULL; if (!f) // expected-note{{'f' is non-null}} // expected-note@-1{{Taking false branch}} return; expectedClose(f, &p); } // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } // namespace stream_shared_with_ptr_of_same_lifetime namespace stream_passed_into_fn_that_doesnt_intend_to_free { void expectedClose(FILE *f) { } void f() { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} if (!f) // expected-note{{'f' is non-null}} // expected-note@-1{{Taking false branch}} return; expectedClose(f); } // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } // namespace stream_passed_into_fn_that_doesnt_intend_to_free namespace stream_passed_into_fn_that_doesnt_intend_to_free2 { void bar(); void expectedClose(FILE *f) { // Correctly realize that calling bar() doesn't mean that this function would // like to deallocate anything. bar(); } void f() { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} if (!f) // expected-note{{'f' is non-null}} // expected-note@-1{{Taking false branch}} return; expectedClose(f); } // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } // namespace stream_passed_into_fn_that_doesnt_intend_to_free2 namespace streamstate_from_closed_to_open { // StreamState of the symbol changed from nothing to Allocated. We don't want to // emit notes when the RefKind changes in the stack frame. static FILE *fopenWrapper() { FILE *f = fopen("input.txt", "w"); // expected-note{{Stream opened here}} assert(f); return f; } void use_ret() { FILE *v; v = fopenWrapper(); // expected-note {{Calling 'fopenWrapper'}} // expected-note@-1{{Returning from 'fopenWrapper'}} } // expected-warning{{Opened stream never closed. Potential resource leak [unix.Stream]}} // expected-note@-1{{Opened stream never closed. Potential resource leak}} } // namespace streamstate_from_closed_to_open