Files
nethack/include/tradstdc.h
nhmall 978ec6a3a7 augment include/extern.h with nonnull arg info
Define some macros in include/tradstdc.h, for compilers that support
__attribute__((nonnull)), to assist in identifying which parameters
on functions are not supposed to be null pointers.

Next, for the majority of functions declared in include/extern.h, this
adds the appropriate macro that matches the actual use of each function's
parameters. The additions were done after performing some analysis.

These were the rules that were followed when determining which function
parameters should be nonnul, and which are nullable:

    1. If the first use of, or reference to, the pointer parameter in the
       function is a dereference, then the parameter will be considered
       nonnull.

    2. If there is code in the function that tests for the pointer parameter
       being null, and adjusts the code-path accordingly so that no segfault
       will occur, then the parameter will not be considered nonnull (it can
       be null).

The use of the nonnull attributes allows the compiler to detect code in
callers of the function where a null parameter could get passed to the function.

If a warning is received the developer will have to do one of the following:

     - If the null being passed to the function is now appropriate,
       and the function should be able to expect a null parameter, then the
       NONNULLxxx macro will have to be removed from the function's prototype.

    or

     - If the null being passed to the function is not appropriate,
       correct the caller so it is not passing null.

    or

     - If the warning is about comparing to null, it may indicate an
       unnecessary null check in the code involved. If it is deemed to be
       unnecessary, it can then be removed.

Some static analysis tools apparently can work with the attribute, as well.

Following this, it was discovered that some functions were using one of the
(now) nonnull parameters in the first argument to the 'is_art(obj, ART)'
macro, which is defined like so:
 =>   #define is_art(o,art) ((o) && (o)->oartifact == (art))

That macro expansion inline resulted in a diagnostic warning because of the
'(o)' portion of the expanded macro, anywhere the macro was used with one of
the nonnull parameters. A test against null for a 'nonnull parameter' causes
a diagnostic warning.

To work around that, I replaced the is_art() macro with a function in
artifact.c, that accomplishes the same thing as the macro.

 =>   boolean
      is_art(struct obj *obj, int art)
      {
          if (obj && obj->oartifact == art)
              return TRUE;
          return FALSE;
      }

Some documentation...

These are the macros that have been defined for use when specifying the nonnull
parameters in a function prototype:

   ----------------------------------------------------------------------------
   |      Macro     |              Purpose                                    |
   +----------------+---------------------------------------------------------+
   | NONULL         | The function return value is never NULL.                |
   +----------------+---------------------------------------------------------+
   | NONNULLPTRS    | Every pointer argument is declared nonnull.             |
   +----------------+---------------------------------------------------------+
   | NONNULLARG1    | The 1st argument is declared nonnull.                   |
   +----------------+---------------------------------------------------------+
   | NONNULLARG2    | The 2nd argument is declared nonnull.                   |
   +----------------+---------------------------------------------------------+
   | NONNULLARG3    | The 3rd argument is declared nonnull.                   |
   +----------------+---------------------------------------------------------+
   | NONNULLARG4    | The 4th argument is declared nonnull (not used).        |
   +----------------+---------------------------------------------------------+
   | NONNULLARG5    | The 5th argument is declared nonnull.                   |
   +----------------+---------------------------------------------------------+
   | NONNULLARG7    | The 7th argument is declared nonnull (bhit).            |
   +----------------+---------------------------------------------------------+
   | NONNULLARG12   | The 1st and 2nd arguments are declared nonnull.         |
   +----------------+---------------------------------------------------------+
   | NONNULLARG13   | The 1st and 3rd arguments are declared nonnull.         |
   +----------------+---------------------------------------------------------+
   | NONNULLARG123  | The 1st, 2nd and 3rd arguments are declared nonnull.    |
   +----------------+---------------------------------------------------------+
   | NONNULLARG14   | The 1st and 4th arguments are declared nonnull.         |
   +----------------+---------------------------------------------------------+
   | NONNULLARG134  | The 1st, 3rd and 4th arguments are declared nonnull.    |
   +----------------+---------------------------------------------------------+
   | NONNULLARG17   | The 1st and 7th arguments are declared nonnull (this    |
   |                | was a special-case added for askchain(), where the      |
   |                | arguments are spread out that way. This macro           |
   |                | could be removed if the askchain arguments in the       |
   |                | prototype and callers were changed to make the          |
   |                | nonnull arguments side-by-side).                        |
   +----------------+---------------------------------------------------------+
   | NONNULLARG145  | The 1st, 4th and 5th arguments are declared nonnull     |
   |                | (this was a special-case added for find_roll_to_hit(),  |
   |                | in uhitm.c, where the arguments are spread out that way.|
   |                | We can't just use NONNULLPTRS there because the 3rd     |
   |                | argument 'weapon' can be NULL).                         |
   +----------------+---------------------------------------------------------+
   | NONNULLARG24   | The 2nd and 4th arguments are declared nonnull (this    |
   |                | was a special-case added for query_objlist()            |
   |                | in invent.c).                                           |
   +----------------+---------------------------------------------------------+
   | NONNULLARG45   | The 4th and 5th arguments are declared nonnull (this    |
   |                | was a special-case added for do_screen_description(),   |
   |                | in pager.c, where the arguments are spread out that     |
   |                | way. We can't just use NONNULLPTRS there because the    |
   |                | 6th argument can be NULL).                              |
   +----------------+---------------------------------------------------------+
   | NO_NONNULLS    | This macro expands to nothing. It is just used to       |
   |                | mark that analysis has been done on the function,       |
   |                | and concluded that none of the arguments could be       |
   |                | marked nonnull.That distinguishes a function that has   |
   |                | not been analyzed (yet), from one that has.             |
   +----------------+---------------------------------------------------------+

