sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
+srcck1$(BEXE): $(TOP)/tool/srcck1.c
+ $(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c
+
+sourcetest: srcck1$(BEXE) sqlite3.c
+ ./srcck1 sqlite3.c
+
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(FUZZERSHELL_OPT) \
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
-test: $(TESTPROGS) fastfuzztest
+test: $(TESTPROGS) sourcetest fastfuzztest
./testfixture$(TEXE) $(TOP)/test/veryquick.test $(TESTOPTS)
# Run a test using valgrind. This can take a really long time
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
+srcck1$(EXE): $(TOP)/tool/srcck1.c
+ $(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c
+
+sourcetest: srcck1$(EXE) sqlite3.c
+ ./srcck1 sqlite3.c
+
fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
$(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \
# The default test case. Runs most of the faster standard TCL tests,
# and fuzz tests, and sqlite3_analyzer and sqldiff tests.
#
-test: $(TESTPROGS) fastfuzztest
+test: $(TESTPROGS) sourcetest fastfuzztest
./testfixture$(EXE) $(TOP)/test/veryquick.test $(TESTOPTS)
# Run a test using valgrind. This can take a really long time
-C Make\ssure\svariable\sdeclarations\soccur\sat\sthe\sbeginning\sof\sblocks,\seven\nwith\sSQLITE_DEBUG\senabled.
-D 2016-02-06T19:48:50.321
-F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4
+C Add\sa\sutility\sprogram\sthat\slooks\sfor\sassert(),\sNEVER(),\sALWAYS(),\sand\ntestcase()\smacros\sthat\shave\sside-effects,\sand\sreports\serrors\swhen\sthey\sare\nfound.\s\sAlso\sfix\sa\sbug\sthat\sthis\sutility\sdetected\sas\sit\swas\sbeing\stested.
+D 2016-02-06T22:32:06.228
+F Makefile.in 0a957a57243a3d55e96b1514e22ffae5db9ea116
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 72b7858f02017611c3ac1ddc965251017fed0845
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 960071a0bceb043bc5627573986154f507931f33
+F main.mk f51c0652d2a623160e90a758e01312a6a00f3454
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc
F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf
F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
-F src/btree.c 0b359bcc2316a57acf12f583253974ad22b4654f
+F src/btree.c 4c8caaeed7878aafdb607c3d2bcbc365bb0d19a1
F src/btree.h 368ceeb4bd9312dc8df2ffd64b4b7dbcf4db5f8e
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
F src/build.c 198eaa849c193f28b802ed135b2483c68ef7a35c
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c b1b0880fc474abfab89e737b0ecfde0bd7a60902
F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b
-F src/main.c 62b7fe3ed245757d1ff2e6268a7ec0bc30100308
+F src/main.c b67a45397b93b7ba8fbd6bfcb03423d245baed05
F src/malloc.c 337e9808b5231855fe28857950f4f60ae42c417f
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/sqldiff.c 5a26205111e6fa856d9b1535b1637744dcdb930b
+F tool/srcck1.c 3119733530abcef14f1b0603c66207a342936263
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P a2952231ac7abe165ed070875728f752ae0be608
-R da8a8c4dec6af3be8a6f4cff524d4f5f
+P 2f7778e64d93ef237e23ceac01ea9808df5cf2a1
+R e75f025e263d6165f7cd0bcd65f5ad12
U drh
-Z f3b1e3c58e3e9ab1cb32cfa377da1181
+Z 663376736f9ceb33b8ce1b1cb94df97c
-2f7778e64d93ef237e23ceac01ea9808df5cf2a1
\ No newline at end of file
+b0b4624fc5d53bb0cc9fae7dad51984837d946ac
\ No newline at end of file
{
CellInfo info;
pPage->xParseCell(pPage, pCell, &info);
- assert( nHeader=(int)(info.pPayload - pCell) );
+ assert( nHeader==(int)(info.pPayload - pCell) );
assert( info.nKey==nKey );
assert( *pnSize == info.nSize );
assert( spaceLeft == info.nLocal );
u8 aBalanceQuickSpace[13];
u8 *pFree = 0;
- TESTONLY( int balance_quick_called = 0 );
- TESTONLY( int balance_deeper_called = 0 );
+ VVA_ONLY( int balance_quick_called = 0 );
+ VVA_ONLY( int balance_deeper_called = 0 );
do {
int iPage = pCur->iPage;
** and copy the current contents of the root-page to it. The
** next iteration of the do-loop will balance the child page.
*/
- assert( (balance_deeper_called++)==0 );
+ assert( balance_deeper_called==0 );
+ VVA_ONLY( balance_deeper_called++ );
rc = balance_deeper(pPage, &pCur->apPage[1]);
if( rc==SQLITE_OK ){
pCur->iPage = 1;
** function. If this were not verified, a subtle bug involving reuse
** of the aBalanceQuickSpace[] might sneak in.
*/
- assert( (balance_quick_called++)==0 );
+ assert( balance_quick_called==0 );
+ VVA_ONLY( balance_quick_called++ );
rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
}else
#endif
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
- assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 );
+ VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
+ assert( nRef>=0 );
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = btreePagecount(sCheck.pBt);
*/
case SQLITE_TESTCTRL_ASSERT: {
volatile int x = 0;
- assert( (x = va_arg(ap,int))!=0 );
+ assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
break;
}
--- /dev/null
+/*
+** 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>
+
+/* 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;
+}
+
+/* Change 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 j, 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;
+}