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 , 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);