The NO_NONNULLS macro is meant to place a flag on the prototype to
make people aware that an assessed function was determined to not
be eligible for nonnull parameters. It expands to nothing.

Unfortunately, that macro was added partway through this exercise, so there
aren't many instances of it in the upper parts of include/extern.h, even though
the functions there were likely assessed and categorized as not having any
eligible nonnull parameters. It just never got any macro at all, in that case.

Following the parameter usage analysis that was done, the following was
noted:

       Some NetHack functions have added a test to catch a passed null
       parameter, and exit the function early as a result, or call
       impossible(), and then exit. While that approach prevents segfaults
       from dereferencing a null parameter, the early return is silent
       (when impossible is not called anyway), and the function's true
       purpose is not fulfilled. Also, the calling function may have no
       awareness that the function did not complete its intended purpose,
       in many instances.

       Functions with such a test and early return, cannot have the parameter
       declared 'nonnull', because the code to test for 'null' will cause a
       diagnostic to be issued if the parameter is nonnull.

       It might be good to revisit some of those functions and consider,
       on a case by case basis, declaring the parameter nonnull in the
       prototype, and the test/code-path commented out.
2023-12-14 20:06:03 -05:00

468 lines
15 KiB
C

/* NetHack 3.7 tradstdc.h $NHDT-Date: 1685522034 2023/05/31 08:33:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.54 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2006. */
/* NetHack may be freely redistributed. See license for details. */
#ifndef TRADSTDC_H
#define TRADSTDC_H
#if defined(DUMB) && !defined(NOVOID)
#define NOVOID
#endif
#ifdef NOVOID
#define void int
#endif
/*
* Borland C provides enough ANSI C compatibility in its Borland C++
* mode to warrant this. But it does not set __STDC__ unless it compiles
* in its ANSI keywords only mode, which prevents use of <dos.h> and
* far pointer use.
*/
#if (defined(__STDC__) || defined(__TURBOC__)) && !defined(NOTSTDC)
#define NHSTDC
#endif
#if defined(ultrix) && defined(__STDC__) && !defined(__LANGUAGE_C)
/* Ultrix seems to be in a constant state of flux. This check attempts to
* set up ansi compatibility if it wasn't set up correctly by the compiler.
*/
#ifdef mips
#define __mips mips
#endif
#ifdef LANGUAGE_C
#define __LANGUAGE_C LANGUAGE_C
#endif
#endif
/*
* ANSI X3J11 detection.
* Makes substitutes for compatibility with the old C standard.
*/
/* Decide how to handle variable parameter lists:
* USE_STDARG means use the ANSI <stdarg.h> facilities (only ANSI compilers
* should do this, and only if the library supports it).
* USE_VARARGS means use the <varargs.h> facilities. Again, this should only
* be done if the library supports it. ANSI is *not* required for this.
* Otherwise, the kludgy old methods are used.
*/
/* #define USE_VARARGS */ /* use <varargs.h> instead of <stdarg.h> */
/* #define USE_OLDARGS */ /* don't use any variable argument facilities */
#if defined(apollo) /* Apollos have stdarg(3) but not stdarg.h */
#define USE_VARARGS
#endif
#if !defined(USE_STDARG) && !defined(USE_VARARGS) && !defined(USE_OLDARGS)
/* the old VARARGS and OLDARGS stuff is still here, but since we're
requiring C99 these days it's unlikely to be useful */
#define USE_STDARG
#endif
#ifdef NEED_VARARGS /* only define these if necessary */
/*
* These changed in 3.6.0. VA_END() provides a hidden
* closing brace to complement VA_DECL()'s hidden opening brace, so code
* started with VA_DECL() needs an extra opening brace to complement
* the explicit final closing brace. This was done so that the source
* would look less strange, where VA_DECL() appeared to introduce a
* function whose opening brace was missing; there are now visible and
* invisible braces at beginning and end. Sample usage:
void foo VA_DECL(int, arg) --macro expansion has a hidden opening brace
{ --explicit opening brace (actually introduces a nested block)
VA_START(bar);
...code for foo...
VA_END(); --expansion provides a closing brace for the nested block
} --closing brace, pairs with the hidden one in VA_DECL()
* Reading the code--or using source browsing tools which match braces--
* results in seeing a matched set of braces. Usage of VA_END() is
* potentially trickier, but nethack uses it in a straightforward manner.
*/
#ifdef USE_STDARG
#include <stdarg.h>
#define VA_DECL(typ1, var1) \
(typ1 var1, ...) \
{ \
va_list the_args;
#define VA_DECL2(typ1, var1, typ2, var2) \
(typ1 var1, typ2 var2, ...) \
{ \
va_list the_args;
#define VA_INIT(var1, typ1)
#define VA_NEXT(var1, typ1) (var1 = va_arg(the_args, typ1))
#define VA_ARGS the_args
#define VA_START(x) va_start(the_args, x)
#define VA_END() \
va_end(the_args); \
}
#define VA_PASS1(a1) a1
#if defined(ULTRIX_PROTO) && !defined(_VA_LIST_)
#define _VA_LIST_ /* prevents multiple def in stdio.h */
#endif
#else
#ifdef USE_VARARGS
#include <varargs.h>
#define VA_DECL(typ1, var1) \
(va_alist) va_dcl \
{ \
va_list the_args; \
typ1 var1;
#define VA_DECL2(typ1, var1, typ2, var2) \
(va_alist) va_dcl \
{ \
va_list the_args; \
typ1 var1; \
typ2 var2;
#define VA_ARGS the_args
#define VA_START(x) va_start(the_args)
#define VA_INIT(var1, typ1) var1 = va_arg(the_args, typ1)
#define VA_NEXT(var1, typ1) (var1 = va_arg(the_args, typ1))
#define VA_END() \
va_end(the_args); \
}
#define VA_PASS1(a1) a1
#else
/*USE_OLDARGS*/
/*
* CAVEAT: passing double (including float promoted to double) will
* almost certainly break this, as would any integer type bigger than
* sizeof (char *).
* NetHack avoids floating point, and any configuration able to use
* 'long long int' or I64P32 or the like should be using USE_STDARG.
*/
#ifndef VA_TYPE
typedef const char *vA;
#define VA_TYPE
#endif
#define VA_ARGS arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
#define VA_DECL(typ1, var1) \
(var1, VA_ARGS) typ1 var1; vA VA_ARGS; \
{
#define VA_DECL2(typ1, var1, typ2, var2) \
(var1, var2, VA_ARGS) typ1 var1; typ2 var2; vA VA_ARGS; \
{
#define VA_START(x)
#define VA_INIT(var1, typ1)
/* This is inherently risky, and should only be attempted as a
very last resort; manipulating arguments which haven't actually
been passed may or may not cause severe trouble depending on
the function-calling/argument-passing mechanism being used.
[nethack's core doesn't use VA_NEXT() so doesn't use VA_SHIFT()
either, and this definition is just retained for completeness.
lev_comp does use VA_NEXT(), but it passes all 'argX' arguments.
Note: as of 3.7.0, lev_comp doesn't exist anymore.]
*/
#define VA_SHIFT() \
(arg1 = arg2, arg2 = arg3, arg3 = arg4, arg4 = arg5, arg5 = arg6, \
arg6 = arg7, arg7 = arg8, arg8 = arg9, arg9 = 0)
#define VA_NEXT(var1, typ1) ((var1 = (typ1) arg1), VA_SHIFT(), var1)
#define VA_END() }
/* needed in pline.c, where full number of arguments is known and expected */
#define VA_PASS1(a1) \
(vA) a1, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0
#endif
#endif
#endif /* NEED_VARARGS */
/* generic pointer, always a macro; genericptr_t is usually a typedef */
#define genericptr void *
#ifndef genericptr_t
typedef genericptr genericptr_t; /* (void *) or (char *) */
#endif
#if defined(MICRO) || defined(WIN32)
/* We actually want to know which systems have an ANSI run-time library
* to know which support the %p format for printing pointers.
* Due to the presence of things like gcc, NHSTDC is not a good test.
* So we assume microcomputers have all converted to ANSI and bigger
* computers which may have older libraries give reasonable results with
* casting pointers to unsigned long int (fmt_ptr() in alloc.c).
*/
#define HAS_PTR_FMT
#endif
/*
* According to ANSI C, prototypes for old-style function definitions like
* int func(arg) short arg; { ... }
* must specify widened arguments (char and short to int, float to double),
* int func(int);
* same as how narrow arguments get passed when there is no prototype info.
* However, various compilers accept shorter arguments (char, short, etc.)
* in prototypes and do typechecking with them. Therefore this mess to
* allow the better typechecking while also allowing some prototypes for
* the ANSI compilers so people quit trying to fix the prototypes to match
* the standard and thus lose the typechecking.
*/
#if defined(MSDOS) && !defined(__GO32__)
#define UNWIDENED_PROTOTYPES
#endif
#if defined(AMIGA) && !defined(AZTEC_50)
#define UNWIDENED_PROTOTYPES
#endif
#if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
#define WIDENED_PROTOTYPES
#endif
#if defined(__MWERKS__) && defined(__BEOS__)
#define UNWIDENED_PROTOTYPES
#endif
#if defined(WIN32)
#define UNWIDENED_PROTOTYPES
#endif
#if defined(ULTRIX_PROTO) && defined(ULTRIX_CC20)
#define UNWIDENED_PROTOTYPES
#endif
#if defined(apollo)
#define UNWIDENED_PROTOTYPES
#endif
#ifndef UNWIDENED_PROTOTYPES
#if defined(NHSTDC) || defined(ULTRIX_PROTO) || defined(THINK_C)
#ifndef WIDENED_PROTOTYPES
#define WIDENED_PROTOTYPES
#endif
#endif
#endif
/* this applies to both VMS and Digital Unix/HP Tru64 */
#ifdef WIDENED_PROTOTYPES
/* ANSI C uses "value preserving rules", where 'unsigned char' and
'unsigned short' promote to 'int' if signed int is big enough to hold
all possible values, rather than traditional "sign preserving rules"
where 'unsigned char' and 'unsigned short' promote to 'unsigned int'.
However, the ANSI C rules aren't binding on non-ANSI compilers.
When DEC C (aka Compaq C, then HP C) is in non-standard 'common' mode
it supports prototypes that expect widened types, but it uses the old
sign preserving rules for how to widen narrow unsigned types. (In its
default 'relaxed' mode, __STDC__ is 1 and uchar widens to 'int'.) */
#if defined(__DECC) && (!defined(__STDC__) || !__STDC__)
#define UCHAR_P unsigned int
#endif
#endif
/* These are used for arguments within VDECL prototype declarations.
*/
#ifdef UNWIDENED_PROTOTYPES
#define CHAR_P char
#define SCHAR_P schar
#define UCHAR_P uchar
#define XCHAR_P coordxy
#define SHORT_P short
#ifndef SKIP_BOOLEAN
#define BOOLEAN_P boolean
#endif
#define ALIGNTYP_P aligntyp
#else
#ifdef WIDENED_PROTOTYPES
#define CHAR_P int
#define SCHAR_P int
#ifndef UCHAR_P
#define UCHAR_P int
#endif
#define XCHAR_P int
#define SHORT_P int
#define BOOLEAN_P int
#define ALIGNTYP_P int
#else
/* Neither widened nor unwidened prototypes. Argument list expansion
* by VDECL always empty; all xxx_P vanish so defs aren't needed. */
#endif
#endif
/* OBJ_P and MONST_P should _only_ be used for declaring function pointers.
*/
#if defined(ULTRIX_PROTO) && !defined(__STDC__)
/* The ultrix 2.0 and 2.1 compilers (on Ultrix 4.0 and 4.2 respectively) can't
* handle "struct obj *" constructs in prototypes. Their bugs are different,
* but both seem to work if we put "void*" in the prototype instead. This
* gives us minimal prototype checking but avoids the compiler bugs.
*/
#define OBJ_P void *
#define MONST_P void *
#else
#define OBJ_P struct obj *
#define MONST_P struct monst *
#endif
#if 0
/* The problem below is still the case through 4.0.5F, but the suggested
* compiler flags in the Makefiles suppress the nasty messages, so we don't
* need to be quite so drastic.
*/
#if defined(__sgi) && !defined(__GNUC__)
/*
* As of IRIX 4.0.1, /bin/cc claims to be an ANSI compiler, but it thinks
* it's impossible for a prototype to match an old-style definition with
* unwidened argument types. Thus, we have to turn off all NetHack
* prototypes, and avoid declaring several system functions, since the system
* include files have prototypes and the compiler also complains that
* prototyped and unprototyped declarations don't match.
*/
#undef VDECL
#define VDECL(f, p) f()
#endif
#endif
/* MetaWare High-C defaults to unsigned chars */
/* AIX 3.2 needs this also */
#if defined(__HC__) || defined(_AIX32)
#undef signed
#endif
#ifdef __clang__
/* clang's gcc emulation is sufficient for nethack's usage */
#ifndef __GNUC__
#define __GNUC__ 5 /* high enough for returns_nonnull */
#endif
#endif
/*
* Give first priority to standard
*/
#ifndef ATTRNORETURN
#if defined(__STDC_VERSION__) || defined(__cplusplus)
#if (__STDC_VERSION__ > 202300L) || defined(__cplusplus)
#define ATTRNORETURN [[noreturn]]
#endif
#endif
#endif
/*
* Allow gcc2 to check parameters of printf-like calls with -Wformat;
* append this to a prototype declaration (see pline() in extern.h).
*/
#ifdef __GNUC__
#if (__GNUC__ >= 2) && !defined(USE_OLDARGS)
#define PRINTF_F(f, v) __attribute__((format(printf, f, v)))
#endif
#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
#define PRINTF_F_PTR(f, v) PRINTF_F(f, v)
#endif
#if __GNUC__ >= 3
#define UNUSED __attribute__((unused))
#ifndef ATTRNORETURN
#ifndef NORETURN
#define NORETURN __attribute__((noreturn))
#endif
#endif
#if (!defined(__linux__) && !defined(MACOS)) || defined(GCC_URWARN)
/* disable gcc's __attribute__((__warn_unused_result__)) since explicitly
discarding the result by casting to (void) is not accepted as a 'use' */
#define __warn_unused_result__ /*empty*/
#define warn_unused_result /*empty*/
#endif
#endif
#if __GNUC__ >= 5
#ifndef NONNULLS_DEFINED
#define DO_DEFINE_NONNULLS
#endif /* !NONNULLS_DEFINED */
/* #pragma message is available */
#define NH_PRAGMA_MESSAGE 1
#endif
#endif
#if defined(__clang__) && !defined(DO_DEFINE_NONNULLS)
#define DO_DEFINE_NONNULLS
#endif
#if defined(DO_DEFINE_NONNULLS) && !defined(NONNULLS_DEFINED)
#define NONNULL __attribute__((returns_nonnull))
#define NONNULLPTRS __attribute__((nonnull))
#define NONNULLARG1 __attribute__((nonnull (1)))
#define NONNULLARG2 __attribute__((nonnull (2)))
#define NONNULLARG3 __attribute__((nonnull (3)))
#define NONNULLARG4 __attribute__((nonnull (4)))
#define NONNULLARG5 __attribute__((nonnull (5)))
#define NONNULLARG7 __attribute__((nonnull (7))) /* for bhit() */
#define NONNULLARG12 __attribute__((nonnull (1, 2)))
#define NONNULLARG123 __attribute__((nonnull (1, 2, 3)))
#define NONNULLARG13 __attribute__((nonnull (1, 3)))
#define NONNULLARG14 __attribute__((nonnull (1, 4))) /* for query_category */
#define NONNULLARG134 __attribute__((nonnull (1, 3, 4))) /* for do_stone_mon */
#define NONNULLARG145 __attribute__((nonnull (1, 4, 5))) /* find_roll_to_hit */
#define NONNULLARG17 __attribute__((nonnull (1, 7))) /* for askchain() */
#define NONNULLARG24 __attribute__((nonnull (2, 4))) /* query_objlist() */
#define NONNULLARG45 __attribute__((nonnull (4, 5))) /* do_screen_descri... */
#define NONNULLS_DEFINED
#undef DO_DEFINE_NONNULLS
#endif /* __clang__ && !NONNULLS_DEFINED */
#ifdef _MSC_VER
#ifndef ATTRNORETURN
#define ATTRNORETURN __declspec(noreturn)
#endif
/* #pragma message is available */
#define NH_PRAGMA_MESSAGE 1
#endif
#ifndef PRINTF_F
#define PRINTF_F(f, v)
#endif
#ifndef PRINTF_F_PTR
#define PRINTF_F_PTR(f, v)
#endif
#ifndef UNUSED
#define UNUSED
#endif
#ifndef ATTRNORETURN
#define ATTRNORETURN
#endif
#ifndef NORETURN
#define NORETURN
#endif
#ifndef NONNULLS_DEFINED
#define NONNULL
#define NONNULLPTRS
#define NONNULLARG1
#define NONNULLARG2
#define NONNULLARG3
#define NONNULLARG4
#define NONNULLARG5
#define NONNULLARG7
#define NONNULLARG12
#define NONNULLARG123
#define NONNULLARG13
#define NONNULLARG14
#define NONNULLARG134
#define NONNULLARG145
#define NONNULLARG17
#define NONNULLARG24
#define NONNULLARG45
#define NONNULLS_DEFINED
#endif /* NONNULLS_DEFINED */
#ifndef NO_NONNULLS
#define NO_NONNULLS
#endif /* NO_NONNULLS */
/*
* Allow gcc and clang to catch the use of non-C99 functions that
* NetHack has replaced with a C99 standard function. The old non-C99
* function will cause a link failure on non-Unix platforms,
* so it is preferrable to catch it early, during compile.
*/
#if !defined(X11_BUILD) && !defined(__cplusplus)
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ >= 12
extern char *index(const char *s, int c) __attribute__ ((unavailable));
extern char *rindex(const char *s, int c) __attribute__ ((unavailable));
#endif
#endif
#if defined(__clang__)
#if __clang_major__ >= 7
extern char *index(const char *s, int c) __attribute__ ((unavailable));
extern char *rindex(const char *s, int c) __attribute__ ((unavailable));
#endif
#endif
#endif
#endif /* TRADSTDC_H */