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