/* * (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 "parameters.h" #ifdef LINT #include /* for st_free */ #include "interface.h" #ifdef ANSI #include #endif /* ANSI */ #include "arith.h" /* definition arith */ #include "label.h" /* definition label */ #include "expr.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 == VOID) 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; } /* 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; } } } /* 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 */