| // RUN: %clang_analyze_cc1 -verify %s \ |
| // RUN: -analyzer-checker=core \ |
| // RUN: -analyzer-checker=alpha.unix.Stream \ |
| // RUN: -analyzer-checker=debug.StreamTester \ |
| // RUN: -analyzer-checker=debug.ExprInspection |
| |
| #include "Inputs/system-header-simulator.h" |
| |
| void clang_analyzer_eval(int); |
| void clang_analyzer_warnIfReached(); |
| void StreamTesterChecker_make_feof_stream(FILE *); |
| void StreamTesterChecker_make_ferror_stream(FILE *); |
| |
| void error_fopen() { |
| FILE *F = fopen("file", "r"); |
| if (!F) |
| return; |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| fclose(F); |
| } |
| |
| void error_freopen() { |
| FILE *F = fopen("file", "r"); |
| if (!F) |
| return; |
| F = freopen(0, "w", F); |
| if (!F) |
| return; |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| fclose(F); |
| } |
| |
| void stream_error_feof() { |
| FILE *F = fopen("file", "r"); |
| if (!F) |
| return; |
| StreamTesterChecker_make_feof_stream(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| clearerr(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| fclose(F); |
| } |
| |
| void stream_error_ferror() { |
| FILE *F = fopen("file", "r"); |
| if (!F) |
| return; |
| StreamTesterChecker_make_ferror_stream(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} |
| clearerr(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| fclose(F); |
| } |
| |
| void error_fread() { |
| FILE *F = tmpfile(); |
| if (!F) |
| return; |
| char Buf[10]; |
| int Ret = fread(Buf, 1, 10, F); |
| if (Ret == 10) { |
| clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} |
| } else { |
| clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{TRUE}} |
| if (feof(F)) { |
| clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} |
| fread(Buf, 1, 10, F); // expected-warning {{Read function called when stream is in EOF state}} |
| clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| } |
| if (ferror(F)) { |
| clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} |
| fread(Buf, 1, 10, F); // expected-warning {{might be 'indeterminate'}} |
| } |
| } |
| fclose(F); |
| Ret = fread(Buf, 1, 10, F); // expected-warning {{Stream might be already closed}} |
| } |
| |
| void error_fwrite() { |
| FILE *F = tmpfile(); |
| if (!F) |
| return; |
| const char *Buf = "123456789"; |
| int Ret = fwrite(Buf, 1, 10, F); |
| if (Ret == 10) { |
| clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} |
| } else { |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} |
| fwrite(0, 1, 10, F); // expected-warning {{might be 'indeterminate'}} |
| } |
| fclose(F); |
| Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} |
| } |
| |
| void freadwrite_zerosize(FILE *F) { |
| fwrite(0, 1, 0, F); |
| fwrite(0, 0, 1, F); |
| fread(0, 1, 0, F); |
| fread(0, 0, 1, F); |
| } |
| |
| void freadwrite_zerosize_eofstate(FILE *F) { |
| fwrite(0, 1, 0, F); |
| fwrite(0, 0, 1, F); |
| fread(0, 1, 0, F); // expected-warning {{Read function called when stream is in EOF state}} |
| fread(0, 0, 1, F); // expected-warning {{Read function called when stream is in EOF state}} |
| } |
| |
| void error_fread_fwrite_zerosize() { |
| FILE *F = fopen("file", "r"); |
| if (!F) |
| return; |
| |
| freadwrite_zerosize(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| |
| StreamTesterChecker_make_ferror_stream(F); |
| freadwrite_zerosize(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} |
| |
| StreamTesterChecker_make_feof_stream(F); |
| freadwrite_zerosize_eofstate(F); |
| clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| |
| fclose(F); |
| } |
| |
| void error_fseek() { |
| FILE *F = fopen("file", "r"); |
| if (!F) |
| return; |
| int rc = fseek(F, 0, SEEK_SET); |
| if (rc) { |
| int IsFEof = feof(F), IsFError = ferror(F); |
| // Get feof or ferror or no error. |
| clang_analyzer_eval(IsFEof || IsFError); |
| // expected-warning@-1 {{FALSE}} |
| // expected-warning@-2 {{TRUE}} |
| clang_analyzer_eval(IsFEof && IsFError); // expected-warning {{FALSE}} |
| // Error flags should not change. |
| if (IsFEof) |
| clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} |
| else |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| if (IsFError) |
| clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} |
| else |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| } else { |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| // Error flags should not change. |
| clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} |
| clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} |
| } |
| fclose(F); |
| } |
| |
| void error_indeterminate() { |
| FILE *F = fopen("file", "r+"); |
| if (!F) |
| return; |
| const char *Buf = "123456789"; |
| int rc = fseek(F, 0, SEEK_SET); |
| if (rc) { |
| if (feof(F)) { |
| fwrite(Buf, 1, 10, F); // no warning |
| } else if (ferror(F)) { |
| fwrite(Buf, 1, 10, F); // expected-warning {{might be 'indeterminate'}} |
| } else { |
| fwrite(Buf, 1, 10, F); // expected-warning {{might be 'indeterminate'}} |
| } |
| } |
| fclose(F); |
| } |
| |
| void error_indeterminate_clearerr() { |
| FILE *F = fopen("file", "r+"); |
| if (!F) |
| return; |
| const char *Buf = "123456789"; |
| int rc = fseek(F, 0, SEEK_SET); |
| if (rc) { |
| if (feof(F)) { |
| clearerr(F); |
| fwrite(Buf, 1, 10, F); // no warning |
| } else if (ferror(F)) { |
| clearerr(F); |
| fwrite(Buf, 1, 10, F); // expected-warning {{might be 'indeterminate'}} |
| } else { |
| clearerr(F); |
| fwrite(Buf, 1, 10, F); // expected-warning {{might be 'indeterminate'}} |
| } |
| } |
| fclose(F); |
| } |
| |
| void error_indeterminate_feof1() { |
| FILE *F = fopen("file", "r+"); |
| if (!F) |
| return; |
| char Buf[10]; |
| if (fread(Buf, 1, 10, F) < 10) { |
| if (feof(F)) { |
| // error is feof, should be non-indeterminate |
| fwrite("1", 1, 1, F); // no warning |
| } |
| } |
| fclose(F); |
| } |
| |
| void error_indeterminate_feof2() { |
| FILE *F = fopen("file", "r+"); |
| if (!F) |
| return; |
| char Buf[10]; |
| if (fread(Buf, 1, 10, F) < 10) { |
| if (ferror(F) == 0) { |
| // error is feof, should be non-indeterminate |
| fwrite("1", 1, 1, F); // no warning |
| } |
| } |
| fclose(F); |
| } |