/*
-** LCLint - annotation-assisted static program checker
-** Copyright (C) 1994-2001 University of Virginia,
+** Splint - annotation-assisted static program checker
+** Copyright (C) 1994-2002 University of Virginia,
** Massachusetts Institute of Technology
**
** This program is free software; you can redistribute it and/or modify it
** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA.
**
-** For information on lclint: lclint-request@cs.virginia.edu
-** To report a bug: lclint-bug@cs.virginia.edu
-** For more information: http://lclint.cs.virginia.edu
+** For information on splint: info@splint.org
+** To report a bug: splint-bug@splint.org
+** For more information: http://www.splint.org
*/
/*
** exprNode.c
*/
# include <ctype.h> /* for isdigit */
-# include "lclintMacros.nf"
+# include "splintMacros.nf"
# include "basic.h"
# include "cgrammar.h"
# include "cgrammar_tokens.h"
# include "transferChecks.h"
# include "exprNodeSList.h"
+static bool exprNode_sameStorage (exprNode p_e1, exprNode p_e2) /*@*/ ;
static bool exprNode_isEmptyStatement (exprNode p_e);
static /*@exposed@*/ exprNode exprNode_firstStatement (/*@returned@*/ exprNode p_e);
static bool exprNode_isFalseConstant (exprNode p_e) /*@*/ ;
return (e); /* s released */
}
+/*@only@*/ exprNode
+exprNode_wideStringLiteral (/*@only@*/ cstring t, /*@only@*/ fileloc loc)
+{
+ exprNode res = exprNode_stringLiteral (t, loc);
+ res->typ = ctype_makeWideString ();
+
+ return res;
+}
+
/*@only@*/ exprNode
exprNode_stringLiteral (/*@only@*/ cstring t, /*@only@*/ fileloc loc)
{
return (e);
}
+exprNode exprNode_makeConstantString (cstring c, /*@only@*/ fileloc loc)
+{
+ exprNode e = exprNode_createPlain (ctype_unknown);
+ e->kind = XPR_VAR;
+ e->loc = loc;
+ e->sref = sRef_makeConst (ctype_string);
+ e->edata = exprData_makeId (uentry_makeUnrecognized (c, fileloc_copy (loc)));
+ e->typ = ctype_string;
+
+ /* No alias errors for unrecognized identifiers */
+ sRef_setAliasKind (e->sref, AK_STATIC, loc);
+ sRef_setExKind (e->sref, XO_OBSERVER, loc);
+
+ return (e);
+}
+
exprNode exprNode_createId (/*@observer@*/ uentry c)
{
if (uentry_isValid (c))
{
exprNode e = exprNode_new ();
-
+
+ DPRINTF (("create id: %s", uentry_unparse (c)));
+
e->typ = uentry_getType (c);
if (uentry_isFunction (c)
/*
** yoikes! leaving this out was a heinous bug...that would have been
- ** caught if i had lclint working first. gag!
+ ** caught if i had splint working first. gag!
*/
e->etext = cstring_undefined;
e->msets = sRefSet_new ();
e->uses = sRefSet_new ();
- /*> missing fields, detected by lclint <*/
+ /*> missing fields, detected by splint <*/
e->exitCode = XK_NEVERESCAPE;
e->isJumpPoint = FALSE;
e->canBreak = FALSE;
if (ctype_isFixedArray (t1))
{
+
int nelements = long_toInt (ctype_getArraySize (t1));
llassert (multiVal_isString (mval));
slit = multiVal_forceString (mval);
- len = cstring_length (slit);
+
+ len = cstring_lengthExpandEscapes (slit);
+
if (len == nelements)
{
+ mstring temp;
+
+ temp = cstring_expandEscapes(slit);
+
+ if (temp[len-1] == '\0')
+ {
+ voptgenerror
+ (FLG_STRINGLITNOROOMFINALNULL,
+ message ("String literal with %d character%& "
+ "is assigned to %s (no room for final null terminator): %s",
+ len + 1,
+ ctype_unparse (t1),
+ exprNode_unparse (e2)),
+ e2->loc);
+ }
+ else
+ {
+
+
voptgenerror
(FLG_STRINGLITNOROOM,
message ("String literal with %d character%& "
"is assigned to %s (no room for null terminator): %s",
- cstring_length (slit),
+ len + 1,
ctype_unparse (t1),
exprNode_unparse (e2)),
- e2->loc);
+ e2->loc);
+ }
}
else if (len > nelements)
{
voptgenerror
(FLG_STRINGLITTOOLONG,
- message ("Stirng literal with %d character%& (counting null terminator) "
+ message ("String literal with %d character%& (counting null terminator) "
"is assigned to %s (insufficient storage available): %s",
- cstring_length (slit),
+ len + 1,
ctype_unparse (t1),
exprNode_unparse (e2)),
e2->loc);
voptgenerror
(FLG_STRINGLITSMALLER,
message ("String literal with %d character%& is assigned to %s (possible waste of storage): %s",
- cstring_length (slit),
+ len + 1,
ctype_unparse (t1),
exprNode_unparse (e2)),
e2->loc);
{
return (multiVal_forceInt (m) >= 0);
}
+
+ /*
+ ** This is not always true if programmer defines enum
+ ** values, but then the constant should be known.
+ */
+
+ if (ctype_isEnum (ctype_realType (e->typ)))
+ {
+ return TRUE;
+ }
}
return FALSE;
/*
** this sets up funny aliasing, that leads to spurious
- ** lclint errors. Hence, the i2 comments.
+ ** splint errors. Hence, the i2 comments.
*/
/* evans 2001-09-09 added ctype_isKnown so there is no swap when e1 type is unknown */
if (freshMods)
{
/*
- ** Spurious errors reported, because lclint can't tell
+ ** Spurious errors reported, because splint can't tell
** mods must be fresh if freshMods is true.
*/
}
}
+/*
+** Returns true iff e1 and e2 are both exactly the same storage
+** (conservative).
+*/
+
+static bool exprNode_sameStorage (exprNode e1, exprNode e2)
+{
+ sRef s1 = exprNode_getSref (e1);
+ sRef s2 = exprNode_getSref (e2);
+
+ return (sRef_realSame (s1, s2));
+}
+
exprNode
exprNode_makeInitBlock (lltok brace, /*@only@*/ exprNodeList inits)
{
/* added 7/11/2000 D.L */
/*@i223*/
- /*DRL 6/8/01 I decided to disable all LCLint Warning here since the code
+ /*DRL 6/8/01 I decided to disable all Splint warnings here since the code
probably needs a rewrite any way */
/*@i65234@*/
}
else
{
- sizet = ctype_ulint;
+ sizet = ctype_ulint;
}
}
return sizet;
}
ret->val = multiVal_undefined;
+
ret->kind = XPR_OP;
ret->edata = exprData_makeOp (e1, e2, op);
tret = ctype_unknown;
te1 = exprNode_getType (e1);
+
DPRINTF (("te1 = %s / %s", exprNode_unparse (e1), ctype_unparse (te1)));
te2 = exprNode_getType (e2);
case TDIV: /* */
case MUL_ASSIGN: /* numeric, numeric -> numeric */
case DIV_ASSIGN: /* */
-
+
+ if (opid == TMULT || opid == MUL_ASSIGN)
+ {
+ ret->val = multiVal_multiply (exprNode_getValue (e1),
+ exprNode_getValue (e2));
+ }
+ else
+ {
+ ret->val = multiVal_divide (exprNode_getValue (e1),
+ exprNode_getValue (e2));
+ }
+
tret = checkNumerics (tr1, tr2, te1, te2, e1, e2, op);
break;
case TMINUS: /* pointer, int -> pointer */
case SUB_ASSIGN: /* int, pointer -> pointer */
case ADD_ASSIGN: /* numeric, numeric -> numeric */
-
+
+ if (opid == TPLUS || opid == ADD_ASSIGN)
+ {
+ ret->val = multiVal_add (exprNode_getValue (e1),
+ exprNode_getValue (e2));
+ }
+ else
+ {
+ ret->val = multiVal_subtract (exprNode_getValue (e1),
+ exprNode_getValue (e2));
+ }
+
tr1 = ctype_fixArrayPtr (tr1);
if ((ctype_isRealPointer (tr1) && !exprNode_isNullValue (e1))
status */
if ((sRef_isPossiblyNullTerminated (e1->sref)) || (sRef_isNullTerminated(e1->sref))) {
int val;
- /*drl 1-4-2001
+ /*drl 1-4-2002
added ugly fixed to stop
program from crashing on point + int +int
one day I'll fix this or ask Seejo wtf the codes supposed to do. */
break;
- case LEFT_ASSIGN: /* Shifts: should be unsigned values */
+ case LEFT_ASSIGN:
case RIGHT_ASSIGN:
case LEFT_OP:
case RIGHT_OP:
case OR_ASSIGN:
{
bool reported = FALSE;
- flagcode code = FLG_BITWISEOPS;
- if (opid == LEFT_OP || opid == LEFT_ASSIGN
- || opid == RIGHT_OP || opid == RIGHT_ASSIGN) {
- code = FLG_SHIFTSIGNED;
- }
+ /*
+ ** Shift Operator
+ */
- if (!ctype_isUnsigned (tr1))
+ if (opid == LEFT_OP || opid == LEFT_ASSIGN
+ || opid == RIGHT_OP || opid == RIGHT_ASSIGN)
{
- if (exprNode_isNonNegative (e1)) {
- ;
- } else {
- reported = optgenerror
- (code,
- message ("Left operand of %s is not unsigned value (%t): %s",
- lltok_unparse (op), te1,
- exprNode_unparse (ret)),
- e1->loc);
-
- if (reported) {
- te1 = ctype_uint;
+ /*
+ ** evans 2002-01-01: fixed this to follow ISO 6.5.7.
+ */
+
+ if (!ctype_isUnsigned (tr2)
+ && !exprNode_isNonNegative (e2))
+ {
+ reported = optgenerror
+ (FLG_SHIFTNEGATIVE,
+ message ("Right operand of %s may be negative (%t): %s",
+ lltok_unparse (op), te2,
+ exprNode_unparse (ret)),
+ e2->loc);
}
- }
+
+ if (!ctype_isUnsigned (tr1)
+ && !exprNode_isNonNegative (e1))
+ {
+ reported = optgenerror
+ (FLG_SHIFTIMPLEMENTATION,
+ message ("Left operand of %s may be negative (%t): %s",
+ lltok_unparse (op), te1,
+ exprNode_unparse (ret)),
+ e1->loc);
+ }
+
+ /*
+ ** Should check size of right operand also...
+ */
+
}
- else
+ else
{
- /* right need not be signed for shifts */
- if (code != FLG_SHIFTSIGNED
- && !ctype_isUnsigned (tr2))
+ if (!ctype_isUnsigned (tr1))
{
- if (!exprNode_isNonNegative (e2)) {
+ if (exprNode_isNonNegative (e1)) {
+ ;
+ } else {
reported = optgenerror
- (code,
- message ("Right operand of %s is not unsigned value (%t): %s",
- lltok_unparse (op), te2,
+ (FLG_BITWISEOPS,
+ message ("Left operand of %s is not unsigned value (%t): %s",
+ lltok_unparse (op), te1,
exprNode_unparse (ret)),
- e2->loc);
+ e1->loc);
+
+ if (reported) {
+ te1 = ctype_uint;
+ }
}
}
+ else
+ {
+ if (!ctype_isUnsigned (tr2))
+ {
+ if (!exprNode_isNonNegative (e2)) {
+ reported = optgenerror
+ (FLG_BITWISEOPS,
+ message ("Right operand of %s is not unsigned value (%t): %s",
+ lltok_unparse (op), te2,
+ exprNode_unparse (ret)),
+ e2->loc);
+ }
+ }
+ }
}
-
+
if (!reported)
{
if (!checkIntegral (e1, e2, ret, op)) {
case NE_OP:
case TLT: /* comparisons */
case TGT: /* numeric, numeric -> bool */
+
+ DPRINTF (("Here we go: %s / %s",
+ ctype_unparse (tr1), ctype_unparse (tr2)));
+
if ((ctype_isReal (tr1) && !ctype_isInt (tr1))
|| (ctype_isReal (tr2) && !ctype_isInt (tr2)))
{
** Types should match.
*/
+ DPRINTF (("Match types: %s / %s", exprNode_unparse (e1),
+ exprNode_unparse (e2)));
+
if (!exprNode_matchTypes (e1, e2))
{
hasError = gentypeerror
llfatalbug
(cstring_makeLiteral
("There has been a problem in the parser. This is believed to result "
- "from a problem with bison v. 1.25. Please try rebuidling LCLint "
+ "from a problem with bison v. 1.25. Please try rebuidling Splint "
"using the pre-compiled grammar files by commenting out the "
"BISON= line in the top-level Makefile."));
}
&& (lltok_isSemi (exprData_getTok (e->edata))));
}
+void exprNode_checkIfPred (exprNode pred)
+{
+ exprNode_checkPred (cstring_makeLiteralTemp ("if"), pred);
+}
+
exprNode exprNode_if (/*@only@*/ exprNode pred, /*@only@*/ exprNode tclause)
{
exprNode ret;
message ("Predicate always exits: %s", exprNode_unparse (pred)),
exprNode_loc (pred));
}
-
- exprNode_checkPred (cstring_makeLiteralTemp ("if"), pred);
+
+ /*! exprNode_checkPred (cstring_makeLiteralTemp ("if"), pred); */ /*@i523@*/
exprNode_checkUse (pred, pred->sref, pred->loc);
if (!exprNode_isError (tclause))
exprNode_loc (pred));
}
- exprNode_checkPred (cstring_makeLiteralTemp ("if"), pred);
+ /*@i3423 exprNode_checkPred (cstring_makeLiteralTemp ("if"), pred);*/
exprNode_checkUse (ret, pred->sref, pred->loc);
exprNode_mergeCondUSs (ret, tclause, eclause);
if (hasDefault)
{
voptgenerror
- (FLG_CONTROL,
+ (FLG_DUPLICATECASES,
message ("Duplicate default cases in switch"),
exprNode_loc (current));
}
(/*@-usedef@*/usedEnums/*@=usedef@*/, cname))
{
voptgenerror
- (FLG_CONTROL,
+ (FLG_DUPLICATECASES,
message ("Duplicate case in switch: %s",
cname),
current->loc);
g_currentloc);
enumNameSList_free (unused);
+ *allpaths = FALSE; /* evans 2002-01-01 */
}
else
{
if (exprNode_isDefined (t) && exprNode_mustEscape (t))
{
voptgenerror
- (FLG_CONTROL,
+ (FLG_ALWAYSEXITS,
message ("Predicate always exits: %s", exprNode_unparse (t)),
exprNode_loc (t));
}
return ret;
}
+bool exprNode_loopMustExec (exprNode forPred)
+{
+ /*
+ ** Returns true if it is obvious that the loop always executes at least once
+ **
+ ** For now, we only identify the most obvious cases. Should be true anytime
+ ** we can prove init => !test.
+ */
+
+ if (exprNode_isDefined (forPred))
+ {
+ exprNode init, test, inc;
+ exprData edata;
+
+ llassert (forPred->kind == XPR_FORPRED);
+
+ edata = forPred->edata;
+ init = exprData_getTripleInit (edata);
+ test = exprData_getTripleTest (edata);
+ inc = exprData_getTripleInc (edata);
+
+ if (exprNode_isAssign (init))
+ {
+ exprNode loopVar = exprData_getOpA (init->edata);
+ exprNode loopInit = exprData_getOpB (init->edata);
+
+ if (exprNode_isDefined (test) && test->kind == XPR_OP)
+ {
+ exprNode testVar = exprData_getOpA (test->edata);
+ exprNode testVal = exprData_getOpB (test->edata);
+ lltok comp = exprData_getOpTok (test->edata);
+ int opid = lltok_getTok (comp);
+
+ DPRINTF (("Same storage: %s / %s", exprNode_unparse (loopVar),
+ exprNode_unparse (testVar)));
+
+ if (exprNode_sameStorage (loopVar, testVar))
+ {
+ multiVal valinit = exprNode_getValue (loopInit);
+ multiVal valtest = exprNode_getValue (testVal);
+
+ DPRINTF (("Values: %s / %s", multiVal_unparse (valinit),
+ multiVal_unparse (valtest)));
+
+ if (multiVal_isInt (valinit) && multiVal_isInt (valtest))
+ {
+ long v1 = multiVal_forceInt (valinit);
+ long v2 = multiVal_forceInt (valtest);
+
+ DPRINTF (("Here: %ld %ld", v1, v2));
+
+ if ((opid == EQ_OP && v1 < v2)
+ || (opid == NE_OP && v1 != v2)
+ || (opid == TLT && v1 <= v2)
+ || (opid == TGT && v1 >= v2)
+ || (opid == LE_OP && v1 < v2)
+ || (opid == GE_OP && v1 > v2))
+ {
+ DPRINTF (("mustexec if inc"));
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ DPRINTF (("loop must exec: FALSE"));
+ return FALSE;
+}
+
exprNode exprNode_for (/*@keep@*/ exprNode inc, /*@keep@*/ exprNode body)
{
exprNode ret;
ret->exitCode = exitkind_makeConditional (body->exitCode);
- exprNode_mergeUSs (inc, body);
+ exprNode_mergeUSs (inc, body);
if (exprNode_isDefined (inc))
{
exprNode tmp;
context_setMessageAnnote (cstring_makeLiteral ("in post loop increment"));
-
tmp = exprNode_effect (exprData_getTripleInc (inc->edata));
exprNode_freeShallow (tmp);
ctype t2 = exprNode_getType (val);
bool hasError = FALSE;
+ DPRINTF (("Check one init: %s / %s",
+ exprNode_unparse (el),
+ exprNode_unparse (val)));
+
if (ctype_isUnknown (t1))
{
voptgenerror (FLG_IMPTYPE,
if (exprNode_isDefined (val) && val->kind == XPR_INITBLOCK)
{
exprNodeList vals = exprData_getArgs (val->edata);
-
+
DPRINTF (("Check one init: %s", exprNodeList_unparse (vals)));
DPRINTF (("Type: %s", ctype_unparse (t1)));
{
int nelements = long_toInt (ctype_getArraySize (t1));
+ DPRINTF (("Checked array: %s / %d",
+ ctype_unparse (t1), nelements));
+
if (exprNode_isStringLiteral (val))
{
exprNode_checkStringLiteralLength (t1, val);
} end_exprNodeList_elements;
}
}
+ /* evans 2001-12-30: added to fix bug reported by Jim Zelenka */
+ else if (ctype_isUnion (ctype_realType (t1)))
+ {
+ uentryList fields = ctype_getFields (t1);
+ int i = 0;
+
+ /*
+ ** Union initializers set the first member always.
+ */
+
+ DPRINTF (("Union initializer: %s / %s",
+ exprNode_unparse (el), ctype_unparse (ctype_realType (t1))));
+
+ if (exprNodeList_size (vals) != 1)
+ {
+ hasError = optgenerror
+ (FLG_TYPE,
+ message ("Initializer block for union "
+ "%s has %d elements, union initializers should have one element: %q",
+ exprNode_unparse (el),
+ exprNodeList_size (vals),
+ exprNodeList_unparse (vals)),
+ val->loc);
+ }
+ else
+ {
+ exprNode oneval = exprNodeList_head (vals);
+ uentry thisfield = uentryList_getN (fields, i);
+ exprNode newel =
+ exprNode_fieldAccessAux (exprNode_fakeCopy (el),
+ exprNode_loc (el),
+ uentry_getName (thisfield));
+
+ if (exprNode_isDefined (newel))
+ {
+ if (exprNode_checkOneInit (newel, oneval))
+ {
+ hasError = TRUE;
+ }
+
+ exprNode_freeIniter (newel);
+ }
+ }
+ }
else
{
hasError = optgenerror
{
exprNode ret;
+ DPRINTF (("Initialization: %s", idDecl_unparse (t)));
+
if (usymtab_exists (idDecl_observeId (t)))
{
uentry ue = usymtab_lookup (idDecl_observeId (t));
ue = uentry_makeUnrecognized (idDecl_observeId (t), fileloc_copy (g_currentloc));
/*!! fileloc_copy (g_currentloc)); */
/*@i32!!! should get error without this */
+
ret = exprNode_fromIdentifierAux (ue);
/*
{
sRef_setDefState (lhs->sref, SS_PARTIAL, fileloc_undefined);
}
-
+
(void) exprNode_checkOneInit (lhs, e);
if (uentry_isStatic (ue))
if (uentry_isInvalid (end))
{
- llerror (FLG_ITER,
+ llerror (FLG_ITERBALANCE,
message ("Iter %s not balanced with end_%s", iname, iname));
}
else
if (!cstring_equalPrefixLit (ename, "end_"))
{
- llerror (FLG_ITER, message ("Iter %s not balanced with end_%s: %s",
+ llerror (FLG_ITERBALANCE, message ("Iter %s not balanced with end_%s: %s",
iname, iname, ename));
}
else
{
if (!cstring_equal (iname, cstring_suffix (ename, 4)))
{
- llerror (FLG_ITER,
+ llerror (FLG_ITERBALANCE,
message ("Iter %s not balanced with end_%s: %s",
iname, iname, ename));
}
e->isJumpPoint = FALSE;
e->exitCode = XK_NEVERESCAPE;
- /*> missing fields, detected by lclint <*/
+ /*> missing fields, detected by splint <*/
e->canBreak = FALSE;
e->mustBreak = FALSE;
e->etext = cstring_undefined;
if (fileloc_isDefined (e->loc))
{
voptgenerror
- (FLG_ITER,
+ (FLG_ITERYIELD,
message ("Yield parameter is not simple identifier: %s",
exprNode_unparse (e)),
e->loc);
else
{
voptgenerror
- (FLG_ITER,
+ (FLG_ITERYIELD,
message ("Yield parameter is not simple identifier: %s",
exprNode_unparse (e)),
g_currentloc);
if (!context_inHeader ())
{
if (optgenerror
- (FLG_ITER,
+ (FLG_ITERYIELD,
message ("Yield parameter shadows local declaration: %q",
uentry_getName (c)),
fileloc_isDefined (e->loc) ? e->loc : g_currentloc))
break;
case XPR_STRINGLITERAL:
- ret = message ("\"%s\"", exprData_getLiteral (data));
+ if (ctype_isWideString (e->typ))
+ {
+ ret = message ("L\"%s\"", exprData_getLiteral (data));
+ }
+ else
+ {
+ ret = message ("\"%s\"", exprData_getLiteral (data));
+ }
break;
case XPR_NUMLIT:
}
else if (ctype_isArrayPtr (expected))
{
- return (val == 0);
+ /*
+ ** evans 2001-10-14: We allow 0 to match any pointer, but only if the type matches or is void *.
+ */
+
+ if (val == 0)
+ {
+ if (ctype_match (exprNode_getType (e), expected)
+ || ctype_isVoidPointer (exprNode_getType (e)))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
}
else if (ctype_isAnyFloat (expected))
{
return TRUE;
}
+ DPRINTF (("Matching literal! %s %s %s %s",
+ ctype_unparse (t1), exprNode_unparse (e2),
+ ctype_unparse (t2), exprNode_unparse (e1)));
+
return (exprNode_matchLiteral (t1, e2) || exprNode_matchLiteral (t2, e1));
}
while (sRef_isValid (s) && sRef_isKnown (s))
{
- ynm readable = sRef_isReadable (s);
+ ynm readable = sRef_isValidLvalue (s);
DPRINTF (("Readable: %s / %s",
sRef_unparseFull (s), ynm_unparse (readable)));
}
}
- checkPassTransfer (current, ucurrent, isSpec, fcn, argno, totargs);
+ transferChecks_passParam (current, ucurrent, isSpec, fcn, argno, totargs);
exprNode_mergeUSs (fcn, current);
}
}
if ((ctype_isRealInt (tr1) || ctype_isReal (tr1)) &&
(ctype_isRealInt (tr2) || ctype_isReal (tr2)))
{
- ;
+ DPRINTF (("No error: [%s] %s / [%s] %s",
+ exprNode_unparse (e1), ctype_unparse (tr1),
+ exprNode_unparse (e2), ctype_unparse (tr2)));
}
else
{
{
DPRINTF (("Check init: %s / %s",
exprNode_unparse (e1), exprNode_unparse (e2)));
- checkInitTransfer (e1, e2);
+ transferChecks_initialization (e1, e2);
}
else
{
- checkAssignTransfer (e1, e2);
+ transferChecks_assign (e1, e2);
}
}
else