Index: third_party/sqlite/src/tool/srcck1.c |
diff --git a/third_party/sqlite/src/tool/srcck1.c b/third_party/sqlite/src/tool/srcck1.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..20084ac47f1a0793c2bd9f6c511ab7da0e34e544 |
--- /dev/null |
+++ b/third_party/sqlite/src/tool/srcck1.c |
@@ -0,0 +1,158 @@ |
+/* |
+** The program does some simple static analysis of the sqlite3.c source |
+** file looking for mistakes. |
+** |
+** Usage: |
+** |
+** ./srcck1 sqlite3.c |
+** |
+** This program looks for instances of assert(), ALWAYS(), NEVER() or |
+** testcase() that contain side-effects and reports errors if any such |
+** instances are found. |
+** |
+** The aim of this utility is to prevent recurrences of errors such |
+** as the one fixed at: |
+** |
+** https://www.sqlite.org/src/info/a2952231ac7abe16 |
+** |
+** Note that another similar error was found by this utility when it was |
+** first written. That other error was fixed by the same check-in that |
+** committed the first version of this utility program. |
+*/ |
+#include <stdlib.h> |
+#include <ctype.h> |
+#include <stdio.h> |
+#include <string.h> |
+ |
+/* Read the complete text of a file into memory. Return a pointer to |
+** the result. Panic if unable to read the file or allocate memory. |
+*/ |
+static char *readFile(const char *zFilename){ |
+ FILE *in; |
+ char *z; |
+ long n; |
+ size_t got; |
+ |
+ in = fopen(zFilename, "rb"); |
+ if( in==0 ){ |
+ fprintf(stderr, "unable to open '%s' for reading\n", zFilename); |
+ exit(1); |
+ } |
+ fseek(in, 0, SEEK_END); |
+ n = ftell(in); |
+ rewind(in); |
+ z = malloc( n+1 ); |
+ if( z==0 ){ |
+ fprintf(stderr, "cannot allocate %d bytes to store '%s'\n", |
+ (int)(n+1), zFilename); |
+ exit(1); |
+ } |
+ got = fread(z, 1, n, in); |
+ fclose(in); |
+ if( got!=(size_t)n ){ |
+ fprintf(stderr, "only read %d of %d bytes from '%s'\n", |
+ (int)got, (int)n, zFilename); |
+ exit(1); |
+ } |
+ z[n] = 0; |
+ return z; |
+} |
+ |
+/* Check the C code in the argument to see if it might have |
+** side effects. The only accurate way to know this is to do a full |
+** parse of the C code, which this routine does not do. This routine |
+** uses a simple heuristic of looking for: |
+** |
+** * '=' not immediately after '>', '<', '!', or '='. |
+** * '++' |
+** * '--' |
+** |
+** If the code contains the phrase "side-effects-ok" is inside a |
+** comment, then always return false. This is used to disable checking |
+** for assert()s with deliberate side-effects, such as used by |
+** SQLITE_TESTCTRL_ASSERT - a facility that allows applications to |
+** determine at runtime whether or not assert()s are enabled. |
+** Obviously, that determination cannot be made unless the assert() |
+** has some side-effect. |
+** |
+** Return true if a side effect is seen. Return false if not. |
+*/ |
+static int hasSideEffect(const char *z, unsigned int n){ |
+ unsigned int i; |
+ for(i=0; i<n; i++){ |
+ if( z[i]=='/' && strncmp(&z[i], "/*side-effects-ok*/", 19)==0 ) return 0; |
+ if( z[i]=='=' && i>0 && z[i-1]!='=' && z[i-1]!='>' |
+ && z[i-1]!='<' && z[i-1]!='!' && z[i+1]!='=' ) return 1; |
+ if( z[i]=='+' && z[i+1]=='+' ) return 1; |
+ if( z[i]=='-' && z[i+1]=='-' ) return 1; |
+ } |
+ return 0; |
+} |
+ |
+/* Return the number of bytes in string z[] prior to the first unmatched ')' |
+** character. |
+*/ |
+static unsigned int findCloseParen(const char *z){ |
+ unsigned int nOpen = 0; |
+ unsigned i; |
+ for(i=0; z[i]; i++){ |
+ if( z[i]=='(' ) nOpen++; |
+ if( z[i]==')' ){ |
+ if( nOpen==0 ) break; |
+ nOpen--; |
+ } |
+ } |
+ return i; |
+} |
+ |
+/* Search for instances of assert(...), ALWAYS(...), NEVER(...), and/or |
+** testcase(...) where the argument contains side effects. |
+** |
+** Print error messages whenever a side effect is found. Return the number |
+** of problems seen. |
+*/ |
+static unsigned int findAllSideEffects(const char *z){ |
+ unsigned int lineno = 1; /* Line number */ |
+ unsigned int i; |
+ unsigned int nErr = 0; |
+ char c, prevC = 0; |
+ for(i=0; (c = z[i])!=0; prevC=c, i++){ |
+ if( c=='\n' ){ lineno++; continue; } |
+ if( isalpha(c) && !isalpha(prevC) ){ |
+ if( strncmp(&z[i],"assert(",7)==0 |
+ || strncmp(&z[i],"ALWAYS(",7)==0 |
+ || strncmp(&z[i],"NEVER(",6)==0 |
+ || strncmp(&z[i],"testcase(",9)==0 |
+ ){ |
+ unsigned int n; |
+ const char *z2 = &z[i+5]; |
+ while( z2[0]!='(' ){ z2++; } |
+ z2++; |
+ n = findCloseParen(z2); |
+ if( hasSideEffect(z2, n) ){ |
+ nErr++; |
+ fprintf(stderr, "side-effect line %u: %.*s\n", lineno, |
+ (int)(&z2[n+1] - &z[i]), &z[i]); |
+ } |
+ } |
+ } |
+ } |
+ return nErr; |
+} |
+ |
+int main(int argc, char **argv){ |
+ char *z; |
+ unsigned int nErr = 0; |
+ if( argc!=2 ){ |
+ fprintf(stderr, "Usage: %s FILENAME\n", argv[0]); |
+ return 1; |
+ } |
+ z = readFile(argv[1]); |
+ nErr = findAllSideEffects(z); |
+ free(z); |
+ if( nErr ){ |
+ fprintf(stderr, "Found %u undesirable side-effects\n", nErr); |
+ return 1; |
+ } |
+ return 0; |
+} |