public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* proposal to add -Wheader-guard option
@ 2014-01-30 22:43 Prathamesh Kulkarni
  2014-02-04  7:06 ` Prathamesh Kulkarni
  0 siblings, 1 reply; 4+ messages in thread
From: Prathamesh Kulkarni @ 2014-01-30 22:43 UTC (permalink / raw)
  To: gcc

[-- Attachment #1: Type: text/plain, Size: 2205 bytes --]

Hi, I was wondering if it's a good idea to add -Wheader-guard option
that warns on mismatches between #ifndef and #define lines
in header guard, similar to -Wheader-guard in clang-3.4 ?
(http://llvm.org/releases/3.4/tools/clang/docs/ReleaseNotes.html)

I have implemented patch for -Wheader-guard (please find it attached).
Consider a file having the following format:
#ifndef cmacro (or #if !defined(cmacro) )
#define dmacro
// rest of the stuff
#endif

The warning is triggered if the edit distance
(http://en.wikipedia.org/wiki/Levenshtein_distance), between cmacro
and dmacro
is <= max (len(cmacro), len(dmacro)) / 2
If the edit distance is more than half, I assume that cmacro
and dmacro are "very different", and the intent
was probably not to define header guard (This is what clang does too).

Example:
#ifndef FOO_H
#define _FOO_H
#endif

foo.h:1:0: warning: FOO_H used as header guard followed by #define of
different macro [-Wheader-guard]
 #ifndef FOO_H
 ^
foo.h:2:0: note: FOO_H is defined here, did you mean _FOO_H ?
 #define _FOO_H
 ^

Warning is not triggered in the following cases:

1] The edit distance between #ifndef (or #!defined) macro
and #define macro is > half of maximum length between two macros

Example:
#ifndef FOO
#define BAR
#endif

2] #ifndef and #define are not on consecutive lines (blank lines/ comment lines
are not ignored).

3] dmacro gets undefined
Example:
#ifndef cmacro
#define dmacro
#undef dmacro
#endif

However the following warning gets generated during the build:
../../src/libcpp/directives.c: In function 'void _cpp_pop_buffer(cpp_reader*)':
../../src/libcpp/directives.c:
2720:59: warning: 'inc_type' may be used
uninitialized in this function [-Wmaybe-uninitialized]
       _cpp_pop_file_buffer (pfile, inc, to_free, inc_type);
                                                           ^
_cpp_pop_buffer(): http://pastebin.com/aLYLnXJa
I have defined inc_type only if inc is not null (ie buffer is file
buffer) in 1st if()
and used it (passed it to _cpp_pop_file_buffer() ), only if inc is not null
in 2nd if(). I guess this warning could be considered harmless ?
How should I should rewrite it to avoid the warning ?

Thanks and Regards,
Prathamesh

[-- Attachment #2: header-guard.patch --]
[-- Type: text/x-patch, Size: 11878 bytes --]

Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 207299)
+++ gcc/c-family/c-common.c	(working copy)
@@ -9558,6 +9558,7 @@ static const struct reason_option_codes_
   {CPP_W_WARNING_DIRECTIVE,		OPT_Wcpp},
   {CPP_W_LITERAL_SUFFIX,		OPT_Wliteral_suffix},
   {CPP_W_DATE_TIME,			OPT_Wdate_time},
+  {CPP_W_HEADER_GUARD,  OPT_Wheader_guard},
   {CPP_W_NONE,				0}
 };
 
Index: gcc/c-family/c-opts.c
===================================================================
--- gcc/c-family/c-opts.c	(revision 207299)
+++ gcc/c-family/c-opts.c	(working copy)
@@ -430,6 +430,10 @@ c_common_handle_option (size_t scode, co
       cpp_opts->cpp_warn_traditional = value;
       break;
 
+    case OPT_Wheader_guard:
+      cpp_opts->cpp_warn_header_guard = value;
+      break;
+
     case OPT_Wtrigraphs:
       cpp_opts->warn_trigraphs = value;
       break;
Index: gcc/c-family/c.opt
===================================================================
--- gcc/c-family/c.opt	(revision 207299)
+++ gcc/c-family/c.opt	(working copy)
@@ -736,6 +736,10 @@ Wtraditional
 C ObjC Var(warn_traditional) Warning
 Warn about features not present in traditional C
 
+Wheader-guard
+C ObjC C++ ObjC++ Warning
+Warn of header guard
+
 Wtraditional-conversion
 C ObjC Var(warn_traditional_conversion) Warning
 Warn of prototypes causing type conversions different from what would happen in the absence of prototype
Index: libcpp/directives.c
===================================================================
--- libcpp/directives.c	(revision 207299)
+++ libcpp/directives.c	(working copy)
@@ -565,6 +565,27 @@ lex_macro_node (cpp_reader *pfile, bool
   return NULL;
 }
 
+// return true if top of if_stack is cmacro
+static bool
+cmacro_ifs_top_p(cpp_reader *pfile)
+{
+  struct if_stack *ifs = pfile->buffer->if_stack;
+  return ifs && (ifs->next == NULL) && (ifs->mi_cmacro != NULL);
+}
+
+static linenum_type
+linediff (struct line_maps *maps, source_location loc1, source_location loc2)
+{
+  linenum_type temp;
+
+  if (loc1 < loc2)
+    temp = loc1, loc1 = loc2, loc2 = temp;
+
+  const struct line_map *m1 = linemap_lookup (maps, loc1);
+  const struct line_map *m2 = linemap_lookup (maps, loc2);
+  return SOURCE_LINE (m1, loc1) - SOURCE_LINE (m2, loc2);
+}
+
 /* Process a #define directive.  Most work is done in macro.c.  */
 static void
 do_define (cpp_reader *pfile)
@@ -586,6 +607,15 @@ do_define (cpp_reader *pfile)
 	  pfile->cb.define (pfile, pfile->directive_line, node);
 
       node->flags &= ~NODE_USED;
+      
+      // possibly #define following #ifndef in the include guard
+      if (pfile->buffer->dmacro == NULL && cmacro_ifs_top_p (pfile)
+          && linediff (pfile->line_table, pfile->directive_line, pfile->buffer->if_stack->line) == 1)
+      {
+        pfile->buffer->dmacro = node;
+        pfile->buffer->cmacro_loc = pfile->buffer->if_stack->line;
+        pfile->buffer->dmacro_loc = pfile->directive_line;
+      }
     }
 }
 
@@ -2527,6 +2557,104 @@ cpp_get_deps (cpp_reader *pfile)
   return pfile->deps;
 }
 
+static inline unsigned
+ed_min3 (unsigned a, unsigned b, unsigned c)
+{
+  return (a < b) ? (a < c) ? a : c
+                 : (b < c) ? b : c;
+}
+
+/*
+ * Levenshtein distance: http://en.wikipedia.org/wiki/Levenshtein_distance
+ * The algorithm is implemented using dynamic programming. 
+ * Instead of the matrix[s_len][t_len], we only need storage
+ * for one row with length = min(s_len, t_len), since we look only at one row and it's previous row
+ * at a time (and previous row's contents are replaced in place).
+ * hstr is string laid along the row of matrix and vstr is laid along column
+ * hstr is string with lesser length between s and t.
+ */
+
+static unsigned
+edit_dist (const unsigned char *s, const unsigned char *t,
+           unsigned s_len, unsigned t_len)
+{
+  unsigned *row; 
+  unsigned n_rows, n_cols, i, j, prev, temp, dist;
+  const unsigned char *hstr, *vstr;
+
+  if (s_len < t_len)
+  {
+    hstr = s; 
+    vstr = t;
+    n_cols = s_len + 1;
+    n_rows = t_len + 1; 
+  }
+  else
+  {
+    hstr = t;
+    vstr = s; 
+    n_cols = t_len + 1;
+    n_rows = s_len + 1;
+  }
+
+  row = (unsigned *) xmalloc (sizeof (*row) * n_cols);
+  for (j = 0; j < n_cols; j++)
+    row[j] = j;
+
+  for (i = 1; i < n_rows; i++)
+  {
+    row[0] = i;
+    for (j = 1, prev = i - 1; j < n_cols; j++)
+    {
+      temp = row[j];
+      row[j] = ed_min3 (row[j - 1] + 1, row[j] + 1, prev + (vstr[i-1] != hstr[j-1]));
+      prev = temp;
+    }
+  }
+
+  dist = row[n_cols - 1];
+  free (row);
+  return dist; 
+}
+
+
+static void
+warn_header_guard (cpp_reader *pfile)
+{
+  const cpp_hashnode *cmacro = pfile->mi_cmacro;
+  const cpp_hashnode *dmacro = pfile->buffer->dmacro;
+
+  /* 
+   * _cpp_file_cmacro_defined_p (), takes care that we are not warning again
+   * for the same file (for example if it's included twice)
+   * if cmacro is defined, or dmacro gets undefined return
+   */
+
+  if (_cpp_file_cmacro_defined_p (pfile->buffer->file) || cmacro == NULL || cmacro->type != NT_VOID
+     || dmacro == NULL || dmacro->type != NT_MACRO)
+    return;
+  
+  const unsigned char *cmacro_name = HT_STR (HT_NODE (&cmacro->ident));
+  unsigned cmacro_len = HT_LEN (HT_NODE (&cmacro->ident));
+
+  const unsigned char *dmacro_name = HT_STR (HT_NODE (&dmacro->ident));
+  unsigned dmacro_len = HT_LEN (HT_NODE (&dmacro->ident));
+
+  unsigned maxhalflen =  (cmacro_len > dmacro_len ? cmacro_len : dmacro_len) >> 1; 
+  unsigned ed = edit_dist (cmacro_name, dmacro_name, cmacro_len, dmacro_len);
+
+  if (ed <= maxhalflen)
+  {
+    cpp_warning_with_line (pfile, CPP_W_HEADER_GUARD, pfile->buffer->cmacro_loc, 0, 
+                           "%s used as header guard followed by #define of different macro",
+                           cmacro_name); 
+
+    cpp_error_with_line (pfile, CPP_DL_NOTE, pfile->buffer->dmacro_loc, 0,
+                         "%s is defined here, did you mean %s ?",
+                         cmacro_name, dmacro_name); 
+  }
+}
+
 /* Push a new buffer on the buffer stack.  Returns the new buffer; it
    doesn't fail.  It does not generate a file change call back; that
    is the responsibility of the caller.  */
@@ -2559,6 +2687,7 @@ _cpp_pop_buffer (cpp_reader *pfile)
   struct _cpp_file *inc = buffer->file;
   struct if_stack *ifs;
   const unsigned char *to_free;
+  enum include_type inc_type;
 
   /* Walk back up the conditional stack till we reach its level at
      entry to this file, issuing error messages.  */
@@ -2566,6 +2695,13 @@ _cpp_pop_buffer (cpp_reader *pfile)
     cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
 			 "unterminated #%s", dtable[ifs->type].name);
 
+  if (inc)
+  {
+    inc_type = buffer->inc_type;
+    if (CPP_WHEADER_GUARD (pfile) && pfile->mi_valid)
+      warn_header_guard (pfile);
+  }
+
   /* In case of a missing #endif.  */
   pfile->state.skipping = 0;
 
@@ -2581,7 +2717,7 @@ _cpp_pop_buffer (cpp_reader *pfile)
 
   if (inc)
     {
-      _cpp_pop_file_buffer (pfile, inc, to_free);
+      _cpp_pop_file_buffer (pfile, inc, to_free, inc_type);
 
       _cpp_do_file_change (pfile, LC_LEAVE, 0, 0, 0);
     }
Index: libcpp/files.c
===================================================================
--- libcpp/files.c	(revision 207299)
+++ libcpp/files.c	(working copy)
@@ -198,6 +198,12 @@ static int pchf_save_compare (const void
 static int pchf_compare (const void *d_p, const void *e_p);
 static bool check_file_against_entries (cpp_reader *, _cpp_file *, bool);
 
+bool
+_cpp_file_cmacro_defined_p (_cpp_file *file)
+{
+  return file->cmacro != NULL;
+}
+
 /* Given a filename in FILE->PATH, with the empty string interpreted
    as <stdin>, open it.
 
@@ -859,6 +865,14 @@ should_stack_file (cpp_reader *pfile, _c
   return f == NULL;
 }
 
+/* Initialize controlling macro state */
+static inline void
+mi_init (cpp_reader *pfile)
+{
+  pfile->mi_valid = true;
+  pfile->mi_cmacro = 0;
+}
+
 /* Place the file referenced by FILE into a new buffer on the buffer
    stack if possible.  IMPORT is true if this stacking attempt is
    because of a #import directive.  Returns true if a buffer is
@@ -895,10 +909,10 @@ _cpp_stack_file (cpp_reader *pfile, _cpp
   buffer->file = file;
   buffer->sysp = sysp;
   buffer->to_free = file->buffer_start;
+  buffer->dmacro = 0;
 
   /* Initialize controlling macro state.  */
-  pfile->mi_valid = true;
-  pfile->mi_cmacro = 0;
+  mi_init (pfile); 
 
   /* Generate the call back.  */
   _cpp_do_file_change (pfile, LC_ENTER, file->path, 1, sysp);
@@ -1012,7 +1026,8 @@ _cpp_stack_include (cpp_reader *pfile, c
     /* _cpp_stack_file didn't stack the file, so let's rollback the
        compensation dance we performed above.  */
     pfile->line_table->highest_location++;
-
+  else
+    pfile->buffer->inc_type = type;
   return stacked;
 }
 
@@ -1445,7 +1460,7 @@ cpp_push_default_include (cpp_reader *pf
    input stack.  */
 void
 _cpp_pop_file_buffer (cpp_reader *pfile, _cpp_file *file,
-		      const unsigned char *to_free)
+		      const unsigned char *to_free, enum include_type inc_type)
 {
   /* Record the inclusion-preventing macro, which could be NULL
      meaning no controlling macro.  */
@@ -1453,6 +1468,10 @@ _cpp_pop_file_buffer (cpp_reader *pfile,
     file->cmacro = pfile->mi_cmacro;
 
   /* Invalidate control macros in the #including file.  */
+
+  if (inc_type == IT_DEFAULT || inc_type == IT_CMDLINE)
+    mi_init (pfile); 
+  else
   pfile->mi_valid = false;
 
   if (to_free)
Index: libcpp/include/cpplib.h
===================================================================
--- libcpp/include/cpplib.h	(revision 207299)
+++ libcpp/include/cpplib.h	(working copy)
@@ -354,6 +354,9 @@ struct cpp_options
      traditional C.  */
   unsigned char cpp_warn_traditional;
 
+  /* Nonzero means warn about header guard */
+  unsigned char cpp_warn_header_guard;
+
   /* Nonzero means warn about long long numeric constants.  */
   unsigned char cpp_warn_long_long;
 
@@ -933,7 +936,8 @@ enum {
   CPP_W_INVALID_PCH,
   CPP_W_WARNING_DIRECTIVE,
   CPP_W_LITERAL_SUFFIX,
-  CPP_W_DATE_TIME
+  CPP_W_DATE_TIME,
+  CPP_W_HEADER_GUARD
 };
 
 /* Output a diagnostic of some kind.  */
Index: libcpp/internal.h
===================================================================
--- libcpp/internal.h	(revision 207299)
+++ libcpp/internal.h	(working copy)
@@ -348,6 +348,11 @@ struct cpp_buffer
      needs to be extern "C" protected in C++, and zero otherwise.  */
   unsigned char sysp;
 
+  enum include_type inc_type;
+  const cpp_hashnode *dmacro;
+  source_location cmacro_loc;
+  source_location dmacro_loc;
+
   /* The directory of the this buffer's file.  Its NAME member is not
      allocated, so we don't need to worry about freeing it.  */
   struct cpp_dir dir;
@@ -597,6 +602,7 @@ cpp_in_system_header (cpp_reader *pfile)
 }
 #define CPP_PEDANTIC(PF) CPP_OPTION (PF, cpp_pedantic)
 #define CPP_WTRADITIONAL(PF) CPP_OPTION (PF, cpp_warn_traditional)
+#define CPP_WHEADER_GUARD(PF) CPP_OPTION (PF, cpp_warn_header_guard)
 
 static inline int cpp_in_primary_file (cpp_reader *);
 static inline int
@@ -640,11 +646,12 @@ extern void _cpp_report_missing_guards (
 extern void _cpp_init_files (cpp_reader *);
 extern void _cpp_cleanup_files (cpp_reader *);
 extern void _cpp_pop_file_buffer (cpp_reader *, struct _cpp_file *,
-				  const unsigned char *);
+				  const unsigned char *, enum include_type);
 extern bool _cpp_save_file_entries (cpp_reader *pfile, FILE *f);
 extern bool _cpp_read_file_entries (cpp_reader *, FILE *);
 extern const char *_cpp_get_file_name (_cpp_file *);
 extern struct stat *_cpp_get_file_stat (_cpp_file *);
+extern bool _cpp_file_cmacro_defined_p (_cpp_file *); 
 
 /* In expr.c */
 extern bool _cpp_parse_expr (cpp_reader *, bool);

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: proposal to add -Wheader-guard option
  2014-01-30 22:43 proposal to add -Wheader-guard option Prathamesh Kulkarni
@ 2014-02-04  7:06 ` Prathamesh Kulkarni
  2014-02-04  7:15   ` Markus Trippelsdorf
  0 siblings, 1 reply; 4+ messages in thread
From: Prathamesh Kulkarni @ 2014-02-04  7:06 UTC (permalink / raw)
  To: gcc

Ping ?

On Fri, Jan 31, 2014 at 12:47 AM, Prathamesh Kulkarni
<bilbotheelffriend@gmail.com> wrote:
> Hi, I was wondering if it's a good idea to add -Wheader-guard option
> that warns on mismatches between #ifndef and #define lines
> in header guard, similar to -Wheader-guard in clang-3.4 ?
> (http://llvm.org/releases/3.4/tools/clang/docs/ReleaseNotes.html)
>
> I have implemented patch for -Wheader-guard (please find it attached).
> Consider a file having the following format:
> #ifndef cmacro (or #if !defined(cmacro) )
> #define dmacro
> // rest of the stuff
> #endif
>
> The warning is triggered if the edit distance
> (http://en.wikipedia.org/wiki/Levenshtein_distance), between cmacro
> and dmacro
> is <= max (len(cmacro), len(dmacro)) / 2
> If the edit distance is more than half, I assume that cmacro
> and dmacro are "very different", and the intent
> was probably not to define header guard (This is what clang does too).
>
> Example:
> #ifndef FOO_H
> #define _FOO_H
> #endif
>
> foo.h:1:0: warning: FOO_H used as header guard followed by #define of
> different macro [-Wheader-guard]
>  #ifndef FOO_H
>  ^
> foo.h:2:0: note: FOO_H is defined here, did you mean _FOO_H ?
>  #define _FOO_H
>  ^
>
> Warning is not triggered in the following cases:
>
> 1] The edit distance between #ifndef (or #!defined) macro
> and #define macro is > half of maximum length between two macros
>
> Example:
> #ifndef FOO
> #define BAR
> #endif
>
> 2] #ifndef and #define are not on consecutive lines (blank lines/ comment lines
> are not ignored).
>
> 3] dmacro gets undefined
> Example:
> #ifndef cmacro
> #define dmacro
> #undef dmacro
> #endif
>
> However the following warning gets generated during the build:
> ../../src/libcpp/directives.c: In function 'void _cpp_pop_buffer(cpp_reader*)':
> ../../src/libcpp/directives.c:
> 2720:59: warning: 'inc_type' may be used
> uninitialized in this function [-Wmaybe-uninitialized]
>        _cpp_pop_file_buffer (pfile, inc, to_free, inc_type);
>                                                            ^
> _cpp_pop_buffer(): http://pastebin.com/aLYLnXJa
> I have defined inc_type only if inc is not null (ie buffer is file
> buffer) in 1st if()
> and used it (passed it to _cpp_pop_file_buffer() ), only if inc is not null
> in 2nd if(). I guess this warning could be considered harmless ?
> How should I should rewrite it to avoid the warning ?
>
> Thanks and Regards,
> Prathamesh

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: proposal to add -Wheader-guard option
  2014-02-04  7:06 ` Prathamesh Kulkarni
