Files
nethack/sys/unix/hints/include/compiler.370
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

205 lines
6.0 KiB
Plaintext
Executable File

#------------------------------------------------------------------------------
# NetHack 3.7 compiler.370 $NHDT-Date: 1668359835 2022/11/13 17:17:15 $ $NHDT-Branch: NetHack-3.7 $
# compiler flags: CCFLAGS is used to construct a value for CFLAGS with
# various -I, -D, and -W settings appended below;
# these are the settings of most interest for an end-user build
# (clang doesn't support '-Og', gcc needs 4.x or later)
CCFLAGS = -g
#CCFLAGS = -g -Og
#CCFLAGS = -O2
# Note: this is not the usual 'CFLAGS' which is used in default
# rules for compiling C code; specifying a value for that on the
# 'make' command line should be avoided.
#If you want to force use of one particular set of compilers, do it
#here, ahead of the detection, so that the detection will match your
#choice and set variables CCISCLANG, GCCGTEQ, CLANGPPGTEQ9 etc.
#accordingly.
#
#CC= gcc
#CXX= g++ -std-gnu++11
#
#CC= clang
#CXX=clang++ -std=gnu++11
#
# If these are set on entry, preparation for C++ compiles is affected.
# CPLUSPLUS_NEEDED = 1 C++ compile bits included
# CPLUSPLUS_NEED17 = 1 C++ -std=c++17 (at least)
# CPLUSPLUS_NEED_DEPSUPPRESS = 1 C++ -Wno-deprecated-copy,
# -Wno-deprecated-declarations
ifeq "$(WANT_ASAN)" "1"
USE_ASAN=1
endif
# If you want to override the compiler detection just carried out
# uncomment one of the following pairs. Note, however, that
# doing this after the detection above will likely result in
# mismatched variable values for CCISCLANG, GCCGTEQ, CLANGPPGTEQ9 etc.
#
#CC= gcc
#CXX= g++ -std-gnu++11
#
#CC= clang
#CXX=clang++ -std=gnu++11
CFLAGS=$(CCFLAGS) -I../include -DNOTPARMDECL
CFLAGS+=-Wall -Wextra \
-Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings
CFLAGS+=-pedantic
CFLAGS+=-Wmissing-declarations
#CFLAGS+=-Wformat=2
# these are left out of the C++ flags
CFLAGS+=-Wformat-nonliteral
CFLAGS+=-Wunreachable-code
#
# the following are not allowed in C++
CFLAGS+=-Wimplicit
CFLAGS+=-Wimplicit-function-declaration
CFLAGS+=-Wimplicit-int
CFLAGS+=-Wmissing-prototypes
CFLAGS+=-Wold-style-definition
CFLAGS+=-Wstrict-prototypes
CFLAGS+=-Wnonnull
#detection of clang vs gcc
CCISCLANG := $(shell echo `$(CC) --version` | grep clang)
ifeq "$(CCISCLANG)" ""
# gcc-specific follows
CXX=g++ -std=gnu++11
# get the version of gcc
GCCGTEQ9 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 9)
GCCGTEQ11 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 11)
GCCGTEQ12 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 12)
ifeq "$(GCCGTEQ9)" "1"
# flags present in gcc version greater than or equal to 9 can go here
CFLAGS+=-Wformat-overflow
CFLAGS+=-Wmissing-parameter-type
endif # GCC greater than or equal to 9
#ifeq "$(GCCGTEQ11)" "1"
#endif
#ifeq "$(GCCGTEQ12)" "1"
#endif
# end of gcc-specific
else # gcc or clang?
CXX=clang++ -std=gnu++11
# clang-specific follows
CLANGGTEQ14 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 14)
ifeq "$(CLANGGTEQ14)" "1"
CFLAGS+=-Wno-deprecated-declarations
else
# older versions complain about things newer ones don't without this
CFLAGS+=-Wno-missing-field-initializers
endif
# none
endif # clang-specific ends here
ifdef MAKEFILE_SRC
ifdef CPLUSPLUS_NEEDED
CCXXFLAGS = -g -I../include -DNOTPARMDECL
CCXXFLAGS+=-Wall -Wextra -Wno-missing-field-initializers \
-Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings
CCXXFLAGS+=-pedantic
CCXXFLAGS+=-Wmissing-declarations
#detection of clang++ vs g++
CXXISCLANG := $(shell echo `$(CXX) --version` | grep clang)
ifeq "$(CXXISCLANG)" ""
# g++-specific
CCXX=g++ -std=gnu++11
# get the version of g++
GPPGTEQ9 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 9)
GPPGTEQ11 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 11)
GPPGTEQ12 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 12)
ifeq "$(GPPGTEQ9)" "1"
CCXXFLAGS+=-Wformat-overflow
ifdef CPLUSPLUS_NEED_DEPSUPPRESS
CCXXFLAGS+=-Wno-deprecated-copy
CCXXFLAGS+=-Wno-deprecated-declarations
endif # CPLUSPLUS_NEED_DEPSUPPRESS
endif # g++ version greater than or equal to 9
ifeq "$(GPPGTEQ11)" "1"
# the g++ linker will have trouble linking if the following isn't included
# when compiling the C files.
CFLAGS+=-fPIC
endif # g++ version greater than or equal to 11
ifdef CPLUSPLUS_NEED17
ifeq "$(GPPGTEQ12)" "1"
CCXX=g++ -std=c++20
else # g++ version greater than or equal to 12? (no follows)
CCXX=g++ -std=c++17
endif # g++ version greater than or equal to 12
endif # CPLUSPLUS_NEED17
else # g++ or clang++ ?
# clang++-specific
CCXX=clang++ -std=c++11
# get the version of clang++
CLANGPPGTEQ9 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 9)
CLANGPPGTEQ11 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 11)
CLANGPPGTEQ14 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 14)
CLANGPPGTEQ17 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 17)
ifeq "$(CLANGPPGTEQ9)" "1"
#CCXXFLAGS+=-Wformat-overflow
endif
ifeq "$(CLANGPPGTEQ14)" "1"
CPLUSPLUS_NEED_DEPSUPPRESS=1
endif
ifdef CPLUSPLUS_NEED_DEPSUPPRESS
CCXXFLAGS+=-Wno-deprecated
CCXXFLAGS+=-Wno-deprecated-declarations
endif # CPLUSPLUS_NEED_DEPSUPPRESS
# The clang++ linker seems to have trouble linking if the following isn't
# included when compiling the C files by clang..
CFLAGS+=-fPIC
ifdef CPLUSPLUS_NEED17
ifeq "$(CLANGPPGTEQ14)" "1"
CCXX=clang++ -std=c++17
endif # clang++ greater than or equal to 14
ifeq "$(CLANGPPGTEQ17)" "1"
CCXX=clang++ -std=c++20
endif # clang++ greater than or equal to 17
endif # CPLUSPLUS_NEED17
endif # end of clang++-specific section
CXX=$(CCXX)
endif # CPLUSPLUS_NEEDED
endif # MAKEFILE_SRC
ifeq "$(c89)" "1"
HAVECSTD=c89
endif
ifeq "$(C89)" "1"
HAVECSTD=c89
endif
ifeq "$(c99)" "1"
HAVECSTD=c99
endif
ifeq "$(C99)" "1"
HAVECSTD=c99
endif
ifeq "$(c11)" "1"
HAVECSTD=c11
endif
ifeq "$(C11)" "1"
HAVECSTD=c11
endif
ifeq "$(c23)" "1"
HAVECSTD=c2x
endif
ifeq "$(C23)" "1"
HAVECSTD=c2x
endif
ifneq "$(HAVECSTD)" ""
CSTD = -std=$(HAVECSTD)
endif
#end of compiler.370
#------------------------------------------------------------------------------