2 ** The program does some simple static analysis of the sqlite3.c source
3 ** file looking for mistakes.
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.
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
){
36 in
= fopen(zFilename
, "rb");
38 fprintf(stderr
, "unable to open '%s' for reading\n", zFilename
);
41 fseek(in
, 0, SEEK_END
);
46 fprintf(stderr
, "cannot allocate %d bytes to store '%s'\n",
47 (int)(n
+1), zFilename
);
50 got
= fread(z
, 1, n
, in
);
53 fprintf(stderr
, "only read %d of %d bytes from '%s'\n",
54 (int)got
, (int)n
, zFilename
);
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 '='.
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
){
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;
92 /* Return the number of bytes in string z[] prior to the first unmatched ')'
95 static unsigned int findCloseParen(const char *z
){
96 unsigned int nOpen
= 0;
99 if( z
[i
]=='(' ) nOpen
++;
101 if( nOpen
==0 ) break;
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
114 static unsigned int findAllSideEffects(const char *z
){
115 unsigned int lineno
= 1; /* Line number */
117 unsigned int nErr
= 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
128 const char *z2
= &z
[i
+5];
129 while( z2
[0]!='(' ){ z2
++; }
131 n
= findCloseParen(z2
);
132 if( hasSideEffect(z2
, n
) ){
134 fprintf(stderr
, "side-effect line %u: %.*s\n", lineno
,
135 (int)(&z2
[n
+1] - &z
[i
]), &z
[i
]);
143 int main(int argc
, char **argv
){
145 unsigned int nErr
= 0;
147 fprintf(stderr
, "Usage: %s FILENAME\n", argv
[0]);
150 z
= readFile(argv
[1]);
151 nErr
= findAllSideEffects(z
);
154 fprintf(stderr
, "Found %u undesirable side-effects\n", nErr
);