srcck1.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. ** The program does some simple static analysis of the sqlite3.c source
  3. ** file looking for mistakes.
  4. **
  5. ** Usage:
  6. **
  7. ** ./srcck1 sqlite3.c
  8. **
  9. ** This program looks for instances of assert(), ALWAYS(), NEVER() or
  10. ** testcase() that contain side-effects and reports errors if any such
  11. ** instances are found.
  12. **
  13. ** The aim of this utility is to prevent recurrences of errors such
  14. ** as the one fixed at:
  15. **
  16. ** https://www.sqlite.org/src/info/a2952231ac7abe16
  17. **
  18. ** Note that another similar error was found by this utility when it was
  19. ** first written. That other error was fixed by the same check-in that
  20. ** committed the first version of this utility program.
  21. */
  22. #include <stdlib.h>
  23. #include <ctype.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. /* Read the complete text of a file into memory. Return a pointer to
  27. ** the result. Panic if unable to read the file or allocate memory.
  28. */
  29. static char *readFile(const char *zFilename){
  30. FILE *in;
  31. char *z;
  32. long n;
  33. size_t got;
  34. in = fopen(zFilename, "rb");
  35. if( in==0 ){
  36. fprintf(stderr, "unable to open '%s' for reading\n", zFilename);
  37. exit(1);
  38. }
  39. fseek(in, 0, SEEK_END);
  40. n = ftell(in);
  41. rewind(in);
  42. z = malloc( n+1 );
  43. if( z==0 ){
  44. fprintf(stderr, "cannot allocate %d bytes to store '%s'\n",
  45. (int)(n+1), zFilename);
  46. exit(1);
  47. }
  48. got = fread(z, 1, n, in);
  49. fclose(in);
  50. if( got!=(size_t)n ){
  51. fprintf(stderr, "only read %d of %d bytes from '%s'\n",
  52. (int)got, (int)n, zFilename);
  53. exit(1);
  54. }
  55. z[n] = 0;
  56. return z;
  57. }
  58. /* Check the C code in the argument to see if it might have
  59. ** side effects. The only accurate way to know this is to do a full
  60. ** parse of the C code, which this routine does not do. This routine
  61. ** uses a simple heuristic of looking for:
  62. **
  63. ** * '=' not immediately after '>', '<', '!', or '='.
  64. ** * '++'
  65. ** * '--'
  66. **
  67. ** If the code contains the phrase "side-effects-ok" is inside a
  68. ** comment, then always return false. This is used to disable checking
  69. ** for assert()s with deliberate side-effects, such as used by
  70. ** SQLITE_TESTCTRL_ASSERT - a facility that allows applications to
  71. ** determine at runtime whether or not assert()s are enabled.
  72. ** Obviously, that determination cannot be made unless the assert()
  73. ** has some side-effect.
  74. **
  75. ** Return true if a side effect is seen. Return false if not.
  76. */
  77. static int hasSideEffect(const char *z, unsigned int n){
  78. unsigned int i;
  79. for(i=0; i<n; i++){
  80. if( z[i]=='/' && strncmp(&z[i], "/*side-effects-ok*/", 19)==0 ) return 0;
  81. if( z[i]=='=' && i>0 && z[i-1]!='=' && z[i-1]!='>'
  82. && z[i-1]!='<' && z[i-1]!='!' && z[i+1]!='=' ) return 1;
  83. if( z[i]=='+' && z[i+1]=='+' ) return 1;
  84. if( z[i]=='-' && z[i+1]=='-' ) return 1;
  85. }
  86. return 0;
  87. }
  88. /* Return the number of bytes in string z[] prior to the first unmatched ')'
  89. ** character.
  90. */
  91. static unsigned int findCloseParen(const char *z){
  92. unsigned int nOpen = 0;
  93. unsigned i;
  94. for(i=0; z[i]; i++){
  95. if( z[i]=='(' ) nOpen++;
  96. if( z[i]==')' ){
  97. if( nOpen==0 ) break;
  98. nOpen--;
  99. }
  100. }
  101. return i;
  102. }
  103. /* Search for instances of assert(...), ALWAYS(...), NEVER(...), and/or
  104. ** testcase(...) where the argument contains side effects.
  105. **
  106. ** Print error messages whenever a side effect is found. Return the number
  107. ** of problems seen.
  108. */
  109. static unsigned int findAllSideEffects(const char *z){
  110. unsigned int lineno = 1; /* Line number */
  111. unsigned int i;
  112. unsigned int nErr = 0;
  113. char c, prevC = 0;
  114. for(i=0; (c = z[i])!=0; prevC=c, i++){
  115. if( c=='\n' ){ lineno++; continue; }
  116. if( isalpha(c) && !isalpha(prevC) ){
  117. if( strncmp(&z[i],"assert(",7)==0
  118. || strncmp(&z[i],"ALWAYS(",7)==0
  119. || strncmp(&z[i],"NEVER(",6)==0
  120. || strncmp(&z[i],"testcase(",9)==0
  121. ){
  122. unsigned int n;
  123. const char *z2 = &z[i+5];
  124. while( z2[0]!='(' ){ z2++; }
  125. z2++;
  126. n = findCloseParen(z2);
  127. if( hasSideEffect(z2, n) ){
  128. nErr++;
  129. fprintf(stderr, "side-effect line %u: %.*s\n", lineno,
  130. (int)(&z2[n+1] - &z[i]), &z[i]);
  131. }
  132. }
  133. }
  134. }
  135. return nErr;
  136. }
  137. int main(int argc, char **argv){
  138. char *z;
  139. unsigned int nErr = 0;
  140. if( argc!=2 ){
  141. fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
  142. return 1;
  143. }
  144. z = readFile(argv[1]);
  145. nErr = findAllSideEffects(z);
  146. free(z);
  147. if( nErr ){
  148. fprintf(stderr, "Found %u undesirable side-effects\n", nErr);
  149. return 1;
  150. }
  151. return 0;
  152. }