Patch to implement #include depth checking

Nick Clifton nickc@cygnus.com
Thu Jul 8 05:36:00 GMT 1999


Hi Guys,

  At the request of a Cygnus customer I have implemented a patch to
  issue a warning if the nesting of #include files exceeds a given
  number.  The idea to help software project management by allowing a
  limit to be set on the depth of inclusion nesting.  The warnings are
  not generated for system header files, and they can be suppressed,
  if necessary, on a per header file basis by the use of a #pragma. 

  The patch works for both cpp and cpplib, and is pretty much self
  contained in the preprocessor code.

  What do you think - is this patch suitable for inclusion into EGCS ? 

Cheers
	Nick

Thu Jul  8 13:03:34 1999  Nick Clifton  <nickc@cygnus.com>

	* cccp.c (warn_if_include_depth_exceeds): New variable.
	(print_help): Describe new command-line option
	-Winclude-depth-exceeds.
	(main): Parse -Winclude-depth-exceeds command-line option.
	(finclude): Generate a warning if the inclusion depth exceeds
	the limit set by -Winclude-depth-exceeds.
	(do_pragma): Accept #pragma include_depth_check_off and
	#pragma include_depth_check_on.

	* cpp.texi: Document -Winclude-depth-exceeds command-line
	option and the #pragmas associated with it.

	* invoke.texi: Document -Winclude-depth-exceeds command-line
	option and the #pragmas associated with it.

	* cpplib.h (struct cpp_options): Define new fields:
	warn_if_include_depth_exceeds, depth_warnings_triggered and
	saved_warn_depth.

	* cppinit.c (cpp_options_init): Initialise new fields in
	struct cpp_options.
	(cpp_handle_option): Parse -Winclude-depth-exceeds
	command-line option.
	(print_help): Describe new command-line option
	-Winclude-depth-exceeds.

	* cpplib.c (do_include): Generate a warning if the inclusion
	depth exceeds the limit set by -Winclude-depth-exceeds.
	(do_pragma): Accept #pragma include_depth_check_off and
	#pragma include_depth_check_on.

	* toplev.c (documented_lang_options): Add
	-Winclude-depth-exceeds.

Index: cccp.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cccp.c,v
retrieving revision 1.70
diff -p -r1.70 cccp.c
*** cccp.c	1999/05/31 11:44:42	1.70
--- cccp.c	1999/07/08 12:26:19
*************** static int warn_import = 1;
*** 251,256 ****
--- 251,261 ----
  
  static int warnings_are_errors;
  
+ /* Maximum inclusion nesting depth, before a warning is produced.
+    -1 means do not produce any warnings.  This is the default.  */
+ 
+ static int warn_if_include_depth_exceeds = -1;
+ 
  /* Nonzero means try to imitate old fashioned non-ANSI preprocessor.  */
  
  int traditional;
*************** print_help ()
*** 1159,1164 ****
--- 1164,1170 ----
    printf ("  -Wno-undef                Do not warn about testing undefined macros\n");
    printf ("  -Wimport                  Warn about the use of the #import directive\n");
    printf ("  -Wno-import               Do not warn about the use of #import\n");
+   printf ("  -Winclude-depth-exceeds=<n> Warn if #include files nest more than <n> levels\n");
    printf ("  -Werror                   Treat all warnings as errors\n");
    printf ("  -Wno-error                Do not treat warnings as errors\n");
    printf ("  -Wall                     Enable all preprocessor warnings\n");
*************** main (argc, argv)
*** 1524,1529 ****
--- 1530,1545 ----
  	  warnings_are_errors = 1;
  	else if (!strcmp (argv[i], "-Wno-error"))
  	  warnings_are_errors = 0;
