#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#if defined(__RCSID)
__RCSID("$NetBSD: chk.c,v 1.72 2025/05/24 07:38:59 rillig Exp $");
#endif
#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "lint2.h"
static void check_used_not_defined(const hte_t *);
static void check_defined_not_used(const hte_t *);
static void check_declared_not_used_or_defined(const hte_t *);
static void check_multiple_definitions(const hte_t *);
static void chkvtui(const hte_t *, const sym_t *, const sym_t *);
static void chkvtdi(const hte_t *, const sym_t *, const sym_t *);
static void chkfaui(const hte_t *, const sym_t *, const sym_t *);
static void chkau(const hte_t *, int, const sym_t *, const sym_t *,
const pos_t *, const fcall_t *, const fcall_t *,
const type_t *, const type_t *);
static void check_return_values(const hte_t *, const sym_t *);
static void check_argument_declarations(const hte_t *,
const sym_t *, const sym_t *);
static void printflike(const hte_t *, const fcall_t *,
int, const char *, const type_t **);
static void scanflike(const hte_t *, const fcall_t *,
int, const char *, const type_t **);
static void bad_format_string(const hte_t *, const fcall_t *);
static void inconsistent_arguments(const hte_t *, const fcall_t *, int);
static void too_few_arguments(const hte_t *, const fcall_t *);
static void too_many_arguments(const hte_t *, const fcall_t *);
static bool types_compatible(const type_t *, const type_t *,
bool, bool, bool, bool *);
static bool prototypes_compatible(const type_t *, const type_t *, bool *);
static bool matches_no_arg_function(const type_t *, bool *);
void
mark_main_as_used(void)
{
hte_t *hte;
if ((hte = htab_search("main", false)) != NULL)
hte->h_used = true;
}
void
check_name(const hte_t *hte)
{
sym_t *sym, *def, *pdecl, *decl;
if (!uflag) {
check_used_not_defined(hte);
check_defined_not_used(hte);
if (xflag)
check_declared_not_used_or_defined(hte);
}
check_multiple_definitions(hte);
def = pdecl = decl = NULL;
for (sym = hte->h_syms; sym != NULL; sym = sym->s_next) {
if (def == NULL && (sym->s_def == DEF || sym->s_def == TDEF))
def = sym;
if (pdecl == NULL && sym->s_def == DECL &&
TP(sym->s_type)->t_tspec == FUNC &&
TP(sym->s_type)->t_proto) {
pdecl = sym;
}
if (decl == NULL && sym->s_def == DECL)
decl = sym;
}
if (pdecl != NULL)
decl = pdecl;
chkvtui(hte, def, decl);
chkvtdi(hte, def, decl);
chkfaui(hte, def, decl);
check_return_values(hte, def);
check_argument_declarations(hte, def, decl);
}
static void
check_used_not_defined(const hte_t *hte)
{
fcall_t *fcall;
usym_t *usym;
if (!hte->h_used || hte->h_def)
return;
if ((fcall = hte->h_calls) != NULL) {
msg(0, hte->h_name, mkpos(&fcall->f_pos));
} else if ((usym = hte->h_usyms) != NULL) {
msg(0, hte->h_name, mkpos(&usym->u_pos));
}
}
static void
check_defined_not_used(const hte_t *hte)
{
sym_t *sym;
if (!hte->h_def || hte->h_used)
return;
for (sym = hte->h_syms; sym != NULL; sym = sym->s_next) {
if (sym->s_def == DEF || sym->s_def == TDEF) {
msg(1, hte->h_name, mkpos(&sym->s_pos));
break;
}
}
}
static void
check_declared_not_used_or_defined(const hte_t *hte)
{
sym_t *sym;
if (hte->h_syms == NULL || hte->h_used || hte->h_def)
return;
sym = hte->h_syms;
if (TP(sym->s_type)->t_tspec == FUNC)
return;
if (sym->s_def != DECL)
errx(1, "internal error: check_declared_not_used_or_defined");
msg(2, hte->h_name, mkpos(&sym->s_pos));
}
static void
check_multiple_definitions(const hte_t *hte)
{
sym_t *sym, *def1;
if (!hte->h_def)
return;
def1 = NULL;
for (sym = hte->h_syms; sym != NULL; sym = sym->s_next) {
if (sym->s_def != DEF && (!sflag || sym->s_def != TDEF))
continue;
if (sym->s_inline)
continue;
if (def1 == NULL) {
def1 = sym;
continue;
}
msg(3, hte->h_name, mkpos(&def1->s_pos), mkpos(&sym->s_pos));
}
}
static void
chkvtui(const hte_t *hte, const sym_t *def, const sym_t *decl)
{
fcall_t *call;
type_t *tp1, *tp2;
bool dowarn, eq;
tspec_t t1;
if (hte->h_calls == NULL)
return;
if (def == NULL)
def = decl;
if (def == NULL)
return;
t1 = (tp1 = TP(def->s_type)->t_subt)->t_tspec;
for (call = hte->h_calls; call != NULL; call = call->f_next) {
tp2 = TP(call->f_type)->t_subt;
eq = types_compatible(tp1, tp2,
true, false, false, (dowarn = false, &dowarn));
if (!call->f_rused) {
if ((t1 == STRUCT || t1 == UNION) && !eq) {
msg(17, hte->h_name,
mkpos(&def->s_pos), mkpos(&call->f_pos));
}
continue;
}
if (!eq || (sflag && dowarn)) {
msg(4, hte->h_name,
mkpos(&def->s_pos), mkpos(&call->f_pos));
}
}
}
static void
chkvtdi(const hte_t *hte, const sym_t *def, const sym_t *decl)
{
if (def == NULL)
def = decl;
if (def == NULL)
return;
const type_t *tp1 = TP(def->s_type);
for (sym_t *sym = hte->h_syms; sym != NULL; sym = sym->s_next) {
if (sym == def)
continue;
const type_t *tp2 = TP(sym->s_type);
bool dowarn = false;
bool is_func = tp1->t_tspec == FUNC && tp2->t_tspec == FUNC;
const type_t *xt1 = is_func ? tp1->t_subt : tp1;
const type_t *xt2 = is_func ? tp2->t_subt : tp2;
bool eq = types_compatible(xt1, xt2,
is_func, false, false, &dowarn);
if (!eq || (sflag && dowarn)) {
msg(5, hte->h_name,
is_func ? "returns" : "has type",
type_name(xt1), mkpos(&def->s_pos),
type_name(xt2), mkpos(&sym->s_pos));
}
}
}
static int
total_args(int n, const type_t **tpp)
{
for (; *tpp != NULL; tpp++)
n++;
return n;
}
static void
chkfaui(const hte_t *hte, const sym_t *def, const sym_t *decl)
{
const type_t *tp1, *tp2, **ap1, **ap2;
const pos_t *pos1p = NULL;
fcall_t *calls, *call, *call1;
int n, as;
arginf_t *ai;
if ((calls = hte->h_calls) == NULL)
return;
tp1 = NULL;
call1 = NULL;
if (def != NULL) {
if ((tp1 = TP(def->s_type))->t_tspec != FUNC)
return;
pos1p = &def->s_pos;
} else if (decl != NULL && TP(decl->s_type)->t_proto) {
if ((tp1 = TP(decl->s_type))->t_tspec != FUNC)
return;
pos1p = &decl->s_pos;
}
if (tp1 == NULL) {
call1 = calls;
calls = calls->f_next;
if ((tp1 = TP(call1->f_type))->t_tspec != FUNC)
return;
pos1p = &call1->f_pos;
}
n = 1;
for (call = calls; call != NULL; call = call->f_next) {
if ((tp2 = TP(call->f_type))->t_tspec != FUNC)
continue;
ap1 = tp1->t_args;
ap2 = tp2->t_args;
n = 0;
while (*ap1 != NULL && *ap2 != NULL) {
if (def != NULL && def->s_check_only_first_args &&
n >= def->s_check_num_args)
break;
n++;
chkau(hte, n, def, decl, pos1p, call1, call,
*ap1, *ap2);
ap1++;
ap2++;
}
if (*ap1 == *ap2) {
} else if (def != NULL && def->s_check_only_first_args &&
n >= def->s_check_num_args) {
} else if (*ap2 != NULL && tp1->t_proto && tp1->t_vararg) {
} else {
msg(7, hte->h_name, total_args(n, ap1), mkpos(pos1p),
total_args(n, ap2), mkpos(&call->f_pos));
continue;
}
if (def == NULL || (!def->s_printflike && !def->s_scanflike))
continue;
as = def->s_printflike
? def->s_printflike_arg
: def->s_scanflike_arg;
for (ai = call->f_args; ai != NULL; ai = ai->a_next) {
if (ai->a_num == as)
break;
}
if (ai == NULL || !ai->a_fmt)
continue;
if (def->s_printflike) {
printflike(hte, call, n, ai->a_fstrg, ap2);
} else {
scanflike(hte, call, n, ai->a_fstrg, ap2);
}
}
}
static void
chkau(const hte_t *hte,
int n, const sym_t *def, const sym_t *decl,
const pos_t *pos1p, const fcall_t *call1, const fcall_t *call,
const type_t *arg1, const type_t *arg2)
{
bool promote, asgn, dowarn;
tspec_t t1, t2;
arginf_t *ai, *ai1;
promote = def != NULL && def->s_old_style_function;
asgn = def != NULL || (decl != NULL && TP(decl->s_type)->t_proto);
dowarn = false;
if (types_compatible(arg1, arg2, true, promote, asgn, &dowarn) &&
(!sflag || !dowarn))
return;
t1 = arg1->t_tspec;
t2 = arg2->t_tspec;
if (is_integer(t1) && is_integer(t2) &&
!arg1->t_is_enum && !arg2->t_is_enum) {
if (promote) {
if (t1 == FLOAT) {
t1 = DOUBLE;
} else if (t1 == CHAR || t1 == SCHAR) {
t1 = INT;
} else if (t1 == UCHAR) {
t1 = tflag ? UINT : INT;
} else if (t1 == SHORT) {
t1 = INT;
} else if (t1 == USHORT) {
t1 = INT_MAX < USHRT_MAX || tflag ? UINT : INT;
}
}
if (signed_type(t1) == signed_type(t2)) {
ai1 = call1 != NULL ? call1->f_args : NULL;
for ( ; ai1 != NULL; ai1 = ai1->a_next) {
if (ai1->a_num == n)
break;
}
for (ai = call->f_args; ai != NULL; ai = ai->a_next) {
if (ai->a_num == n)
break;
}
if (ai1 == NULL && ai == NULL) {
if (!hflag)
return;
} else if (ai1 == NULL || ai == NULL) {
if (ai == NULL)
ai = ai1;
if (ai->a_zero || ai->a_pcon)
return;
} else {
if (!ai1->a_ncon && !ai->a_ncon)
return;
}
}
} else if (t1 == PTR && is_integer(t2)) {
for (ai = call->f_args; ai != NULL; ai = ai->a_next) {
if (ai->a_num == n)
break;
}
if (ai != NULL && ai->a_zero)
return;
}
msg(6, hte->h_name, n, type_name(arg1), mkpos(pos1p),
type_name(arg2), mkpos(&call->f_pos));
}
static void
printflike(const hte_t *hte, const fcall_t *call,
int n, const char *fmt, const type_t **ap)
{
const char *fp;
char fc;
bool fwidth, prec, left, sign, space, alt, zero;
tspec_t sz, t1, t2 = NO_TSPEC;
const type_t *tp;
fp = fmt;
fc = *fp++;
for (;;) {
if (fc == '\0') {
if (*ap != NULL)
too_many_arguments(hte, call);
break;
}
if (fc != '%') {
bad_format_string(hte, call);
break;
}
fc = *fp++;
fwidth = prec = left = sign = space = alt = zero = false;
sz = NO_TSPEC;
for (;;) {
if (fc == '-') {
if (left)
break;
left = true;
} else if (fc == '+') {
if (sign)
break;
sign = true;
} else if (fc == ' ') {
if (space)
break;
space = true;
} else if (fc == '#') {
if (alt)
break;
alt = true;
} else if (fc == '0') {
if (zero)
break;
zero = true;
} else {
break;
}
fc = *fp++;
}
if (ch_isdigit(fc)) {
fwidth = true;
do { fc = *fp++; } while (ch_isdigit(fc));
} else if (fc == '*') {
fwidth = true;
fc = *fp++;
if ((tp = *ap++) == NULL) {
too_few_arguments(hte, call);
break;
}
n++;
if ((t1 = tp->t_tspec) != INT && (hflag || t1 != UINT))
inconsistent_arguments(hte, call, n);
}
if (fc == '.') {
fc = *fp++;
prec = true;
if (ch_isdigit(fc)) {
do {
fc = *fp++;
} while (ch_isdigit(fc));
} else if (fc == '*') {
fc = *fp++;
if ((tp = *ap++) == NULL) {
too_few_arguments(hte, call);
break;
}
n++;
if (tp->t_tspec != INT)
inconsistent_arguments(hte, call, n);
} else {
bad_format_string(hte, call);
break;
}
}
if (fc == 'h') {
sz = SHORT;
} else if (fc == 'l') {
sz = LONG;
} else if (fc == 'q') {
sz = LLONG;
} else if (fc == 'L') {
sz = LDOUBLE;
}
if (sz != NO_TSPEC)
fc = *fp++;
if (fc == '%') {
if (sz != NO_TSPEC || left || sign || space ||
alt || zero || prec || fwidth) {
bad_format_string(hte, call);
}
fc = *fp++;
continue;
}
if (fc == '\0') {
bad_format_string(hte, call);
break;
}
if ((tp = *ap++) == NULL) {
too_few_arguments(hte, call);
break;
}
n++;
if ((t1 = tp->t_tspec) == PTR)
t2 = tp->t_subt->t_tspec;
if (fc == 'd' || fc == 'i') {
if (alt || sz == LDOUBLE) {
bad_format_string(hte, call);
break;
}
int_conv:
if (sz == LONG) {
if (t1 != LONG && (hflag || t1 != ULONG))
inconsistent_arguments(hte, call, n);
} else if (sz == LLONG) {
if (t1 != LLONG && (hflag || t1 != ULLONG))
inconsistent_arguments(hte, call, n);
} else {
if (t1 != INT && (hflag || t1 != UINT))
inconsistent_arguments(hte, call, n);
}
} else if (fc == 'o' || fc == 'u' || fc == 'x' || fc == 'X') {
if ((alt && fc == 'u') || sz == LDOUBLE)
bad_format_string(hte, call);
uint_conv:
if (sz == LONG) {
if (t1 != ULONG && (hflag || t1 != LONG))
inconsistent_arguments(hte, call, n);
} else if (sz == LLONG) {
if (t1 != ULLONG && (hflag || t1 != LLONG))
inconsistent_arguments(hte, call, n);
} else if (sz == SHORT) {
if (t1 != UINT && t1 != INT)
inconsistent_arguments(hte, call, n);
} else {
if (t1 != UINT && (hflag || t1 != INT))
inconsistent_arguments(hte, call, n);
}
} else if (fc == 'D' || fc == 'O' || fc == 'U') {
if ((alt && fc != 'O') || sz != NO_TSPEC || !tflag)
bad_format_string(hte, call);
sz = LONG;
if (fc == 'D') {
goto int_conv;
} else {
goto uint_conv;
}
} else if (fc == 'f' || fc == 'e' || fc == 'E' ||
fc == 'g' || fc == 'G') {
if (sz == NO_TSPEC)
sz = DOUBLE;
if (sz != DOUBLE && sz != LDOUBLE)
bad_format_string(hte, call);
if (t1 != sz)
inconsistent_arguments(hte, call, n);
} else if (fc == 'c') {
if (sz != NO_TSPEC || alt || zero)
bad_format_string(hte, call);
if (t1 != INT)
inconsistent_arguments(hte, call, n);
} else if (fc == 's') {
if (sz != NO_TSPEC || alt || zero)
bad_format_string(hte, call);
if (t1 != PTR ||
(t2 != CHAR && t2 != UCHAR && t2 != SCHAR)) {
inconsistent_arguments(hte, call, n);
}
} else if (fc == 'p') {
if (fwidth || prec || sz != NO_TSPEC || alt || zero)
bad_format_string(hte, call);
if (t1 != PTR || (hflag && t2 != VOID))
inconsistent_arguments(hte, call, n);
} else if (fc == 'n') {
if (fwidth || prec || alt || zero || sz == LDOUBLE)
bad_format_string(hte, call);
if (t1 != PTR) {
inconsistent_arguments(hte, call, n);
} else if (sz == LONG) {
if (t2 != LONG && t2 != ULONG)
inconsistent_arguments(hte, call, n);
} else if (sz == SHORT) {
if (t2 != SHORT && t2 != USHORT)
inconsistent_arguments(hte, call, n);
} else {
if (t2 != INT && t2 != UINT)
inconsistent_arguments(hte, call, n);
}
} else {
bad_format_string(hte, call);
break;
}
fc = *fp++;
}
}
static void
scanflike(const hte_t *hte, const fcall_t *call,
int n, const char *fmt, const type_t **ap)
{
const char *fp;
char fc;
bool noasgn, fwidth;
tspec_t sz, t1 = NO_TSPEC, t2 = NO_TSPEC;
const type_t *tp = NULL;
fp = fmt;
fc = *fp++;
for (;;) {
if (fc == '\0') {
if (*ap != NULL)
too_many_arguments(hte, call);
break;
}
if (fc != '%') {
bad_format_string(hte, call);
break;
}
fc = *fp++;
noasgn = fwidth = false;
sz = NO_TSPEC;
if (fc == '*') {
noasgn = true;
fc = *fp++;
}
if (ch_isdigit(fc)) {
fwidth = true;
do { fc = *fp++; } while (ch_isdigit(fc));
}
if (fc == 'h') {
sz = SHORT;
} else if (fc == 'l') {
sz = LONG;
} else if (fc == 'q') {
sz = LLONG;
} else if (fc == 'L') {
sz = LDOUBLE;
}
if (sz != NO_TSPEC)
fc = *fp++;
if (fc == '%') {
if (sz != NO_TSPEC || noasgn || fwidth)
bad_format_string(hte, call);
fc = *fp++;
continue;
}
if (!noasgn) {
if ((tp = *ap++) == NULL) {
too_few_arguments(hte, call);
break;
}
n++;
if ((t1 = tp->t_tspec) == PTR)
t2 = tp->t_subt->t_tspec;
}
if (fc == 'd' || fc == 'i' || fc == 'n') {
if (sz == LDOUBLE)
bad_format_string(hte, call);
if (sz != SHORT && sz != LONG && sz != LLONG)
sz = INT;
conv:
if (!noasgn) {
if (t1 != PTR) {
inconsistent_arguments(hte, call, n);
} else if (t2 != signed_type(sz)) {
inconsistent_arguments(hte, call, n);
} else if (hflag && t2 != sz) {
inconsistent_arguments(hte, call, n);
} else if (tp->t_subt->t_const) {
inconsistent_arguments(hte, call, n);
}
}
} else if (fc == 'o' || fc == 'u' || fc == 'x') {
if (sz == LDOUBLE)
bad_format_string(hte, call);
if (sz == SHORT) {
sz = USHORT;
} else if (sz == LONG) {
sz = ULONG;
} else if (sz == LLONG) {
sz = ULLONG;
} else {
sz = UINT;
}
goto conv;
} else if (fc == 'D') {
if (sz != NO_TSPEC || !tflag)
bad_format_string(hte, call);
sz = LONG;
goto conv;
} else if (fc == 'O') {
if (sz != NO_TSPEC || !tflag)
bad_format_string(hte, call);
sz = ULONG;
goto conv;
} else if (fc == 'X') {
if (sz != NO_TSPEC || !tflag)
bad_format_string(hte, call);
sz = ULONG;
goto conv;
} else if (fc == 'E') {
if (sz != NO_TSPEC || !tflag)
bad_format_string(hte, call);
sz = DOUBLE;
goto conv;
} else if (fc == 'F') {
if (sz != NO_TSPEC || !tflag)
bad_format_string(hte, call);
sz = DOUBLE;
goto conv;
} else if (fc == 'G') {
if (sz != NO_TSPEC && sz != LONG && sz != LDOUBLE)
bad_format_string(hte, call);
goto fconv;
} else if (fc == 'e' || fc == 'f' || fc == 'g') {
fconv:
if (sz == NO_TSPEC) {
sz = FLOAT;
} else if (sz == LONG) {
sz = DOUBLE;
} else if (sz != LDOUBLE) {
bad_format_string(hte, call);
sz = FLOAT;
}
goto conv;
} else if (fc == 's' || fc == '[' || fc == 'c') {
if (sz != NO_TSPEC)
bad_format_string(hte, call);
if (fc == '[') {
if ((fc = *fp++) == '-') {
bad_format_string(hte, call);
fc = *fp++;
}
if (fc != ']') {
bad_format_string(hte, call);
if (fc == '\0')
break;
}
}
if (!noasgn) {
if (t1 != PTR) {
inconsistent_arguments(hte, call, n);
} else if (t2 != CHAR && t2 != UCHAR &&
t2 != SCHAR) {
inconsistent_arguments(hte, call, n);
}
}
} else if (fc == 'p') {
if (sz != NO_TSPEC)
bad_format_string(hte, call);
if (!noasgn) {
if (t1 != PTR || t2 != PTR) {
inconsistent_arguments(hte, call, n);
} else if (tp->t_subt->t_subt->t_tspec!=VOID) {
if (hflag)
inconsistent_arguments(hte, call, n);
}
}
} else {
bad_format_string(hte, call);
break;
}
fc = *fp++;
}
}
static void
bad_format_string(const hte_t *hte, const fcall_t *call)
{
msg(13, hte->h_name, mkpos(&call->f_pos));
}
static void
inconsistent_arguments(const hte_t *hte, const fcall_t *call, int n)
{
msg(14, hte->h_name, mkpos(&call->f_pos), n);
}
static void
too_few_arguments(const hte_t *hte, const fcall_t *call)
{
msg(15, hte->h_name, mkpos(&call->f_pos));
}
static void
too_many_arguments(const hte_t *hte, const fcall_t *call)
{
msg(16, hte->h_name, mkpos(&call->f_pos));
}
static const char ignorelist[][8] = {
"memcpy",
"memmove",
"memset",
"printf",
"strcat",
"strcpy",
"vprintf",
};
static void
check_return_values(const hte_t *hte, const sym_t *def)
{
fcall_t *call;
bool used, ignored;
if (def == NULL)
return;
if (hte->h_calls == NULL)
return;
if (def->s_function_has_return_value) {
if (!hflag)
return;
if (hflag && bsearch(hte->h_name, ignorelist,
sizeof(ignorelist) / sizeof(ignorelist[0]),
sizeof(ignorelist[0]),
(int (*)(const void *, const void *))strcmp) != NULL)
return;
used = ignored = false;
for (call = hte->h_calls; call != NULL; call = call->f_next) {
used |= call->f_rused || call->f_rdisc;
ignored |= !call->f_rused && !call->f_rdisc;
}
if (!used && ignored) {
msg(8, hte->h_name);
} else if (used && ignored) {
msg(9, hte->h_name);
}
} else {
for (call = hte->h_calls; call != NULL; call = call->f_next) {
if (call->f_rused)
msg(10, hte->h_name, mkpos(&call->f_pos));
}
}
}
static void
check_argument_declarations(const hte_t *hte,
const sym_t *def, const sym_t *decl)
{
bool osdef, eq, dowarn;
int n;
const sym_t *sym1, *sym;
const type_t **ap1, **ap2, *tp1, *tp2;
osdef = false;
if (def != NULL) {
osdef = def->s_old_style_function;
sym1 = def;
} else if (decl != NULL && TP(decl->s_type)->t_proto) {
sym1 = decl;
} else {
return;
}
if (TP(sym1->s_type)->t_tspec != FUNC)
return;
for (sym = hte->h_syms; sym != NULL; sym = sym->s_next) {
if (sym == sym1 || !TP(sym->s_type)->t_proto)
continue;
ap1 = TP(sym1->s_type)->t_args;
ap2 = TP(sym->s_type)->t_args;
n = 0;
while (*ap1 != NULL && *ap2 != NULL) {
const type_t *xt1, *xt2;
dowarn = false;
eq = types_compatible(xt1 = *ap1, xt2 = *ap2,
true, osdef, false, &dowarn);
if (!eq || dowarn) {
msg(11, hte->h_name, n + 1,
type_name(xt1), mkpos(&sym1->s_pos),
type_name(xt2), mkpos(&sym->s_pos));
}
n++;
ap1++;
ap2++;
}
if (*ap1 == *ap2) {
tp1 = TP(sym1->s_type);
tp2 = TP(sym->s_type);
if (tp1->t_vararg == tp2->t_vararg)
continue;
if (tp2->t_vararg && sym1->s_check_only_first_args &&
sym1->s_check_num_args == n && !sflag) {
continue;
}
}
msg(12, hte->h_name,
total_args(n, ap1), mkpos(&sym1->s_pos),
total_args(n, ap2), mkpos(&sym->s_pos));
}
}
static bool
types_compatible(const type_t *tp1, const type_t *tp2,
bool ignqual, bool promot, bool asgn, bool *dowarn)
{
tspec_t t, to;
int indir;
to = NO_TSPEC;
indir = 0;
while (tp1 != NULL && tp2 != NULL) {
t = tp1->t_tspec;
if (promot) {
if (t == FLOAT) {
t = DOUBLE;
} else if (t == CHAR || t == SCHAR) {
t = INT;
} else if (t == UCHAR) {
t = tflag ? UINT : INT;
} else if (t == SHORT) {
t = INT;
} else if (t == USHORT) {
t = INT_MAX < USHRT_MAX || tflag ? UINT : INT;
}
}
if (asgn && to == PTR) {
if (indir == 1 && (t == VOID || tp2->t_tspec == VOID))
return true;
}
if (t != tp2->t_tspec) {
if (sflag || hflag || to != PTR)
return false;
if (signed_type(t) != signed_type(tp2->t_tspec))
return false;
}
if (tp1->t_is_enum && tp2->t_is_enum) {
if (tp1->t_istag && tp2->t_istag) {
return tp1->t_tag == tp2->t_tag;
} else if (tp1->t_istynam && tp2->t_istynam) {
return tp1->t_tynam == tp2->t_tynam;
} else if (tp1->t_isuniqpos && tp2->t_isuniqpos) {
return (tp1->t_uniqpos.p_line ==
tp2->t_uniqpos.p_line &&
tp1->t_uniqpos.p_file ==
tp2->t_uniqpos.p_file &&
tp1->t_uniqpos.p_uniq ==
tp2->t_uniqpos.p_uniq);
} else {
return false;
}
}
if (asgn && indir == 1) {
if (!tp1->t_const && tp2->t_const)
return false;
if (!tp1->t_volatile && tp2->t_volatile)
return false;
} else if (!ignqual && !tflag) {
if (tp1->t_const != tp2->t_const)
return false;
if (tp1->t_const != tp2->t_const)
return false;
}
if (t == STRUCT || t == UNION) {
if (tp1->t_istag && tp2->t_istag) {
return tp1->t_tag == tp2->t_tag;
} else if (tp1->t_istynam && tp2->t_istynam) {
return tp1->t_tynam == tp2->t_tynam;
} else if (tp1->t_isuniqpos && tp2->t_isuniqpos) {
return (tp1->t_uniqpos.p_line ==
tp2->t_uniqpos.p_line &&
tp1->t_uniqpos.p_file ==
tp2->t_uniqpos.p_file &&
tp1->t_uniqpos.p_uniq ==
tp2->t_uniqpos.p_uniq);
} else {
return false;
}
}
if (t == ARRAY && tp1->t_dim != tp2->t_dim) {
if (tp1->t_dim != 0 && tp2->t_dim != 0)
return false;
}
if (t == FUNC) {
if (tp1->t_proto && tp2->t_proto) {
if (!prototypes_compatible(tp1, tp2, dowarn))
return false;
} else if (tp1->t_proto) {
if (!matches_no_arg_function(tp1, dowarn))
return false;
} else if (tp2->t_proto) {
if (!matches_no_arg_function(tp2, dowarn))
return false;
}
}
tp1 = tp1->t_subt;
tp2 = tp2->t_subt;
ignqual = promot = false;
to = t;
indir++;
}
return tp1 == tp2;
}
static bool
prototypes_compatible(const type_t *tp1, const type_t *tp2, bool *dowarn)
{
if (tp1->t_vararg != tp2->t_vararg)
return false;
const type_t **a1 = tp1->t_args;
const type_t **a2 = tp2->t_args;
for (; *a1 != NULL && *a2 != NULL; a1++, a2++)
if (!types_compatible(*a1, *a2, true, false, false, dowarn))
return false;
return *a1 == *a2;
}
static bool
matches_no_arg_function(const type_t *tp, bool *dowarn)
{
if (tp->t_vararg && dowarn != NULL)
*dowarn = true;
for (const type_t **arg = tp->t_args; *arg != NULL; arg++) {
tspec_t t = (*arg)->t_tspec;
if (t == FLOAT)
return false;
if (t == CHAR || t == SCHAR || t == UCHAR)
return false;
if (t == SHORT || t == USHORT)
return false;
}
return true;
}