428 lines
8.3 KiB
C
428 lines
8.3 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*/
|
|
/* $Id$ */
|
|
/* Lint miscellaneous routines */
|
|
|
|
#include "lint.h"
|
|
|
|
#ifdef LINT
|
|
|
|
#include <alloc.h> /* for st_free */
|
|
#include "interface.h"
|
|
#ifdef ANSI
|
|
#include <flt_arith.h>
|
|
#endif /* ANSI */
|
|
#include "arith.h" /* definition arith */
|
|
#include "label.h" /* definition label */
|
|
#include "expr.h"
|
|
#include "idf.h"
|
|
#include "def.h"
|
|
#include "code.h" /* RVAL etc */
|
|
#include "LLlex.h"
|
|
#include "Lpars.h"
|
|
#include "stack.h"
|
|
#include "type.h"
|
|
#include "level.h"
|
|
#include "l_state.h"
|
|
|
|
extern char *symbol2str();
|
|
extern struct type *func_type;
|
|
|
|
PRIVATE lint_enum_arith();
|
|
PRIVATE lint_conversion();
|
|
PRIVATE int numsize();
|
|
|
|
check_hiding(idf, lvl, sc)
|
|
struct idf *idf;
|
|
int lvl;
|
|
int sc;
|
|
{
|
|
/* Checks if there is already a definition for this non-extern
|
|
name on a more global level.
|
|
*/
|
|
struct def *def = idf->id_def;
|
|
|
|
if ( def && def->df_level < lvl
|
|
&& ! ( lvl == L_FORMAL2
|
|
|| def->df_level == L_UNIVERSAL
|
|
|| sc == GLOBAL
|
|
|| sc == EXTERN
|
|
)
|
|
) {
|
|
warning("%s is already defined as a %s",
|
|
idf->id_text,
|
|
def->df_level == L_GLOBAL ? "global" :
|
|
def->df_level == L_FORMAL2 ? "formal" :
|
|
"more global local"
|
|
);
|
|
}
|
|
}
|
|
|
|
lint_new_oper(expr)
|
|
struct expr *expr;
|
|
{
|
|
/* Does additional checking on a newly constructed expr node
|
|
of class Oper.
|
|
|
|
Some code in this routine could be contracted, but since
|
|
I am not sure we have covered the entire ground, we'll
|
|
leave the contracting for some rainy day.
|
|
*/
|
|
register struct expr *left = expr->OP_LEFT;
|
|
register struct expr *right = expr->OP_RIGHT;
|
|
register int oper = expr->OP_OPER;
|
|
register int l_fund =
|
|
left == 0 ? 0 : /* for monadics */
|
|
left->ex_type->tp_fund;
|
|
register int r_fund =
|
|
right == 0 ? 0 : /* for ( without parameters */
|
|
right->ex_type->tp_fund;
|
|
|
|
/* In ch7.c, in ch7asgn(), a combined operator/assignment
|
|
is hammered into correctness by repeated application of
|
|
ch7bin(), which calls new_oper(), which calls lint_new_oper().
|
|
These spurious calls understandably cause spurious error
|
|
messages, which we don't like. So we try to suppress these
|
|
wierd calls here. This refers to the code marked
|
|
this is really $#@&*%$# !
|
|
in ch7asgn().
|
|
*/
|
|
switch (oper) {
|
|
case PLUSAB:
|
|
case MINAB:
|
|
case TIMESAB:
|
|
case DIVAB:
|
|
case MODAB:
|
|
case LEFTAB:
|
|
case RIGHTAB:
|
|
case ANDAB:
|
|
case XORAB:
|
|
case ORAB:
|
|
/* is the left operand wierd? */
|
|
if ( left->ex_class == Value
|
|
&& left->VL_CLASS == Const
|
|
&& left->VL_VALUE == 0
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (oper) {
|
|
case '=':
|
|
lint_conversion(right, l_fund);
|
|
break;
|
|
|
|
case PLUSAB:
|
|
lint_conversion(right, l_fund);
|
|
case '+':
|
|
lint_enum_arith(l_fund, oper, r_fund);
|
|
break;
|
|
|
|
case MINAB:
|
|
lint_conversion(right, l_fund);
|
|
case '-':
|
|
if (left == 0) {
|
|
/* unary */
|
|
if (r_fund == ENUM)
|
|
warning("negating an enum");
|
|
}
|
|
else {
|
|
/* binary */
|
|
if (l_fund == ENUM && r_fund == ENUM) {
|
|
if (left->ex_type != right->ex_type)
|
|
warning("subtracting enums of different type");
|
|
/* update the type, cem does not do it */
|
|
expr->ex_type = int_type;
|
|
}
|
|
lint_enum_arith(l_fund, oper, r_fund);
|
|
}
|
|
break;
|
|
|
|
case TIMESAB:
|
|
lint_conversion(right, l_fund);
|
|
case '*':
|
|
if (left == 0) {
|
|
/* unary */
|
|
}
|
|
else {
|
|
/* binary */
|
|
if (l_fund == ENUM || r_fund == ENUM)
|
|
warning("multiplying enum");
|
|
}
|
|
break;
|
|
|
|
case DIVAB:
|
|
lint_conversion(right, l_fund);
|
|
case '/':
|
|
if (l_fund == ENUM || r_fund == ENUM)
|
|
warning("division on enum");
|
|
break;
|
|
|
|
case MODAB:
|
|
lint_conversion(right, l_fund);
|
|
case '%':
|
|
if (l_fund == ENUM || r_fund == ENUM)
|
|
warning("modulo on enum");
|
|
break;
|
|
|
|
case '~':
|
|
if (r_fund == ENUM || r_fund == FLOAT || r_fund == DOUBLE)
|
|
warning("~ on %s", symbol2str(r_fund));
|
|
break;
|
|
|
|
case '!':
|
|
if (r_fund == ENUM)
|
|
warning("! on enum");
|
|
break;
|
|
|
|
case INT2INT:
|
|
case INT2FLOAT:
|
|
case FLOAT2INT:
|
|
case FLOAT2FLOAT:
|
|
lint_conversion(right, l_fund);
|
|
break;
|
|
|
|
case '<':
|
|
case '>':
|
|
case LESSEQ:
|
|
case GREATEREQ:
|
|
case EQUAL:
|
|
case NOTEQUAL:
|
|
if ( (l_fund == ENUM || r_fund == ENUM)
|
|
&& left->ex_type != right->ex_type
|
|
) {
|
|
warning("comparing enum with non-enum");
|
|
}
|
|
lint_relop(left, right, oper);
|
|
lint_relop(right, left,
|
|
oper == '<' ? '>' :
|
|
oper == '>' ? '<' :
|
|
oper == LESSEQ ? GREATEREQ :
|
|
oper == GREATEREQ ? LESSEQ :
|
|
oper
|
|
);
|
|
break;
|
|
|
|
case LEFTAB:
|
|
case RIGHTAB:
|
|
lint_conversion(right, l_fund);
|
|
case LEFT:
|
|
case RIGHT:
|
|
if (l_fund == ENUM || r_fund == ENUM)
|
|
warning("shift on enum");
|
|
break;
|
|
|
|
case ANDAB:
|
|
case ORAB:
|
|
case XORAB:
|
|
lint_conversion(right, l_fund);
|
|
case '&':
|
|
case '|':
|
|
case '^':
|
|
if (l_fund == ENUM || r_fund == ENUM)
|
|
warning("bit operations on enum");
|
|
break;
|
|
|
|
case ',':
|
|
case '?':
|
|
case ':':
|
|
case AND:
|
|
case OR:
|
|
case POSTINCR:
|
|
case POSTDECR:
|
|
case PLUSPLUS:
|
|
case MINMIN:
|
|
case '(':
|
|
case '.':
|
|
case ARROW:
|
|
default:
|
|
/* OK with lint */
|
|
break;
|
|
}
|
|
}
|
|
|
|
PRIVATE
|
|
lint_enum_arith(l_fund, oper, r_fund)
|
|
int l_fund, oper, r_fund;
|
|
{
|
|
if ( l_fund == ENUM
|
|
&& r_fund != CHAR
|
|
&& r_fund != SHORT
|
|
&& r_fund != INT
|
|
) {
|
|
warning("%s on enum and %s",
|
|
symbol2str(oper), symbol2str(r_fund));
|
|
}
|
|
else
|
|
if ( r_fund == ENUM
|
|
&& l_fund != CHAR
|
|
&& l_fund != SHORT
|
|
&& l_fund != INT
|
|
) {
|
|
warning("%s on %s and enum",
|
|
symbol2str(oper), symbol2str(l_fund));
|
|
}
|
|
}
|
|
|
|
PRIVATE
|
|
lint_conversion(from_expr, to_fund)
|
|
struct expr *from_expr;
|
|
int to_fund;
|
|
{
|
|
register int from_fund = from_expr->ex_type->tp_fund;
|
|
|
|
/* was there an attempt to reduce the type of the from_expr
|
|
of the form
|
|
expr & 0377
|
|
or something like this?
|
|
*/
|
|
if (from_expr->ex_class == Oper && from_expr->OP_OPER == INT2INT) {
|
|
from_expr = from_expr->OP_LEFT;
|
|
}
|
|
if (from_expr->ex_class == Oper && from_expr->OP_OPER == '&') {
|
|
struct expr *bits =
|
|
is_cp_cst(from_expr->OP_LEFT) ? from_expr->OP_LEFT :
|
|
is_cp_cst(from_expr->OP_RIGHT) ? from_expr->OP_RIGHT :
|
|
0;
|
|
|
|
if (bits) {
|
|
arith val = bits->VL_VALUE;
|
|
|
|
if (val < 256)
|
|
from_fund = CHAR;
|
|
else if (val < 256)
|
|
from_fund = SHORT;
|
|
}
|
|
}
|
|
if (numsize(from_fund) > numsize(to_fund)) {
|
|
awarning("conversion from %s to %s may lose accuracy",
|
|
symbol2str(from_fund), symbol2str(to_fund));
|
|
}
|
|
}
|
|
|
|
PRIVATE int
|
|
numsize(fund)
|
|
{
|
|
switch (fund) {
|
|
case CHAR: return 1;
|
|
case SHORT: return 2;
|
|
case INT: return 3;
|
|
case ENUM: return 3;
|
|
case LONG: return 4;
|
|
case FLOAT: return 5;
|
|
case DOUBLE: return 6;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
lint_ret_conv(from_expr)
|
|
struct expr *from_expr;
|
|
{
|
|
lint_conversion(from_expr, func_type->tp_fund);
|
|
}
|
|
|
|
lint_ptr_conv(from, to)
|
|
short from, to;
|
|
{
|
|
/* X -> X ok -- this includes struct -> struct, of any size
|
|
* X -> CHAR ok
|
|
* DOUBLE -> X ok
|
|
* FLOAT -> LONG -> INT -> SHORT ok
|
|
*/
|
|
if (from == to)
|
|
return;
|
|
|
|
if (to == CHAR)
|
|
return;
|
|
|
|
if (from == DOUBLE)
|
|
return;
|
|
|
|
switch (from) {
|
|
case FLOAT:
|
|
switch (to) {
|
|
case LONG:
|
|
case INT:
|
|
case SHORT:
|
|
return;
|
|
}
|
|
break;
|
|
case LONG:
|
|
switch (to) {
|
|
case INT:
|
|
case SHORT:
|
|
return;
|
|
}
|
|
break;
|
|
case INT:
|
|
switch (to) {
|
|
case SHORT:
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (from == VOID) {
|
|
/* OK any which way */
|
|
}
|
|
else
|
|
if (from == CHAR) {
|
|
hwarning("pointer to char may not align correctly for a %s",
|
|
symbol2str(to));
|
|
}
|
|
else {
|
|
warning("pointer to %s may not align correctly for a %s",
|
|
symbol2str(from), symbol2str(to));
|
|
}
|
|
}
|
|
|
|
lint_relop(left, right, oper)
|
|
struct expr *left, *right;
|
|
int oper; /* '<', '>', LESSEQ, GREATEREQ, EQUAL, NOTEQUAL */
|
|
{
|
|
/* left operand may be converted */
|
|
if ( left->ex_class == Oper
|
|
&& left->OP_OPER == INT2INT
|
|
) {
|
|
left = left->OP_RIGHT;
|
|
}
|
|
|
|
/* <unsigned> <relop> <neg-const|0> is doubtful */
|
|
if ( left->ex_type->tp_unsigned
|
|
&& right->ex_class == Value
|
|
&& right->VL_CLASS == Const
|
|
) {
|
|
if (!right->ex_type->tp_unsigned && right->VL_VALUE < 0) {
|
|
warning("unsigned compared to negative constant");
|
|
}
|
|
if (right->VL_VALUE == 0) {
|
|
switch (oper) {
|
|
case '<':
|
|
warning("unsigned < 0 will always fail");
|
|
break;
|
|
|
|
case LESSEQ:
|
|
warning("unsigned <= 0 is probably wrong");
|
|
break;
|
|
|
|
case GREATEREQ:
|
|
warning("unsigned >= 0 will always succeed");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* <char> <relop> <neg-const> is undefined */
|
|
if ( left->ex_type->tp_fund == CHAR
|
|
&& right->ex_class == Value
|
|
&& right->VL_CLASS == Const
|
|
&& (right->VL_VALUE < 0 || right->VL_VALUE > 127)
|
|
) {
|
|
warning("character compared to negative constant");
|
|
}
|
|
}
|
|
|
|
#endif /* LINT */
|