+ 	else if (!strncmp (argv[i], "-Winclude-depth-exceeds=", 24))
+ 	  {
+ 	    warn_if_include_depth_exceeds = atoi (argv[i] + 24);
+ 	    
+ 	    if (warn_if_include_depth_exceeds == 0)
+ 	      {
+ 		warning ("Bad value for '-Winclude-depth-exceeds' option - ignored");
+ 		warn_if_include_depth_exceeds = -1;
+ 	      }
+ 	  }
  	else if (!strcmp (argv[i], "-Wall"))
  	  {
  	    warn_trigraphs = 1;
*************** finclude (f, inc, op, system_header_p, d
*** 5283,5288 ****
--- 5299,5327 ----
    indepth++;
    input_file_stack_tick++;
  
+   if (warn_if_include_depth_exceeds != -1
+       && ! system_header_p)
+     {
+       /* In order to prevent a recursive inclusion loop from producing
+ 	 reams of warnings, once we have produced a warning we disable
+ 	 further warnings until the nesting depth drops below the depth 
+ 	 that triggered it.  */
+       static int depth_warnings_triggered = -1;
+ 
+       if (depth_warnings_triggered != -1)
+ 	{
+ 	  if (indepth < depth_warnings_triggered)
+ 	    depth_warnings_triggered = -1;
+ 	}
+       else if (indepth > warn_if_include_depth_exceeds)
+ 	{
+ 	  warning ("Inclusion depth (%d) exceeds limit set on command line (%d)",
+ 		   indepth, warn_if_include_depth_exceeds);
+ 
+ 	  depth_warnings_triggered = indepth;
+ 	}
+     }
+ 
    if (!no_trigraphs)
      trigraph_pcp (fp);
  
*************** do_pragma (buf, limit, op, keyword)
*** 7075,7112 ****
       struct directive *keyword ATTRIBUTE_UNUSED;
  {
    SKIP_WHITE_SPACE (buf);
!   if (!strncmp ((char *) buf, "once", 4)) {
!     /* Allow #pragma once in system headers, since that's not the user's
!        fault.  */
!     if (!instack[indepth].system_header_p)
!       warning ("`#pragma once' is obsolete");
!     do_once ();
!   }
! 
!   if (!strncmp ((char *) buf, "implementation", 14)) {
!     /* Be quiet about `#pragma implementation' for a file only if it hasn't
!        been included yet.  */
! 
!     int h;
!     U_CHAR *p = buf + 14, *fname;
!     SKIP_WHITE_SPACE (p);
!     if (*p != '\"')
!       return 0;
  
!     fname = p + 1;
!     if ((p = (U_CHAR *) index ((char *) fname, '\"')))
!       *p = '\0';
!     
!     for (h = 0; h < INCLUDE_HASHSIZE; h++) {
!       struct include_file *inc;
!       for (inc = include_hashtab[h]; inc; inc = inc->next) {
! 	if (!strcmp (base_name (inc->fname), (char *) fname)) {
! 	  warning ("`#pragma implementation' for \"%s\" appears after its #include",fname);
! 	  return 0;
  	}
-       }
      }
    }
    return 0;
  }
  
--- 7114,7177 ----
       struct directive *keyword ATTRIBUTE_UNUSED;
  {
    SKIP_WHITE_SPACE (buf);
!   
!   if (!strncmp ((char *) buf, "once", 4))
!     {
!       /* Allow #pragma once in system headers, since that's not the user's
! 	 fault.  */
!       if (!instack[indepth].system_header_p)
! 	warning ("`#pragma once' is obsolete");
!       do_once ();
!     }
  
!   if (!strncmp ((char *) buf, "implementation", 14))
!     {
!       /* Be quiet about `#pragma implementation' for a file only if it hasn't
! 	 been included yet.  */
! 
!       int h;
!       U_CHAR *p = buf + 14, *fname;
!       SKIP_WHITE_SPACE (p);
!       if (*p != '\"')
! 	return 0;
!       
!       fname = p + 1;
!       if ((p = (U_CHAR *) index ((char *) fname, '\"')))
! 	*p = '\0';
!       
!       for (h = 0; h < INCLUDE_HASHSIZE; h++)
! 	{
! 	  struct include_file *inc;
! 	  for (inc = include_hashtab[h]; inc; inc = inc->next)
! 	    {
! 	      if (!strcmp (base_name (inc->fname), (char *) fname))
! 		{
! 		  warning ("`#pragma implementation' for \"%s\" appears after its #include",fname);
! 		  return 0;
! 		}
! 	    }
  	}
      }
+ 
+   {
+     static int saved_warn_depth = -2;
+     
+     if (!strncmp ((char *) buf, "include_depth_check_off", 23))
+       {
+ 	saved_warn_depth = warn_if_include_depth_exceeds;
+ 	warn_if_include_depth_exceeds = -1;
+       }
+     
+     if (!strncmp ((char *) buf, "include_depth_check_on", 22))
+       {
+ 	if (saved_warn_depth != -2)
+ 	  {
+ 	    warn_if_include_depth_exceeds = saved_warn_depth;
+ 	    saved_warn_depth = -2;
+ 	  }
+       }
    }
+   
    return 0;
  }
  
Index: cpp.texi
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cpp.texi,v
retrieving revision 1.11
diff -p -r1.11 cpp.texi
*** cpp.texi	1999/06/14 17:21:43	1.11
--- cpp.texi	1999/07/08 12:26:20
*************** some old C programs contain such lines.
*** 2461,2468 ****
  The ANSI standard specifies that the effect of the @samp{#pragma}
  directive is implementation-defined.  In the GNU C preprocessor,
  @samp{#pragma} directives are not used, except for @samp{#pragma once}
! (@pxref{Once-Only}).  However, they are left in the preprocessor output,
! so they are available to the compilation pass.
  
  @findex #ident
  The @samp{#ident} directive is supported for compatibility with certain
--- 2461,2469 ----
  The ANSI standard specifies that the effect of the @samp{#pragma}
  directive is implementation-defined.  In the GNU C preprocessor,
  @samp{#pragma} directives are not used, except for @samp{#pragma once}
! (@pxref{Once-Only}) and @samp{#pragma include_depth_check_@{on|off@}}.
! However, they are left in the preprocessor output, so they are available
! to the compilation pass. 
  
  @findex #ident
  The @samp{#ident} directive is supported for compatibility with certain
*************** ANSI C@.
*** 2710,2715 ****
--- 2711,2723 ----
  @item -Wundef
  @findex -Wundef
  Warn if an undefined identifier is evaluated in an @samp{#if} directive.
+ 
+ @item -Winclude-depth-exceeds=@samp{n}
+ @findex -Winclude-depth-exceeds
+ Warn if the number of nested levels of @samp{#include}d files exceeds
+ @samp{n}.  This warning can be suppressed by @samp{#pragma
+ include_depth_check_off} and re-enabled by @samp{#pragma
+ include_depth_check_on}.
  
  @item -I @var{directory}
  @findex -I

Index: invoke.texi
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/invoke.texi,v
retrieving revision 1.118
diff -p -r1.118 invoke.texi
*** invoke.texi	1999/07/05 08:44:30	1.118
--- invoke.texi	1999/07/08 12:26:23
*************** Only @samp{#define @var{name}} is includ
*** 2741,2746 ****
--- 2741,2753 ----
  @item -trigraphs
  Support ANSI C trigraphs.  The @samp{-ansi} option also has this effect.
  
+ @item -Winclude-depth-exceeds=@samp{n}
+ @findex -Winclude-depth-exceeds
+ Warn if the number of nested levels of @samp{#include}d files exceeds
+ @samp{n}.  This warning can be suppressed by @samp{#pragma
+ include_depth_check_off} and re-enabled by @samp{#pragma 
+ include_depth_check_on}.
+ 
  @item -Wp,@var{option}
  Pass @var{option} as an option to the preprocessor.  If @var{option}
  contains commas, it is split into multiple options at the commas.

Index: cpplib.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cpplib.h,v
retrieving revision 1.38
diff -p -r1.38 cpplib.h
*** cpplib.h	1999/05/10 15:24:35	1.38
--- cpplib.h	1999/07/08 12:26:24
*************** struct cpp_options {
*** 411,416 ****
--- 411,434 ----
  
    char warnings_are_errors;
  
+   /* Maximum inclusion nesting depth, before a warning is produced.
+      -1 means do not produce any warnings.  This is the default.  */
+ 
+   int  warn_if_include_depth_exceeds;
+   
+   /* In order to prevent a recursive inclusion loop from producing
+      reams of warnings, once we have produced a warning we disable
+      further warnings until the nesting depth drops below the depth 
+      that triggered it.  */
+   
+   int  depth_warnings_triggered;
+ 
+   /* In order to allow the depth warnings to be temporarily suppressed
+      (by a pragma), we need to be able to record the value of
+      warn_if_include_depth_exceeds when the suppresion occurs.  */
+ 
+   int saved_warn_depth;
+   
    /* Nonzero causes output not to be done,
       but directives such as #define that have side effects
       are still obeyed.  */

Index: cppinit.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cppinit.c,v
retrieving revision 1.14
diff -p -r1.14 cppinit.c
*** cppinit.c	1999/05/10 15:24:34	1.14
--- cppinit.c	1999/07/08 12:26:23
*************** cpp_options_init (opts)
*** 524,530 ****
    opts->dollars_in_ident = 1;
    opts->cplusplus_comments = 1;
    opts->warn_import = 1;
! 
    opts->pending = (struct cpp_pending *) xmalloc (sizeof (struct cpp_pending));
    bzero ((char *) opts->pending, sizeof (struct cpp_pending));
  }
--- 524,533 ----
    opts->dollars_in_ident = 1;
    opts->cplusplus_comments = 1;
    opts->warn_import = 1;
!   opts->warn_if_include_depth_exceeds = -1;
!   opts->depth_warnings_triggered = -1;
!   opts->saved_warn_depth = -2;
!   
    opts->pending = (struct cpp_pending *) xmalloc (sizeof (struct cpp_pending));
    bzero ((char *) opts->pending, sizeof (struct cpp_pending));
  }
*************** cpp_handle_option (pfile, argc, argv)
*** 1430,1435 ****
--- 1433,1449 ----
  	  opts->warnings_are_errors = 1;
  	else if (!strcmp (argv[i], "-Wno-error"))
  	  opts->warnings_are_errors = 0;
+ 	else if (!strncmp (argv[i], "-Winclude-depth-exceeds=", 24))
+ 	  {
+ 	    opts->warn_if_include_depth_exceeds = atoi (argv[i] + 24);
+ 	    
+ 	    if (opts->warn_if_include_depth_exceeds == 0)
+ 	      {
+ 		cpp_warning (pfile, "\
+ Bad value for '-Winclude-depth-exceeds' option - ignored");
+ 		opts->warn_if_include_depth_exceeds = -1;
+ 	      }
+ 	  }
  	else if (!strcmp (argv[i], "-Wall"))
  	  {
  	    opts->warn_trigraphs = 1;
*************** Switches:\n\
*** 1749,1754 ****
--- 1763,1769 ----
    -Wno-undef                Do not warn about testing undefined macros\n\
    -Wimport                  Warn about the use of the #import directive\n\
    -Wno-import               Do not warn about the use of #import\n\
+   -Winclude-depth-exceeds=<n> Warn if #include files nest more than <n> levels\n\
    -Werror                   Treat all warnings as errors\n\
    -Wno-error                Do not treat warnings as errors\n\
    -Wall                     Enable all preprocessor warnings\n\

Index: cpplib.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cpplib.c,v
retrieving revision 1.82
diff -p -r1.82 cpplib.c
*** cpplib.c	1999/06/07 10:35:26	1.82
--- cpplib.c	1999/07/08 12:26:24
*************** do_include (pfile, keyword)
*** 983,988 ****
--- 983,1010 ----
  	   "#import is obsolete, use an #ifndef wrapper in the header file");
      }
  
+   if (CPP_OPTIONS (pfile)->warn_if_include_depth_exceeds != -1
+       && ! CPP_BUFFER (pfile)->system_header_p)
+     {
+       if (CPP_OPTIONS (pfile)->depth_warnings_triggered != -1)
+ 	{
+ 	  if (pfile->buffer_stack_depth
+ 	      < CPP_OPTIONS (pfile)->depth_warnings_triggered)
+ 	    CPP_OPTIONS (pfile)->depth_warnings_triggered = -1;
+ 	}
+       else if (pfile->buffer_stack_depth
+ 	       > CPP_OPTIONS (pfile)->warn_if_include_depth_exceeds)
+ 	{
+ 	  cpp_warning (pfile, "\
+ Inclusion depth (%d) exceeds limit set on command line (%d)",
+ 		       pfile->buffer_stack_depth,
+ 		       CPP_OPTIONS (pfile)->warn_if_include_depth_exceeds);
+ 
+ 	  CPP_OPTIONS (pfile)->depth_warnings_triggered =
+ 	    pfile->buffer_stack_depth;
+ 	}
+     }
+   
    pfile->parsing_include_directive++;
    token = get_directive_token (pfile);
    pfile->parsing_include_directive--;
*************** do_pragma (pfile, keyword)
*** 1516,1521 ****
--- 1538,1558 ----
          cpp_warning (pfile,
  	  "`#pragma implementation' for `%s' appears after file is included",
  		     fcopy);
+     }
+ 
+   if (!strncmp ((char *) buf, "include_depth_check_off", 23))
+     {
+       CPP_OPTIONS (pfile)->saved_warn_depth = CPP_OPTIONS (pfile)->warn_if_include_depth_exceeds;
+       CPP_OPTIONS (pfile)->warn_if_include_depth_exceeds = -1;
+     }
+   
+   if (!strncmp ((char *) buf, "include_depth_check_on", 22))
+     {
+       if (CPP_OPTIONS (pfile)->saved_warn_depth != -2)
+ 	{
+ 	  CPP_OPTIONS (pfile)->warn_if_include_depth_exceeds = CPP_OPTIONS (pfile)->saved_warn_depth;
+ 	  CPP_OPTIONS (pfile)->saved_warn_depth = -2;
+ 	}
      }
  
    return 0;

Index: toplev.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/toplev.c,v
retrieving revision 1.186
diff -p -r1.186 toplev.c
*** toplev.c	1999/05/20 10:37:00	1.186
--- toplev.c	1999/07/08 12:26:26
*************** documented_lang_options[] =
*** 1061,1066 ****
--- 1061,1067 ----
    { "-Wimplicit", "" },
    { "-Wno-implicit", "" },
    { "-Wimport", "Warn about the use of the #import directive" },
+   { "-Winclude-depth-exceeds", "Warn if #include files nest more than N levels" },
    { "-Wno-import", "" },
    { "-Wlong-long","" },
    { "-Wno-long-long", "Do not warn about using 'long long' when -pedantic" },


More information about the Gcc-patches mailing list