@ 2014-02-04  7:15   ` Markus Trippelsdorf
  2014-02-04  9:39     ` Prathamesh Kulkarni
  0 siblings, 1 reply; 4+ messages in thread
From: Markus Trippelsdorf @ 2014-02-04  7:15 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc

On 2014.02.04 at 12:36 +0530, Prathamesh Kulkarni wrote:
> Ping ?

Patches should be posted to the gcc-patches@gcc.gnu.org list.
Please follow up there.

-- 
Markus

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: proposal to add -Wheader-guard option
  2014-02-04  7:15   ` Markus Trippelsdorf
@ 2014-02-04  9:39     ` Prathamesh Kulkarni
  0 siblings, 0 replies; 4+ messages in thread
From: Prathamesh Kulkarni @ 2014-02-04  9:39 UTC (permalink / raw)
  To: Markus Trippelsdorf; +Cc: gcc

On Tue, Feb 4, 2014 at 12:45 PM, Markus Trippelsdorf
<markus@trippelsdorf.de> wrote:
> On 2014.02.04 at 12:36 +0530, Prathamesh Kulkarni wrote:
>> Ping ?
>
> Patches should be posted to the gcc-patches@gcc.gnu.org list.
> Please follow up there.
Redirected to gcc-patches@gcc.gnu.org list. I apologize for posting here.
>
> --
> Markus

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-02-04  9:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-30 22:43 proposal to add -Wheader-guard option Prathamesh Kulkarni
2014-02-04  7:06 ` Prathamesh Kulkarni
2014-02-04  7:15   ` Markus Trippelsdorf
2014-02-04  9:39     ` Prathamesh Kulkarni

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).