diff mbox series

libcpp: Implement clang -Wheader-guard warning [PR96842]

Message ID ZuILEmBPJuADSTgu@tucnak
State New
Headers show
Series libcpp: Implement clang -Wheader-guard warning [PR96842] | expand

Commit Message

Jakub Jelinek Sept. 11, 2024, 9:26 p.m. UTC
Hi!

The following patch implements the clang -Wheader-guard warning, which warns
if a valid multiple inclusion header guard's #ifndef/#if !defined directive
is immediately (no other non-line directives nor other (non-comment)
tokens in between) followed by #define directive for some different macro,
which in get_suggestion rules is close enough to the actual header guard
macro (i.e. likely misspelling), the #define is object-like with empty
definition (I've followed what clang implements) and the macro isn't defined
later on (at least not on the final #endif at the end of a header).

In this case it emits a warning, so that
#ifndef STDIO_H
#define STDOI_H
...
#endif
or similar misspellings can be caught.

clang enables this warning by default, but I've put it into -Wall instead
as it still seems to be a style warning, nothing more severe; if a header
doesn't survive multiple inclusion because of the misspelling, users will
get different diagnostics.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-09-11  Jakub Jelinek  <jakub@redhat.com>

	PR preprocessor/96842
libcpp/
	* include/cpplib.h (struct cpp_options): Add warn_header_guard member.
	(enum cpp_warning_reason): Add CPP_W_HEADER_GUARD enumerator.
	* internal.h (struct cpp_reader): Add mi_def_cmacro, mi_loc and
	mi_def_loc members.
	(_cpp_defined_macro_p): Constify type pointed by argument type.
	Formatting fix.
	* init.cc (cpp_create_reader): Clear
	CPP_OPTION (pfile, warn_header_guard).
	* directives.cc (struct if_stack): Add def_loc and mi_def_cmacro
	members.
	(DIRECTIVE_TABLE): Add IF_COND flag to define.
	(do_define): Set ifs->mi_def_cmacro on a define immediately following
	#ifndef directive for the guard.  Clear pfile->mi_valid.  Formatting
	fix.
	(do_endif): Copy over pfile->mi_def_cmacro and pfile->mi_def_loc
	if ifs->mi_def_cmacro is set and pfile->mi_cmacro isn't a defined
	macro.
	(push_conditional): Clear mi_def_cmacro and mi_def_loc members.
	* files.cc (_cpp_pop_file_buffer): Emit -Wheader-guard diagnostics.
gcc/
	* doc/invoke.texi (Wheader-guard): Document.
gcc/c-family/
	* c.opt (Wheader-guard): New option.
	* c.opt.urls: Regenerated.
	* c-ppoutput.cc (init_pp_output): Initialize also cb->get_suggestion.
gcc/testsuite/
	* c-c++-common/cpp/Wheader-guard-1.c: New test.
	* c-c++-common/cpp/Wheader-guard-1-1.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-2.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-3.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-4.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-5.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-6.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-7.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-8.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-9.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-10.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-11.h: New test.
	* c-c++-common/cpp/Wheader-guard-1-12.h: New test.
	* c-c++-common/cpp/Wheader-guard-2.c: New test.
	* c-c++-common/cpp/Wheader-guard-2.h: New test.
	* c-c++-common/cpp/Wheader-guard-3.c: New test.
	* c-c++-common/cpp/Wheader-guard-3.h: New test.


	Jakub

Comments

Eric Gallager Sept. 12, 2024, 2:46 p.m. UTC | #1
On Wed, Sep 11, 2024 at 5:28 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> The following patch implements the clang -Wheader-guard warning, which warns
> if a valid multiple inclusion header guard's #ifndef/#if !defined directive
> is immediately (no other non-line directives nor other (non-comment)
> tokens in between) followed by #define directive for some different macro,
> which in get_suggestion rules is close enough to the actual header guard
> macro (i.e. likely misspelling), the #define is object-like with empty
> definition (I've followed what clang implements) and the macro isn't defined
> later on (at least not on the final #endif at the end of a header).
>
> In this case it emits a warning, so that
> #ifndef STDIO_H
> #define STDOI_H
> ...
> #endif
> or similar misspellings can be caught.
>
> clang enables this warning by default, but I've put it into -Wall instead
> as it still seems to be a style warning, nothing more severe; if a header
> doesn't survive multiple inclusion because of the misspelling, users will
> get different diagnostics.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>

Thanks for this patch! I can't approve it myself, but I hope someone
who can does so soon! It looks fine to me, for whatever that's worth!

> 2024-09-11  Jakub Jelinek  <jakub@redhat.com>
>
>         PR preprocessor/96842
> libcpp/
>         * include/cpplib.h (struct cpp_options): Add warn_header_guard member.
>         (enum cpp_warning_reason): Add CPP_W_HEADER_GUARD enumerator.
>         * internal.h (struct cpp_reader): Add mi_def_cmacro, mi_loc and
>         mi_def_loc members.
>         (_cpp_defined_macro_p): Constify type pointed by argument type.
>         Formatting fix.
>         * init.cc (cpp_create_reader): Clear
>         CPP_OPTION (pfile, warn_header_guard).
>         * directives.cc (struct if_stack): Add def_loc and mi_def_cmacro
>         members.
>         (DIRECTIVE_TABLE): Add IF_COND flag to define.
>         (do_define): Set ifs->mi_def_cmacro on a define immediately following
>         #ifndef directive for the guard.  Clear pfile->mi_valid.  Formatting
>         fix.
>         (do_endif): Copy over pfile->mi_def_cmacro and pfile->mi_def_loc
>         if ifs->mi_def_cmacro is set and pfile->mi_cmacro isn't a defined
>         macro.
>         (push_conditional): Clear mi_def_cmacro and mi_def_loc members.
>         * files.cc (_cpp_pop_file_buffer): Emit -Wheader-guard diagnostics.
> gcc/
>         * doc/invoke.texi (Wheader-guard): Document.
> gcc/c-family/
>         * c.opt (Wheader-guard): New option.
>         * c.opt.urls: Regenerated.
>         * c-ppoutput.cc (init_pp_output): Initialize also cb->get_suggestion.
> gcc/testsuite/
>         * c-c++-common/cpp/Wheader-guard-1.c: New test.
>         * c-c++-common/cpp/Wheader-guard-1-1.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-2.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-3.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-4.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-5.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-6.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-7.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-8.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-9.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-10.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-11.h: New test.
>         * c-c++-common/cpp/Wheader-guard-1-12.h: New test.
>         * c-c++-common/cpp/Wheader-guard-2.c: New test.
>         * c-c++-common/cpp/Wheader-guard-2.h: New test.
>         * c-c++-common/cpp/Wheader-guard-3.c: New test.
>         * c-c++-common/cpp/Wheader-guard-3.h: New test.
>
> --- libcpp/include/cpplib.h.jj  2024-09-03 16:47:47.323031836 +0200
> +++ libcpp/include/cpplib.h     2024-09-11 16:39:36.373680969 +0200
> @@ -435,6 +435,10 @@ struct cpp_options
>    /* Different -Wimplicit-fallthrough= levels.  */
>    unsigned char cpp_warn_implicit_fallthrough;
>
> +  /* Nonzero means warn about a define of a different macro right after
> +     #ifndef/#if !defined header guard directive.  */
> +  unsigned char warn_header_guard;
> +
>    /* Nonzero means we should look for header.gcc files that remap file
>       names.  */
>    unsigned char remap;
> @@ -702,7 +706,8 @@ enum cpp_warning_reason {
>    CPP_W_EXPANSION_TO_DEFINED,
>    CPP_W_BIDIRECTIONAL,
>    CPP_W_INVALID_UTF8,
> -  CPP_W_UNICODE
> +  CPP_W_UNICODE,
> +  CPP_W_HEADER_GUARD
>  };
>
>  /* Callback for header lookup for HEADER, which is the name of a
> --- libcpp/internal.h.jj        2024-09-03 16:47:47.324031823 +0200
> +++ libcpp/internal.h   2024-09-11 17:09:26.481097532 +0200
> @@ -493,9 +493,11 @@ struct cpp_reader
>       been used.  */
>    bool seen_once_only;
>
> -  /* Multiple include optimization.  */
> +  /* Multiple include optimization and -Wheader-guard warning.  */
>    const cpp_hashnode *mi_cmacro;
>    const cpp_hashnode *mi_ind_cmacro;
> +  const cpp_hashnode *mi_def_cmacro;
> +  location_t mi_loc, mi_def_loc;
>    bool mi_valid;
>
>    /* Lexing.  */
> @@ -676,7 +678,8 @@ _cpp_in_main_source_file (cpp_reader *pf
>  }
>
>  /* True if NODE is a macro for the purposes of ifdef, defined etc.  */
> -inline bool _cpp_defined_macro_p (cpp_hashnode *node)
> +inline bool
> +_cpp_defined_macro_p (const cpp_hashnode *node)
>  {
>    /* Do not treat conditional macros as being defined.  This is due to
>       the powerpc port using conditional macros for 'vector', 'bool',
> --- libcpp/init.cc.jj   2024-09-03 16:47:47.323031836 +0200
> +++ libcpp/init.cc      2024-09-11 20:07:35.533395061 +0200
> @@ -228,6 +228,7 @@ cpp_create_reader (enum c_lang lang, cpp
>    CPP_OPTION (pfile, warn_variadic_macros) = 1;
>    CPP_OPTION (pfile, warn_builtin_macro_redefined) = 1;
>    CPP_OPTION (pfile, cpp_warn_implicit_fallthrough) = 0;
> +  CPP_OPTION (pfile, warn_header_guard) = 0;
>    /* By default, track locations of tokens resulting from macro
>       expansion.  The '2' means, track the locations with the highest
>       accuracy.  Read the comments for struct
> --- libcpp/directives.cc.jj     2024-09-03 16:47:47.299032147 +0200
> +++ libcpp/directives.cc        2024-09-11 18:52:42.000000000 +0200
> @@ -31,7 +31,10 @@ struct if_stack
>  {
>    struct if_stack *next;
>    location_t line;             /* Line where condition started.  */
> -  const cpp_hashnode *mi_cmacro;/* macro name for #ifndef around entire file */
> +  location_t def_loc;          /* Locus of following #define if any.  */
> +  const cpp_hashnode *mi_cmacro;/* Macro name for #ifndef around entire
> +                                  file.  */
> +  const cpp_hashnode *mi_def_cmacro;  /* Macro name in following #define.  */
>    bool skip_elses;             /* Can future #else / #elif be skipped?  */
>    bool was_skipping;           /* If were skipping on entry.  */
>    int type;                    /* Most recent conditional for diagnostics.  */
> @@ -144,7 +147,7 @@ static void cpp_pop_definition (cpp_read
>     where the extension appears to have come from.  */
>
>  #define DIRECTIVE_TABLE                                                        \
> -  D(define,    T_DEFINE = 0,   KANDR,     IN_I)                        \
> +  D(define,    T_DEFINE = 0,   KANDR,     IN_I | IF_COND)              \
>    D(include,   T_INCLUDE,      KANDR,     INCL | EXPAND)               \
>    D(endif,     T_ENDIF,        KANDR,     COND)                        \
>    D(ifdef,     T_IFDEF,        KANDR,     COND | IF_COND)              \
> @@ -661,8 +664,8 @@ do_define (cpp_reader *pfile)
>
>        /* If we have been requested to expand comments into macros,
>          then re-enable saving of comments.  */
> -      pfile->state.save_comments =
> -       ! CPP_OPTION (pfile, discard_comments_in_macro_exp);
> +      pfile->state.save_comments
> +       = ! CPP_OPTION (pfile, discard_comments_in_macro_exp);
>
>        if (pfile->cb.before_define)
>         pfile->cb.before_define (pfile);
> @@ -672,7 +675,28 @@ do_define (cpp_reader *pfile)
>           pfile->cb.define (pfile, pfile->directive_line, node);
>
>        node->flags &= ~NODE_USED;
> +
> +      if (pfile->mi_valid
> +         && !pfile->mi_cmacro
> +         && CPP_OPTION (pfile, warn_header_guard)
> +         && node->type == NT_USER_MACRO
> +         && node->value.macro
> +         && node->value.macro->count == 0
> +         && !node->value.macro->fun_like)
> +       {
> +         cpp_buffer *buffer = pfile->buffer;
> +         struct if_stack *ifs = buffer->if_stack;
> +         if (ifs
> +             && !ifs->next
> +             && ifs->mi_cmacro
> +             && node != ifs->mi_cmacro)
> +           {
> +             ifs->mi_def_cmacro = node;
> +             ifs->def_loc = pfile->directive_line;
> +           }
> +       }
>      }
> +  pfile->mi_valid = false;
>  }
>
>  /* Handle #undef.  Mark the identifier NT_VOID in the hash table.  */
> @@ -2251,6 +2275,13 @@ do_endif (cpp_reader *pfile)
>         {
>           pfile->mi_valid = true;
>           pfile->mi_cmacro = ifs->mi_cmacro;
> +         pfile->mi_loc = ifs->line;
> +         pfile->mi_def_cmacro = NULL;
> +         if (ifs->mi_def_cmacro && !_cpp_defined_macro_p (pfile->mi_cmacro))
> +           {
> +             pfile->mi_def_cmacro = ifs->mi_def_cmacro;
> +             pfile->mi_def_loc = ifs->def_loc;
> +           }
>         }
>
>        buffer->if_stack = ifs->next;
> @@ -2272,6 +2303,7 @@ push_conditional (cpp_reader *pfile, int
>
>    ifs = XOBNEW (&pfile->buffer_ob, struct if_stack);
>    ifs->line = pfile->directive_line;
> +  ifs->def_loc = 0;
>    ifs->next = buffer->if_stack;
>    ifs->skip_elses = pfile->state.skipping || !skip;
>    ifs->was_skipping = pfile->state.skipping;
> @@ -2281,6 +2313,7 @@ push_conditional (cpp_reader *pfile, int
>      ifs->mi_cmacro = cmacro;
>    else
>      ifs->mi_cmacro = 0;
> +  ifs->mi_def_cmacro = 0;
>
>    pfile->state.skipping = skip;
>    buffer->if_stack = ifs;
> --- libcpp/files.cc.jj  2024-09-03 16:47:47.322031849 +0200
> +++ libcpp/files.cc     2024-09-11 19:32:43.754868132 +0200
> @@ -1664,7 +1664,28 @@ _cpp_pop_file_buffer (cpp_reader *pfile,
>    /* Record the inclusion-preventing macro, which could be NULL
>       meaning no controlling macro.  */
>    if (pfile->mi_valid && file->cmacro == NULL)
> -    file->cmacro = pfile->mi_cmacro;
> +    {
> +      file->cmacro = pfile->mi_cmacro;
> +      if (pfile->mi_cmacro
> +         && pfile->mi_def_cmacro
> +         && pfile->cb.get_suggestion)
> +       {
> +         const char *names[]
> +           = { (const char *) NODE_NAME (pfile->mi_def_cmacro), NULL };
> +         if (pfile->cb.get_suggestion (pfile,
> +                                       (const char *)
> +                                       NODE_NAME (pfile->mi_cmacro), names)
> +             && cpp_warning_with_line (pfile, CPP_W_HEADER_GUARD,
> +                                       pfile->mi_loc, 0,
> +                                       "header guard \"%s\" followed by "
> +                                       "\"#define\" of a different macro",
> +                                       NODE_NAME (pfile->mi_cmacro)))
> +           cpp_error_at (pfile, CPP_DL_NOTE, pfile->mi_def_loc,
> +                         "\"%s\" is defined here; did you mean \"%s\"?",
> +                         NODE_NAME (pfile->mi_def_cmacro),
> +                         NODE_NAME (pfile->mi_cmacro));
> +       }
> +    }
>
>    /* Invalidate control macros in the #including file.  */
>    pfile->mi_valid = false;
> --- gcc/doc/invoke.texi.jj      2024-09-09 09:27:38.841086571 +0200
> +++ gcc/doc/invoke.texi 2024-09-11 20:23:10.033730119 +0200
> @@ -371,7 +371,7 @@ Objective-C and Objective-C++ Dialects}.
>  -Wformat-security  -Wformat-signedness  -Wformat-truncation=@var{n}
>  -Wformat-y2k  -Wframe-address
>  -Wframe-larger-than=@var{byte-size}  -Wno-free-nonheap-object
> --Wno-if-not-aligned  -Wno-ignored-attributes
> +-Wheader-guard  -Wno-if-not-aligned  -Wno-ignored-attributes
>  -Wignored-qualifiers  -Wno-incompatible-pointer-types  -Whardened
>  -Wimplicit  -Wimplicit-fallthrough  -Wimplicit-fallthrough=@var{n}
>  -Wno-implicit-function-declaration  -Wno-implicit-int
> @@ -10096,6 +10096,19 @@ Do not warn if certain built-in macros a
>  warnings for redefinition of @code{__TIMESTAMP__}, @code{__TIME__},
>  @code{__DATE__}, @code{__FILE__}, and @code{__BASE_FILE__}.
>
> +@opindex Wheader-guard
> +@item -Wheader-guard
> +Warn if a valid preprocessor header multiple inclusion guard has
> +a @code{#define} directive right after @code{#ifndef} or @code{#if !defined}
> +directive for the multiple inclusion guard, which defines a different macro
> +from the guard macro with a similar name, the actual multiple inclusion guard
> +macro isn't defined at the corresponding @code{#ifndef} directive at the end
> +of the header, and the @code{#define} directive defines object-like macro with
> +empty definition.  In such case, it often is just misspelled guard name,
> +either in the @code{#ifndef} or @code{#if !defined} directive or in the
> +subsequent @code{#define} directive.  This warning is enabled
> +by @option{-Wall}.
> +
>  @opindex Wstrict-prototypes
>  @opindex Wno-strict-prototypes
>  @item -Wstrict-prototypes @r{(C and Objective-C only)}
> --- gcc/c-family/c.opt.jj       2024-09-03 16:47:47.185033625 +0200
> +++ gcc/c-family/c.opt  2024-09-11 20:07:15.786662712 +0200
> @@ -814,6 +814,10 @@ Wglobal-module
>  C++ ObjC++ Var(warn_global_module) Warning Init(1)
>  Warn about the global module fragment not containing only preprocessing directives.
>
> +Wheader-guard
> +C ObjC C++ ObjC++ CPP(warn_header_guard) CppReason(CPP_W_HEADER_GUARD) Var(cpp_warn_header_guard) Init(0) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
> +Warn when #ifndef of a header guard is followed by #define of a different macro with the header guard macro not defined at the end of header.
> +
>  Wif-not-aligned
>  C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning
>  Warn when the field in a struct is not aligned.
> --- gcc/c-family/c.opt.urls.jj  2024-08-14 18:19:28.731129432 +0200
> +++ gcc/c-family/c.opt.urls     2024-09-11 20:24:22.262751351 +0200
> @@ -412,6 +412,9 @@ UrlSuffix(gcc/Warning-Options.html#index
>  Wglobal-module
>  UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wglobal-module)
>
> +Wheader-guard
> +UrlSuffix(gcc/Warning-Options.html#index-Wheader-guard)
> +
>  Wif-not-aligned
>  UrlSuffix(gcc/Warning-Options.html#index-Wif-not-aligned)
>
> --- gcc/c-family/c-ppoutput.cc.jj       2024-08-05 13:04:53.489121581 +0200
> +++ gcc/c-family/c-ppoutput.cc  2024-09-11 20:02:06.500854915 +0200
> @@ -164,6 +164,7 @@ init_pp_output (FILE *out_stream)
>    cb->has_builtin = c_common_has_builtin;
>    cb->has_feature = c_common_has_feature;
>    cb->get_source_date_epoch = cb_get_source_date_epoch;
> +  cb->get_suggestion = cb_get_suggestion;
>    cb->remap_filename = remap_macro_filename;
>
>    /* Initialize the print structure.  */
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c.jj 2024-09-11 19:26:43.967778739 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c    2024-09-11 19:26:20.772095324 +0200
> @@ -0,0 +1,18 @@
> +/* PR preprocessor/96842 */
> +/* { dg-do preprocess } */
> +
> +#include "Wheader-guard-1-1.h"
> +#include "Wheader-guard-1-2.h"
> +#include "Wheader-guard-1-3.h"
> +#include "Wheader-guard-1-4.h"
> +#include "Wheader-guard-1-5.h"
> +#include "Wheader-guard-1-6.h"
> +#include "Wheader-guard-1-7.h"
> +#define WHEADER_GUARD_8
> +#include "Wheader-guard-1-8.h"
> +#include "Wheader-guard-1-9.h"
> +#include "Wheader-guard-1-10.h"
> +#include "Wheader-guard-1-11.h"
> +#include "Wheader-guard-1-12.h"
> +
> +int i;
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h.jj       2024-09-11 19:26:39.912834079 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h  2024-09-11 19:02:02.414054285 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_1
> +#define WHEADER_GUARD_1
> +/* This is how header guards should look like.  */
> +#define SOMETHING1 123
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h.jj       2024-09-11 19:26:39.914834052 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h  2024-09-11 19:11:14.876484333 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_2
> +#define WHEADERGUARD2 1
> +/* Don't warn if the different macro defines some tokens.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h.jj       2024-09-11 19:26:39.915834039 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h  2024-09-11 19:11:23.834361776 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_3
> +#define WHEADERGUARD3()
> +/* Won't warn if it is a function-like macro.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h.jj       2024-09-11 19:26:39.918833998 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h  2024-09-11 19:04:18.569185598 +0200
> @@ -0,0 +1,3 @@
> +#ifndef WHEADER_GUARD_4
> +/* Don't warn if there is no define after #ifndef.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h.jj       2024-09-11 19:26:39.919833984 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h  2024-09-11 19:11:46.934045743 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_5
> +int guard5;
> +#define WHEADERGUARD5
> +/* Don't warn if there are tokens in between #ifndef and #define.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h.jj       2024-09-11 19:26:39.921833957 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h  2024-09-11 19:11:59.357875762 +0200
> @@ -0,0 +1,8 @@
> +#ifndef WHEADER_GUARD_6
> +#define WHEADERGUARD6
> +/* Don't warn if WHEADER_GUARD_6 is eventually defined later.  */
> +#if 0
> +#else
> +#define WHEADER_GUARD_6
> +#endif
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h.jj       2024-09-11 19:26:39.923833930 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h  2024-09-11 19:22:21.860358981 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_7
> +#define SOMETHING7
> +/* Don't warn if the two macros don't have similar names.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h.jj       2024-09-11 19:26:39.925833903 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h  2024-09-11 19:23:13.884647219 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_8
> +#define WHEADERGUARD8
> +/* Don't warn if the guard macro is already defined before
> +   including the header.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h.jj       2024-09-11 19:26:39.926833889 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h  2024-09-11 19:24:34.647543778 +0200
> @@ -0,0 +1,5 @@
> +int guard9;
> +#ifndef WHEADER_GUARD_9
> +#define WHEADERGUARD9
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h.jj      2024-09-11 19:26:39.928833862 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h 2024-09-11 19:25:02.012170293 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_10
> +#define WHEADERGUARD10
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#else
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h.jj      2024-09-11 19:26:39.930833835 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h 2024-09-11 19:25:30.972775022 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_11
> +#define WHEADERGUARD11
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#elif 1
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h.jj      2024-09-11 19:26:39.932833808 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h 2024-09-11 19:25:56.790422645 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_12
> +#define WHEADERGUARD12
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#endif
> +#define ASOMETHING12
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c.jj 2024-09-11 19:28:02.611705351 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c    2024-09-11 20:12:47.124171614 +0200
> @@ -0,0 +1,10 @@
> +/* PR preprocessor/96842 */
> +/* { dg-do preprocess } */
> +/* { dg-options "-Wheader-guard" } */
> +
> +#include "Wheader-guard-2.h"
> +
> +int i;
> +
> +/* { dg-warning "header guard \"WHEADER_GUARD_2\" followed by \"#define\" of a different macro" "" { target *-*-* } 0 } */
> +/* { dg-message "\"WHEADERGUARD2\" is defined here; did you mean \"WHEADER_GUARD_2\"\\\?" "" { target *-*-* } 0 } */
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h.jj 2024-09-11 19:28:45.893114621 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h    2024-09-11 19:28:40.376189917 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_2
> +#define WHEADERGUARD2
> +#define SOMETHING2 123
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c.jj 2024-09-11 19:29:52.333207805 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c    2024-09-11 20:13:17.867754901 +0200
> @@ -0,0 +1,10 @@
> +/* PR preprocessor/96842 */
> +/* { dg-do preprocess } */
> +/* { dg-options "-Wall" } */
> +
> +#include "Wheader-guard-3.h"
> +
> +int i;
> +
> +/* { dg-warning "header guard \"WHEADER_GUARD_3\" followed by \"#define\" of a different macro" "" { target *-*-* } 0 } */
> +/* { dg-message "\"WHEADERGUARD3\" is defined here; did you mean \"WHEADER_GUARD_3\"\\\?" "" { target *-*-* } 0 } */
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h.jj 2024-09-11 19:30:09.454974114 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h    2024-09-11 19:30:23.562781561 +0200
> @@ -0,0 +1,4 @@
> +#if !defined(WHEADER_GUARD_3)
> +#define WHEADERGUARD3
> +#define SOMETHING3 123
> +#endif
>
>         Jakub
>
David Malcolm Sept. 12, 2024, 3:12 p.m. UTC | #2
On Wed, 2024-09-11 at 23:26 +0200, Jakub Jelinek wrote:
> Hi!
> 
> The following patch implements the clang -Wheader-guard warning,
> which warns
> if a valid multiple inclusion header guard's #ifndef/#if !defined
> directive
> is immediately (no other non-line directives nor other (non-comment)
> tokens in between) followed by #define directive for some different
> macro,
> which in get_suggestion rules is close enough to the actual header
> guard
> macro (i.e. likely misspelling), the #define is object-like with
> empty
> definition (I've followed what clang implements) and the macro isn't
> defined
> later on (at least not on the final #endif at the end of a header).
> 
> In this case it emits a warning, so that
> #ifndef STDIO_H
> #define STDOI_H
> ...
> #endif
> or similar misspellings can be caught.
> 
> clang enables this warning by default, but I've put it into -Wall
> instead
> as it still seems to be a style warning, nothing more severe; if a
> header
> doesn't survive multiple inclusion because of the misspelling, users
> will
> get different diagnostics.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 

Overall, LGTM, but I'm not as familiar with libcpp's implementation
details.

> 
> --- libcpp/files.cc.jj	2024-09-03 16:47:47.322031849 +0200
> +++ libcpp/files.cc	2024-09-11 19:32:43.754868132 +0200
> @@ -1664,7 +1664,28 @@ _cpp_pop_file_buffer (cpp_reader *pfile,
>    /* Record the inclusion-preventing macro, which could be NULL
>       meaning no controlling macro.  */
>    if (pfile->mi_valid && file->cmacro == NULL)
> -    file->cmacro = pfile->mi_cmacro;
> +    {
> +      file->cmacro = pfile->mi_cmacro;
> +      if (pfile->mi_cmacro
> +	  && pfile->mi_def_cmacro
> +	  && pfile->cb.get_suggestion)
> +	{
> +	  const char *names[]
> +	    = { (const char *) NODE_NAME (pfile->mi_def_cmacro),
> NULL };
> +	  if (pfile->cb.get_suggestion (pfile,
> +					(const char *)
> +					NODE_NAME (pfile-
> >mi_cmacro), names)
> +	      && cpp_warning_with_line (pfile, CPP_W_HEADER_GUARD,
> +					pfile->mi_loc, 0,
> +					"header guard \"%s\"
> followed by "
> +					"\"#define\" of a different
> macro",
> +					NODE_NAME (pfile-
> >mi_cmacro)))
> +	    cpp_error_at (pfile, CPP_DL_NOTE, pfile->mi_def_loc,
> +			  "\"%s\" is defined here; did you mean
> \"%s\"?",
> +			  NODE_NAME (pfile->mi_def_cmacro),
> +			  NODE_NAME (pfile->mi_cmacro));
> +	}

We were chatting on IRC about how it would be nice to be able to use
%qs in libcppp diagnostics; here is an example (rather than using
\"%s\").

Not a blocker, but it occurs to me that ideally we'd group the warning
and note into a diagnostic group, but unfortunately there's no way to
express that currently via the interface libcpp has.  We would need to
add {begin,end}_group hooks, which in turn suggests that maybe that
libcpp's interface into diagnostics should be an abstract base class
with various vfuncs, rather than a callback.

Also not a blocker, but it would nice to have a fix-it hint here, by
using the rich_location overload of cpp_error_at and adding a fix-it
hint to the rich_location.

Hope this is constructive
Dave
Jakub Jelinek Sept. 12, 2024, 3:18 p.m. UTC | #3
On Thu, Sep 12, 2024 at 11:12:26AM -0400, David Malcolm wrote:
> We were chatting on IRC about how it would be nice to be able to use
> %qs in libcppp diagnostics; here is an example (rather than using
> \"%s\").

Yeah, I'm working on a patch for that.

> Not a blocker, but it occurs to me that ideally we'd group the warning
> and note into a diagnostic group, but unfortunately there's no way to
> express that currently via the interface libcpp has.  We would need to
> add {begin,end}_group hooks, which in turn suggests that maybe that
> libcpp's interface into diagnostics should be an abstract base class
> with various vfuncs, rather than a callback.

I haven't added auto_diagnostic_group because nothing in libcpp does that,
yes, we need some solution for that.

> Also not a blocker, but it would nice to have a fix-it hint here, by
> using the rich_location overload of cpp_error_at and adding a fix-it
> hint to the rich_location.

And yes, I was thinking about fix-it hint, but I think that depends on
better locations there first, currently the patch uses just the lines
with the directives.
I was hoping that can be done incrementally.

	Jakub
David Malcolm Sept. 12, 2024, 3:28 p.m. UTC | #4
On Thu, 2024-09-12 at 17:18 +0200, Jakub Jelinek wrote:
> On Thu, Sep 12, 2024 at 11:12:26AM -0400, David Malcolm wrote:
> > We were chatting on IRC about how it would be nice to be able to
> > use
> > %qs in libcppp diagnostics; here is an example (rather than using
> > \"%s\").
> 
> Yeah, I'm working on a patch for that.

Thanks.

> 
> > Not a blocker, but it occurs to me that ideally we'd group the
> > warning
> > and note into a diagnostic group, but unfortunately there's no way
> > to
> > express that currently via the interface libcpp has.  We would need
> > to
> > add {begin,end}_group hooks, which in turn suggests that maybe that
> > libcpp's interface into diagnostics should be an abstract base
> > class
> > with various vfuncs, rather than a callback.
> 
> I haven't added auto_diagnostic_group because nothing in libcpp does
> that,
> yes, we need some solution for that.

(nods)

> 
> > Also not a blocker, but it would nice to have a fix-it hint here,
> > by
> > using the rich_location overload of cpp_error_at and adding a fix-
> > it
> > hint to the rich_location.
> 
> And yes, I was thinking about fix-it hint, but I think that depends
> on
> better locations there first, currently the patch uses just the lines
> with the directives.
> I was hoping that can be done incrementally.

Indeed, let's defer the fix-it hint to a possible followup.


Thanks
Dave
Marek Polacek Oct. 1, 2024, 9:53 p.m. UTC | #5
On Wed, Sep 11, 2024 at 11:26:42PM +0200, Jakub Jelinek wrote:
> Hi!
> 
> The following patch implements the clang -Wheader-guard warning, which warns
> if a valid multiple inclusion header guard's #ifndef/#if !defined directive
> is immediately (no other non-line directives nor other (non-comment)
> tokens in between) followed by #define directive for some different macro,
> which in get_suggestion rules is close enough to the actual header guard
> macro (i.e. likely misspelling), the #define is object-like with empty
> definition (I've followed what clang implements) and the macro isn't defined
> later on (at least not on the final #endif at the end of a header).
> 
> In this case it emits a warning, so that
> #ifndef STDIO_H
> #define STDOI_H
> ...
> #endif
> or similar misspellings can be caught.
> 
> clang enables this warning by default, but I've put it into -Wall instead
> as it still seems to be a style warning, nothing more severe; if a header
> doesn't survive multiple inclusion because of the misspelling, users will
> get different diagnostics.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2024-09-11  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR preprocessor/96842
> libcpp/
> 	* include/cpplib.h (struct cpp_options): Add warn_header_guard member.
> 	(enum cpp_warning_reason): Add CPP_W_HEADER_GUARD enumerator.
> 	* internal.h (struct cpp_reader): Add mi_def_cmacro, mi_loc and
> 	mi_def_loc members.
> 	(_cpp_defined_macro_p): Constify type pointed by argument type.
> 	Formatting fix.
> 	* init.cc (cpp_create_reader): Clear
> 	CPP_OPTION (pfile, warn_header_guard).
> 	* directives.cc (struct if_stack): Add def_loc and mi_def_cmacro
> 	members.
> 	(DIRECTIVE_TABLE): Add IF_COND flag to define.
> 	(do_define): Set ifs->mi_def_cmacro on a define immediately following
> 	#ifndef directive for the guard.  Clear pfile->mi_valid.  Formatting
> 	fix.
> 	(do_endif): Copy over pfile->mi_def_cmacro and pfile->mi_def_loc
> 	if ifs->mi_def_cmacro is set and pfile->mi_cmacro isn't a defined
> 	macro.
> 	(push_conditional): Clear mi_def_cmacro and mi_def_loc members.
> 	* files.cc (_cpp_pop_file_buffer): Emit -Wheader-guard diagnostics.
> gcc/
> 	* doc/invoke.texi (Wheader-guard): Document.
> gcc/c-family/
> 	* c.opt (Wheader-guard): New option.
> 	* c.opt.urls: Regenerated.
> 	* c-ppoutput.cc (init_pp_output): Initialize also cb->get_suggestion.
> gcc/testsuite/
> 	* c-c++-common/cpp/Wheader-guard-1.c: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-1.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-2.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-3.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-4.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-5.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-6.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-7.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-8.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-9.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-10.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-11.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-1-12.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-2.c: New test.
> 	* c-c++-common/cpp/Wheader-guard-2.h: New test.
> 	* c-c++-common/cpp/Wheader-guard-3.c: New test.
> 	* c-c++-common/cpp/Wheader-guard-3.h: New test.
> 
> --- libcpp/include/cpplib.h.jj	2024-09-03 16:47:47.323031836 +0200
> +++ libcpp/include/cpplib.h	2024-09-11 16:39:36.373680969 +0200
> @@ -435,6 +435,10 @@ struct cpp_options
>    /* Different -Wimplicit-fallthrough= levels.  */
>    unsigned char cpp_warn_implicit_fallthrough;
>  
> +  /* Nonzero means warn about a define of a different macro right after
> +     #ifndef/#if !defined header guard directive.  */
> +  unsigned char warn_header_guard;
> +
>    /* Nonzero means we should look for header.gcc files that remap file
>       names.  */
>    unsigned char remap;
> @@ -702,7 +706,8 @@ enum cpp_warning_reason {
>    CPP_W_EXPANSION_TO_DEFINED,
>    CPP_W_BIDIRECTIONAL,
>    CPP_W_INVALID_UTF8,
> -  CPP_W_UNICODE
> +  CPP_W_UNICODE,
> +  CPP_W_HEADER_GUARD
>  };
>  
>  /* Callback for header lookup for HEADER, which is the name of a
> --- libcpp/internal.h.jj	2024-09-03 16:47:47.324031823 +0200
> +++ libcpp/internal.h	2024-09-11 17:09:26.481097532 +0200
> @@ -493,9 +493,11 @@ struct cpp_reader
>       been used.  */
>    bool seen_once_only;
>  
> -  /* Multiple include optimization.  */
> +  /* Multiple include optimization and -Wheader-guard warning.  */
>    const cpp_hashnode *mi_cmacro;
>    const cpp_hashnode *mi_ind_cmacro;
> +  const cpp_hashnode *mi_def_cmacro;
> +  location_t mi_loc, mi_def_loc;
>    bool mi_valid;
>  
>    /* Lexing.  */
> @@ -676,7 +678,8 @@ _cpp_in_main_source_file (cpp_reader *pf
>  }
>  
>  /* True if NODE is a macro for the purposes of ifdef, defined etc.  */
> -inline bool _cpp_defined_macro_p (cpp_hashnode *node)
> +inline bool
> +_cpp_defined_macro_p (const cpp_hashnode *node)
>  {
>    /* Do not treat conditional macros as being defined.  This is due to
>       the powerpc port using conditional macros for 'vector', 'bool',
> --- libcpp/init.cc.jj	2024-09-03 16:47:47.323031836 +0200
> +++ libcpp/init.cc	2024-09-11 20:07:35.533395061 +0200
> @@ -228,6 +228,7 @@ cpp_create_reader (enum c_lang lang, cpp
>    CPP_OPTION (pfile, warn_variadic_macros) = 1;
>    CPP_OPTION (pfile, warn_builtin_macro_redefined) = 1;
>    CPP_OPTION (pfile, cpp_warn_implicit_fallthrough) = 0;
> +  CPP_OPTION (pfile, warn_header_guard) = 0;
>    /* By default, track locations of tokens resulting from macro
>       expansion.  The '2' means, track the locations with the highest
>       accuracy.  Read the comments for struct
> --- libcpp/directives.cc.jj	2024-09-03 16:47:47.299032147 +0200
> +++ libcpp/directives.cc	2024-09-11 18:52:42.000000000 +0200
> @@ -31,7 +31,10 @@ struct if_stack
>  {
>    struct if_stack *next;
>    location_t line;		/* Line where condition started.  */
> -  const cpp_hashnode *mi_cmacro;/* macro name for #ifndef around entire file */
> +  location_t def_loc;		/* Locus of following #define if any.  */

I'd write "of the following".

> +  const cpp_hashnode *mi_cmacro;/* Macro name for #ifndef around entire
> +				   file.  */
> +  const cpp_hashnode *mi_def_cmacro;  /* Macro name in following #define.  */

And "in the following", though I see the surrounding comments also drop the
the...

>    bool skip_elses;		/* Can future #else / #elif be skipped?  */
>    bool was_skipping;		/* If were skipping on entry.  */
>    int type;			/* Most recent conditional for diagnostics.  */
> @@ -144,7 +147,7 @@ static void cpp_pop_definition (cpp_read
>     where the extension appears to have come from.  */
>  
>  #define DIRECTIVE_TABLE							\
> -  D(define,	T_DEFINE = 0,	KANDR,     IN_I)			\
> +  D(define,	T_DEFINE = 0,	KANDR,     IN_I | IF_COND)		\
>    D(include,	T_INCLUDE,	KANDR,     INCL | EXPAND)		\
>    D(endif,	T_ENDIF,	KANDR,     COND)			\
>    D(ifdef,	T_IFDEF,	KANDR,     COND | IF_COND)		\
> @@ -661,8 +664,8 @@ do_define (cpp_reader *pfile)
>  
>        /* If we have been requested to expand comments into macros,
>  	 then re-enable saving of comments.  */
> -      pfile->state.save_comments =
> -	! CPP_OPTION (pfile, discard_comments_in_macro_exp);
> +      pfile->state.save_comments
> +	= ! CPP_OPTION (pfile, discard_comments_in_macro_exp);
>  
>        if (pfile->cb.before_define)
>  	pfile->cb.before_define (pfile);
> @@ -672,7 +675,28 @@ do_define (cpp_reader *pfile)
>  	  pfile->cb.define (pfile, pfile->directive_line, node);
>  
>        node->flags &= ~NODE_USED;
> +
> +      if (pfile->mi_valid
> +	  && !pfile->mi_cmacro
> +	  && CPP_OPTION (pfile, warn_header_guard)
> +	  && node->type == NT_USER_MACRO
> +	  && node->value.macro
> +	  && node->value.macro->count == 0
> +	  && !node->value.macro->fun_like)
> +	{
> +	  cpp_buffer *buffer = pfile->buffer;
> +	  struct if_stack *ifs = buffer->if_stack;
> +	  if (ifs
> +	      && !ifs->next
> +	      && ifs->mi_cmacro
> +	      && node != ifs->mi_cmacro)
> +	    {
> +	      ifs->mi_def_cmacro = node;
> +	      ifs->def_loc = pfile->directive_line;
> +	    }
> +	}
>      }
> +  pfile->mi_valid = false;
>  }
>  
>  /* Handle #undef.  Mark the identifier NT_VOID in the hash table.  */
> @@ -2251,6 +2275,13 @@ do_endif (cpp_reader *pfile)
>  	{
>  	  pfile->mi_valid = true;
>  	  pfile->mi_cmacro = ifs->mi_cmacro;
> +	  pfile->mi_loc = ifs->line;
> +	  pfile->mi_def_cmacro = NULL;

Maybe s/NULL/nullptr/ nowadays.

> +	  if (ifs->mi_def_cmacro && !_cpp_defined_macro_p (pfile->mi_cmacro))
> +	    {
> +	      pfile->mi_def_cmacro = ifs->mi_def_cmacro;
> +	      pfile->mi_def_loc = ifs->def_loc;
> +	    }
>  	}
>  
>        buffer->if_stack = ifs->next;
> @@ -2272,6 +2303,7 @@ push_conditional (cpp_reader *pfile, int
>  
>    ifs = XOBNEW (&pfile->buffer_ob, struct if_stack);
>    ifs->line = pfile->directive_line;
> +  ifs->def_loc = 0;
>    ifs->next = buffer->if_stack;
>    ifs->skip_elses = pfile->state.skipping || !skip;
>    ifs->was_skipping = pfile->state.skipping;
> @@ -2281,6 +2313,7 @@ push_conditional (cpp_reader *pfile, int
>      ifs->mi_cmacro = cmacro;
>    else
>      ifs->mi_cmacro = 0;
> +  ifs->mi_def_cmacro = 0;

s/0/nullptr/

>    pfile->state.skipping = skip;
>    buffer->if_stack = ifs;
> --- libcpp/files.cc.jj	2024-09-03 16:47:47.322031849 +0200
> +++ libcpp/files.cc	2024-09-11 19:32:43.754868132 +0200
> @@ -1664,7 +1664,28 @@ _cpp_pop_file_buffer (cpp_reader *pfile,
>    /* Record the inclusion-preventing macro, which could be NULL
>       meaning no controlling macro.  */
>    if (pfile->mi_valid && file->cmacro == NULL)
> -    file->cmacro = pfile->mi_cmacro;
> +    {
> +      file->cmacro = pfile->mi_cmacro;
> +      if (pfile->mi_cmacro
> +	  && pfile->mi_def_cmacro
> +	  && pfile->cb.get_suggestion)
> +	{

Consider

  auto mi_cmacro = (const char *) NODE_NAME (pfile->mi_cmacro);
  auto mi_def_cmacro = (const char *) NODE_NAME (pfile->mi_def_cmacro);

which I think would make the rest of the code a little bit more readable.

> +	  const char *names[]
> +	    = { (const char *) NODE_NAME (pfile->mi_def_cmacro), NULL };
> +	  if (pfile->cb.get_suggestion (pfile,
> +					(const char *)
> +					NODE_NAME (pfile->mi_cmacro), names)
> +	      && cpp_warning_with_line (pfile, CPP_W_HEADER_GUARD,
> +					pfile->mi_loc, 0,
> +					"header guard \"%s\" followed by "
> +					"\"#define\" of a different macro",
> +					NODE_NAME (pfile->mi_cmacro)))
> +	    cpp_error_at (pfile, CPP_DL_NOTE, pfile->mi_def_loc,
> +			  "\"%s\" is defined here; did you mean \"%s\"?",
> +			  NODE_NAME (pfile->mi_def_cmacro),
> +			  NODE_NAME (pfile->mi_cmacro));
> +	}
> +    }
>  
>    /* Invalidate control macros in the #including file.  */
>    pfile->mi_valid = false;
> --- gcc/doc/invoke.texi.jj	2024-09-09 09:27:38.841086571 +0200
> +++ gcc/doc/invoke.texi	2024-09-11 20:23:10.033730119 +0200
> @@ -371,7 +371,7 @@ Objective-C and Objective-C++ Dialects}.
>  -Wformat-security  -Wformat-signedness  -Wformat-truncation=@var{n}
>  -Wformat-y2k  -Wframe-address
>  -Wframe-larger-than=@var{byte-size}  -Wno-free-nonheap-object
> --Wno-if-not-aligned  -Wno-ignored-attributes
> +-Wheader-guard  -Wno-if-not-aligned  -Wno-ignored-attributes
>  -Wignored-qualifiers  -Wno-incompatible-pointer-types  -Whardened
>  -Wimplicit  -Wimplicit-fallthrough  -Wimplicit-fallthrough=@var{n}
>  -Wno-implicit-function-declaration  -Wno-implicit-int
> @@ -10096,6 +10096,19 @@ Do not warn if certain built-in macros a
>  warnings for redefinition of @code{__TIMESTAMP__}, @code{__TIME__},
>  @code{__DATE__}, @code{__FILE__}, and @code{__BASE_FILE__}.
>  
> +@opindex Wheader-guard
> +@item -Wheader-guard
> +Warn if a valid preprocessor header multiple inclusion guard has
> +a @code{#define} directive right after @code{#ifndef} or @code{#if !defined}
> +directive for the multiple inclusion guard, which defines a different macro
> +from the guard macro with a similar name, the actual multiple inclusion guard
> +macro isn't defined at the corresponding @code{#ifndef} directive at the end
> +of the header, and the @code{#define} directive defines object-like macro with

"defines an object-like macro"

> +empty definition.  In such case, it often is just misspelled guard name,

"just a misspelled guard name"

> +either in the @code{#ifndef} or @code{#if !defined} directive or in the
> +subsequent @code{#define} directive.  This warning is enabled
> +by @option{-Wall}.
> +
>  @opindex Wstrict-prototypes
>  @opindex Wno-strict-prototypes
>  @item -Wstrict-prototypes @r{(C and Objective-C only)}
> --- gcc/c-family/c.opt.jj	2024-09-03 16:47:47.185033625 +0200
> +++ gcc/c-family/c.opt	2024-09-11 20:07:15.786662712 +0200
> @@ -814,6 +814,10 @@ Wglobal-module
>  C++ ObjC++ Var(warn_global_module) Warning Init(1)
>  Warn about the global module fragment not containing only preprocessing directives.
>  
> +Wheader-guard
> +C ObjC C++ ObjC++ CPP(warn_header_guard) CppReason(CPP_W_HEADER_GUARD) Var(cpp_warn_header_guard) Init(0) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
> +Warn when #ifndef of a header guard is followed by #define of a different macro with the header guard macro not defined at the end of header.
> +
>  Wif-not-aligned
>  C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning
>  Warn when the field in a struct is not aligned.
> --- gcc/c-family/c.opt.urls.jj	2024-08-14 18:19:28.731129432 +0200
> +++ gcc/c-family/c.opt.urls	2024-09-11 20:24:22.262751351 +0200
> @@ -412,6 +412,9 @@ UrlSuffix(gcc/Warning-Options.html#index
>  Wglobal-module
>  UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wglobal-module)
>  
> +Wheader-guard
> +UrlSuffix(gcc/Warning-Options.html#index-Wheader-guard)
> +
>  Wif-not-aligned
>  UrlSuffix(gcc/Warning-Options.html#index-Wif-not-aligned)
>  
> --- gcc/c-family/c-ppoutput.cc.jj	2024-08-05 13:04:53.489121581 +0200
> +++ gcc/c-family/c-ppoutput.cc	2024-09-11 20:02:06.500854915 +0200
> @@ -164,6 +164,7 @@ init_pp_output (FILE *out_stream)
>    cb->has_builtin = c_common_has_builtin;
>    cb->has_feature = c_common_has_feature;
>    cb->get_source_date_epoch = cb_get_source_date_epoch;
> +  cb->get_suggestion = cb_get_suggestion;
>    cb->remap_filename = remap_macro_filename;
>  
>    /* Initialize the print structure.  */
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c.jj	2024-09-11 19:26:43.967778739 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c	2024-09-11 19:26:20.772095324 +0200
> @@ -0,0 +1,18 @@
> +/* PR preprocessor/96842 */
> +/* { dg-do preprocess } */
> +

This is missing
/* { dg-options "-Wall" } */
otherwise we won't detect bogus diagnostics.

> +#include "Wheader-guard-1-1.h"
> +#include "Wheader-guard-1-2.h"
> +#include "Wheader-guard-1-3.h"
> +#include "Wheader-guard-1-4.h"
> +#include "Wheader-guard-1-5.h"
> +#include "Wheader-guard-1-6.h"
> +#include "Wheader-guard-1-7.h"
> +#define WHEADER_GUARD_8
> +#include "Wheader-guard-1-8.h"
> +#include "Wheader-guard-1-9.h"
> +#include "Wheader-guard-1-10.h"
> +#include "Wheader-guard-1-11.h"
> +#include "Wheader-guard-1-12.h"
> +
> +int i;
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h.jj	2024-09-11 19:26:39.912834079 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h	2024-09-11 19:02:02.414054285 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_1
> +#define WHEADER_GUARD_1
> +/* This is how header guards should look like.  */

s/how/what/

> +#define SOMETHING1 123
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h.jj	2024-09-11 19:26:39.914834052 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h	2024-09-11 19:11:14.876484333 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_2
> +#define WHEADERGUARD2 1
> +/* Don't warn if the different macro defines some tokens.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h.jj	2024-09-11 19:26:39.915834039 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h	2024-09-11 19:11:23.834361776 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_3
> +#define WHEADERGUARD3()
> +/* Won't warn if it is a function-like macro.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h.jj	2024-09-11 19:26:39.918833998 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h	2024-09-11 19:04:18.569185598 +0200
> @@ -0,0 +1,3 @@
> +#ifndef WHEADER_GUARD_4
> +/* Don't warn if there is no define after #ifndef.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h.jj	2024-09-11 19:26:39.919833984 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h	2024-09-11 19:11:46.934045743 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_5
> +int guard5;
> +#define WHEADERGUARD5
> +/* Don't warn if there are tokens in between #ifndef and #define.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h.jj	2024-09-11 19:26:39.921833957 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h	2024-09-11 19:11:59.357875762 +0200
> @@ -0,0 +1,8 @@
> +#ifndef WHEADER_GUARD_6
> +#define WHEADERGUARD6
> +/* Don't warn if WHEADER_GUARD_6 is eventually defined later.  */
> +#if 0
> +#else
> +#define WHEADER_GUARD_6
> +#endif
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h.jj	2024-09-11 19:26:39.923833930 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h	2024-09-11 19:22:21.860358981 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_7
> +#define SOMETHING7
> +/* Don't warn if the two macros don't have similar names.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h.jj	2024-09-11 19:26:39.925833903 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h	2024-09-11 19:23:13.884647219 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_8
> +#define WHEADERGUARD8
> +/* Don't warn if the guard macro is already defined before
> +   including the header.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h.jj	2024-09-11 19:26:39.926833889 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h	2024-09-11 19:24:34.647543778 +0200
> @@ -0,0 +1,5 @@
> +int guard9;
> +#ifndef WHEADER_GUARD_9
> +#define WHEADERGUARD9
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h.jj	2024-09-11 19:26:39.928833862 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h	2024-09-11 19:25:02.012170293 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_10
> +#define WHEADERGUARD10
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#else
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h.jj	2024-09-11 19:26:39.930833835 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h	2024-09-11 19:25:30.972775022 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_11
> +#define WHEADERGUARD11
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#elif 1
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h.jj	2024-09-11 19:26:39.932833808 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h	2024-09-11 19:25:56.790422645 +0200
> @@ -0,0 +1,5 @@
> +#ifndef WHEADER_GUARD_12
> +#define WHEADERGUARD12
> +/* Don't warn if it actually isn't a valid header guard.  */
> +#endif
> +#define ASOMETHING12
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c.jj	2024-09-11 19:28:02.611705351 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c	2024-09-11 20:12:47.124171614 +0200
> @@ -0,0 +1,10 @@
> +/* PR preprocessor/96842 */
> +/* { dg-do preprocess } */
> +/* { dg-options "-Wheader-guard" } */
> +
> +#include "Wheader-guard-2.h"
> +
> +int i;
> +
> +/* { dg-warning "header guard \"WHEADER_GUARD_2\" followed by \"#define\" of a different macro" "" { target *-*-* } 0 } */
> +/* { dg-message "\"WHEADERGUARD2\" is defined here; did you mean \"WHEADER_GUARD_2\"\\\?" "" { target *-*-* } 0 } */
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h.jj	2024-09-11 19:28:45.893114621 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h	2024-09-11 19:28:40.376189917 +0200
> @@ -0,0 +1,4 @@
> +#ifndef WHEADER_GUARD_2
> +#define WHEADERGUARD2
> +#define SOMETHING2 123
> +#endif
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c.jj	2024-09-11 19:29:52.333207805 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c	2024-09-11 20:13:17.867754901 +0200
> @@ -0,0 +1,10 @@
> +/* PR preprocessor/96842 */
> +/* { dg-do preprocess } */
> +/* { dg-options "-Wall" } */
> +
> +#include "Wheader-guard-3.h"
> +
> +int i;
> +
> +/* { dg-warning "header guard \"WHEADER_GUARD_3\" followed by \"#define\" of a different macro" "" { target *-*-* } 0 } */
> +/* { dg-message "\"WHEADERGUARD3\" is defined here; did you mean \"WHEADER_GUARD_3\"\\\?" "" { target *-*-* } 0 } */
> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h.jj	2024-09-11 19:30:09.454974114 +0200
> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h	2024-09-11 19:30:23.562781561 +0200
> @@ -0,0 +1,4 @@
> +#if !defined(WHEADER_GUARD_3)
> +#define WHEADERGUARD3
> +#define SOMETHING3 123
> +#endif

The patch is OK if you add the missing -Wall.  The rest are nits.

I've reviewed our header guards and a few of them look non-standard, e.g.
tree-stdarg.h has

#ifndef GCC_TREE_STDARG_H
#define GCC_TREE_STDARG_H 1
...
#endif

More like this:
ctfc.h
dump-context.h
dumpfile.h
dwarf2codeview.h
dwarf2ctf.h
dwarf2out.h
expmed.h
inchash.h
int-vector-builder.h
iterator-utils.h
lower-subreg.h
mux-utils.h
rtlhash.h
rtl-ssa.h
target-globals.h
tree-hasher.h
tree-iterator.h
tree-pass.h
tree-ssa-live.h
tree-ssa-propagate.h
tree-ssa-threadupdate.h
tree-stdarg.h
vec-perm-indices.h
vmsdbg.h
cp/mapper-client.h

Marek
Sam James Oct. 1, 2024, 10:37 p.m. UTC | #6
Marek Polacek <polacek@redhat.com> writes:

> On Wed, Sep 11, 2024 at 11:26:42PM +0200, Jakub Jelinek wrote:
> [...]
>> --- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h.jj	2024-09-11 19:26:39.912834079 +0200
>> +++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h	2024-09-11 19:02:02.414054285 +0200
>> @@ -0,0 +1,5 @@
>> +#ifndef WHEADER_GUARD_1
>> +#define WHEADER_GUARD_1
>> +/* This is how header guards should look like.  */
>
> s/how/what/
>

(or: "This is how header guards should look.")

> [...]
diff mbox series

Patch

--- libcpp/include/cpplib.h.jj	2024-09-03 16:47:47.323031836 +0200
+++ libcpp/include/cpplib.h	2024-09-11 16:39:36.373680969 +0200
@@ -435,6 +435,10 @@  struct cpp_options
   /* Different -Wimplicit-fallthrough= levels.  */
   unsigned char cpp_warn_implicit_fallthrough;
 
+  /* Nonzero means warn about a define of a different macro right after
+     #ifndef/#if !defined header guard directive.  */
+  unsigned char warn_header_guard;
+
   /* Nonzero means we should look for header.gcc files that remap file
      names.  */
   unsigned char remap;
@@ -702,7 +706,8 @@  enum cpp_warning_reason {
   CPP_W_EXPANSION_TO_DEFINED,
   CPP_W_BIDIRECTIONAL,
   CPP_W_INVALID_UTF8,
-  CPP_W_UNICODE
+  CPP_W_UNICODE,
+  CPP_W_HEADER_GUARD
 };
 
 /* Callback for header lookup for HEADER, which is the name of a
--- libcpp/internal.h.jj	2024-09-03 16:47:47.324031823 +0200
+++ libcpp/internal.h	2024-09-11 17:09:26.481097532 +0200
@@ -493,9 +493,11 @@  struct cpp_reader
      been used.  */
   bool seen_once_only;
 
-  /* Multiple include optimization.  */
+  /* Multiple include optimization and -Wheader-guard warning.  */
   const cpp_hashnode *mi_cmacro;
   const cpp_hashnode *mi_ind_cmacro;
+  const cpp_hashnode *mi_def_cmacro;
+  location_t mi_loc, mi_def_loc;
   bool mi_valid;
 
   /* Lexing.  */
@@ -676,7 +678,8 @@  _cpp_in_main_source_file (cpp_reader *pf
 }
 
 /* True if NODE is a macro for the purposes of ifdef, defined etc.  */
-inline bool _cpp_defined_macro_p (cpp_hashnode *node)
+inline bool
+_cpp_defined_macro_p (const cpp_hashnode *node)
 {
   /* Do not treat conditional macros as being defined.  This is due to
      the powerpc port using conditional macros for 'vector', 'bool',
--- libcpp/init.cc.jj	2024-09-03 16:47:47.323031836 +0200
+++ libcpp/init.cc	2024-09-11 20:07:35.533395061 +0200
@@ -228,6 +228,7 @@  cpp_create_reader (enum c_lang lang, cpp
   CPP_OPTION (pfile, warn_variadic_macros) = 1;
   CPP_OPTION (pfile, warn_builtin_macro_redefined) = 1;
   CPP_OPTION (pfile, cpp_warn_implicit_fallthrough) = 0;
+  CPP_OPTION (pfile, warn_header_guard) = 0;
   /* By default, track locations of tokens resulting from macro
      expansion.  The '2' means, track the locations with the highest
      accuracy.  Read the comments for struct
--- libcpp/directives.cc.jj	2024-09-03 16:47:47.299032147 +0200
+++ libcpp/directives.cc	2024-09-11 18:52:42.000000000 +0200
@@ -31,7 +31,10 @@  struct if_stack
 {
   struct if_stack *next;
   location_t line;		/* Line where condition started.  */
-  const cpp_hashnode *mi_cmacro;/* macro name for #ifndef around entire file */
+  location_t def_loc;		/* Locus of following #define if any.  */
+  const cpp_hashnode *mi_cmacro;/* Macro name for #ifndef around entire
+				   file.  */
+  const cpp_hashnode *mi_def_cmacro;  /* Macro name in following #define.  */
   bool skip_elses;		/* Can future #else / #elif be skipped?  */
   bool was_skipping;		/* If were skipping on entry.  */
   int type;			/* Most recent conditional for diagnostics.  */
@@ -144,7 +147,7 @@  static void cpp_pop_definition (cpp_read
    where the extension appears to have come from.  */
 
 #define DIRECTIVE_TABLE							\
-  D(define,	T_DEFINE = 0,	KANDR,     IN_I)			\
+  D(define,	T_DEFINE = 0,	KANDR,     IN_I | IF_COND)		\
   D(include,	T_INCLUDE,	KANDR,     INCL | EXPAND)		\
   D(endif,	T_ENDIF,	KANDR,     COND)			\
   D(ifdef,	T_IFDEF,	KANDR,     COND | IF_COND)		\
@@ -661,8 +664,8 @@  do_define (cpp_reader *pfile)
 
       /* If we have been requested to expand comments into macros,
 	 then re-enable saving of comments.  */
-      pfile->state.save_comments =
-	! CPP_OPTION (pfile, discard_comments_in_macro_exp);
+      pfile->state.save_comments
+	= ! CPP_OPTION (pfile, discard_comments_in_macro_exp);
 
       if (pfile->cb.before_define)
 	pfile->cb.before_define (pfile);
@@ -672,7 +675,28 @@  do_define (cpp_reader *pfile)
 	  pfile->cb.define (pfile, pfile->directive_line, node);
 
       node->flags &= ~NODE_USED;
+
+      if (pfile->mi_valid
+	  && !pfile->mi_cmacro
+	  && CPP_OPTION (pfile, warn_header_guard)
+	  && node->type == NT_USER_MACRO
+	  && node->value.macro
+	  && node->value.macro->count == 0
+	  && !node->value.macro->fun_like)
+	{
+	  cpp_buffer *buffer = pfile->buffer;
+	  struct if_stack *ifs = buffer->if_stack;
+	  if (ifs
+	      && !ifs->next
+	      && ifs->mi_cmacro
+	      && node != ifs->mi_cmacro)
+	    {
+	      ifs->mi_def_cmacro = node;
+	      ifs->def_loc = pfile->directive_line;
+	    }
+	}
     }
+  pfile->mi_valid = false;
 }
 
 /* Handle #undef.  Mark the identifier NT_VOID in the hash table.  */
@@ -2251,6 +2275,13 @@  do_endif (cpp_reader *pfile)
 	{
 	  pfile->mi_valid = true;
 	  pfile->mi_cmacro = ifs->mi_cmacro;
+	  pfile->mi_loc = ifs->line;
+	  pfile->mi_def_cmacro = NULL;
+	  if (ifs->mi_def_cmacro && !_cpp_defined_macro_p (pfile->mi_cmacro))
+	    {
+	      pfile->mi_def_cmacro = ifs->mi_def_cmacro;
+	      pfile->mi_def_loc = ifs->def_loc;
+	    }
 	}
 
       buffer->if_stack = ifs->next;
@@ -2272,6 +2303,7 @@  push_conditional (cpp_reader *pfile, int
 
   ifs = XOBNEW (&pfile->buffer_ob, struct if_stack);
   ifs->line = pfile->directive_line;
+  ifs->def_loc = 0;
   ifs->next = buffer->if_stack;
   ifs->skip_elses = pfile->state.skipping || !skip;
   ifs->was_skipping = pfile->state.skipping;
@@ -2281,6 +2313,7 @@  push_conditional (cpp_reader *pfile, int
     ifs->mi_cmacro = cmacro;
   else
     ifs->mi_cmacro = 0;
+  ifs->mi_def_cmacro = 0;
 
   pfile->state.skipping = skip;
   buffer->if_stack = ifs;
--- libcpp/files.cc.jj	2024-09-03 16:47:47.322031849 +0200
+++ libcpp/files.cc	2024-09-11 19:32:43.754868132 +0200
@@ -1664,7 +1664,28 @@  _cpp_pop_file_buffer (cpp_reader *pfile,
   /* Record the inclusion-preventing macro, which could be NULL
      meaning no controlling macro.  */
   if (pfile->mi_valid && file->cmacro == NULL)
-    file->cmacro = pfile->mi_cmacro;
+    {
+      file->cmacro = pfile->mi_cmacro;
+      if (pfile->mi_cmacro
+	  && pfile->mi_def_cmacro
+	  && pfile->cb.get_suggestion)
+	{
+	  const char *names[]
+	    = { (const char *) NODE_NAME (pfile->mi_def_cmacro), NULL };
+	  if (pfile->cb.get_suggestion (pfile,
+					(const char *)
+					NODE_NAME (pfile->mi_cmacro), names)
+	      && cpp_warning_with_line (pfile, CPP_W_HEADER_GUARD,
+					pfile->mi_loc, 0,
+					"header guard \"%s\" followed by "
+					"\"#define\" of a different macro",
+					NODE_NAME (pfile->mi_cmacro)))
+	    cpp_error_at (pfile, CPP_DL_NOTE, pfile->mi_def_loc,
+			  "\"%s\" is defined here; did you mean \"%s\"?",
+			  NODE_NAME (pfile->mi_def_cmacro),
+			  NODE_NAME (pfile->mi_cmacro));
+	}
+    }
 
   /* Invalidate control macros in the #including file.  */
   pfile->mi_valid = false;
--- gcc/doc/invoke.texi.jj	2024-09-09 09:27:38.841086571 +0200
+++ gcc/doc/invoke.texi	2024-09-11 20:23:10.033730119 +0200
@@ -371,7 +371,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wformat-security  -Wformat-signedness  -Wformat-truncation=@var{n}
 -Wformat-y2k  -Wframe-address
 -Wframe-larger-than=@var{byte-size}  -Wno-free-nonheap-object
--Wno-if-not-aligned  -Wno-ignored-attributes
+-Wheader-guard  -Wno-if-not-aligned  -Wno-ignored-attributes
 -Wignored-qualifiers  -Wno-incompatible-pointer-types  -Whardened
 -Wimplicit  -Wimplicit-fallthrough  -Wimplicit-fallthrough=@var{n}
 -Wno-implicit-function-declaration  -Wno-implicit-int
@@ -10096,6 +10096,19 @@  Do not warn if certain built-in macros a
 warnings for redefinition of @code{__TIMESTAMP__}, @code{__TIME__},
 @code{__DATE__}, @code{__FILE__}, and @code{__BASE_FILE__}.
 
+@opindex Wheader-guard
+@item -Wheader-guard
+Warn if a valid preprocessor header multiple inclusion guard has
+a @code{#define} directive right after @code{#ifndef} or @code{#if !defined}
+directive for the multiple inclusion guard, which defines a different macro
+from the guard macro with a similar name, the actual multiple inclusion guard
+macro isn't defined at the corresponding @code{#ifndef} directive at the end
+of the header, and the @code{#define} directive defines object-like macro with
+empty definition.  In such case, it often is just misspelled guard name,
+either in the @code{#ifndef} or @code{#if !defined} directive or in the
+subsequent @code{#define} directive.  This warning is enabled
+by @option{-Wall}.
+
 @opindex Wstrict-prototypes
 @opindex Wno-strict-prototypes
 @item -Wstrict-prototypes @r{(C and Objective-C only)}
--- gcc/c-family/c.opt.jj	2024-09-03 16:47:47.185033625 +0200
+++ gcc/c-family/c.opt	2024-09-11 20:07:15.786662712 +0200
@@ -814,6 +814,10 @@  Wglobal-module
 C++ ObjC++ Var(warn_global_module) Warning Init(1)
 Warn about the global module fragment not containing only preprocessing directives.
 
+Wheader-guard
+C ObjC C++ ObjC++ CPP(warn_header_guard) CppReason(CPP_W_HEADER_GUARD) Var(cpp_warn_header_guard) Init(0) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn when #ifndef of a header guard is followed by #define of a different macro with the header guard macro not defined at the end of header.
+
 Wif-not-aligned
 C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning
 Warn when the field in a struct is not aligned.
--- gcc/c-family/c.opt.urls.jj	2024-08-14 18:19:28.731129432 +0200
+++ gcc/c-family/c.opt.urls	2024-09-11 20:24:22.262751351 +0200
@@ -412,6 +412,9 @@  UrlSuffix(gcc/Warning-Options.html#index
 Wglobal-module
 UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wglobal-module)
 
+Wheader-guard
+UrlSuffix(gcc/Warning-Options.html#index-Wheader-guard)
+
 Wif-not-aligned
 UrlSuffix(gcc/Warning-Options.html#index-Wif-not-aligned)
 
--- gcc/c-family/c-ppoutput.cc.jj	2024-08-05 13:04:53.489121581 +0200
+++ gcc/c-family/c-ppoutput.cc	2024-09-11 20:02:06.500854915 +0200
@@ -164,6 +164,7 @@  init_pp_output (FILE *out_stream)
   cb->has_builtin = c_common_has_builtin;
   cb->has_feature = c_common_has_feature;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
+  cb->get_suggestion = cb_get_suggestion;
   cb->remap_filename = remap_macro_filename;
 
   /* Initialize the print structure.  */
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c.jj	2024-09-11 19:26:43.967778739 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c	2024-09-11 19:26:20.772095324 +0200
@@ -0,0 +1,18 @@ 
+/* PR preprocessor/96842 */
+/* { dg-do preprocess } */
+
+#include "Wheader-guard-1-1.h"
+#include "Wheader-guard-1-2.h"
+#include "Wheader-guard-1-3.h"
+#include "Wheader-guard-1-4.h"
+#include "Wheader-guard-1-5.h"
+#include "Wheader-guard-1-6.h"
+#include "Wheader-guard-1-7.h"
+#define WHEADER_GUARD_8
+#include "Wheader-guard-1-8.h"
+#include "Wheader-guard-1-9.h"
+#include "Wheader-guard-1-10.h"
+#include "Wheader-guard-1-11.h"
+#include "Wheader-guard-1-12.h"
+
+int i;
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h.jj	2024-09-11 19:26:39.912834079 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h	2024-09-11 19:02:02.414054285 +0200
@@ -0,0 +1,5 @@ 
+#ifndef WHEADER_GUARD_1
+#define WHEADER_GUARD_1
+/* This is how header guards should look like.  */
+#define SOMETHING1 123
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h.jj	2024-09-11 19:26:39.914834052 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h	2024-09-11 19:11:14.876484333 +0200
@@ -0,0 +1,4 @@ 
+#ifndef WHEADER_GUARD_2
+#define WHEADERGUARD2 1
+/* Don't warn if the different macro defines some tokens.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h.jj	2024-09-11 19:26:39.915834039 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h	2024-09-11 19:11:23.834361776 +0200
@@ -0,0 +1,4 @@ 
+#ifndef WHEADER_GUARD_3
+#define WHEADERGUARD3()
+/* Won't warn if it is a function-like macro.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h.jj	2024-09-11 19:26:39.918833998 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h	2024-09-11 19:04:18.569185598 +0200
@@ -0,0 +1,3 @@ 
+#ifndef WHEADER_GUARD_4
+/* Don't warn if there is no define after #ifndef.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h.jj	2024-09-11 19:26:39.919833984 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h	2024-09-11 19:11:46.934045743 +0200
@@ -0,0 +1,5 @@ 
+#ifndef WHEADER_GUARD_5
+int guard5;
+#define WHEADERGUARD5
+/* Don't warn if there are tokens in between #ifndef and #define.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h.jj	2024-09-11 19:26:39.921833957 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h	2024-09-11 19:11:59.357875762 +0200
@@ -0,0 +1,8 @@ 
+#ifndef WHEADER_GUARD_6
+#define WHEADERGUARD6
+/* Don't warn if WHEADER_GUARD_6 is eventually defined later.  */
+#if 0
+#else
+#define WHEADER_GUARD_6
+#endif
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h.jj	2024-09-11 19:26:39.923833930 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h	2024-09-11 19:22:21.860358981 +0200
@@ -0,0 +1,4 @@ 
+#ifndef WHEADER_GUARD_7
+#define SOMETHING7
+/* Don't warn if the two macros don't have similar names.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h.jj	2024-09-11 19:26:39.925833903 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h	2024-09-11 19:23:13.884647219 +0200
@@ -0,0 +1,5 @@ 
+#ifndef WHEADER_GUARD_8
+#define WHEADERGUARD8
+/* Don't warn if the guard macro is already defined before
+   including the header.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h.jj	2024-09-11 19:26:39.926833889 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h	2024-09-11 19:24:34.647543778 +0200
@@ -0,0 +1,5 @@ 
+int guard9;
+#ifndef WHEADER_GUARD_9
+#define WHEADERGUARD9
+/* Don't warn if it actually isn't a valid header guard.  */
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h.jj	2024-09-11 19:26:39.928833862 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h	2024-09-11 19:25:02.012170293 +0200
@@ -0,0 +1,5 @@ 
+#ifndef WHEADER_GUARD_10
+#define WHEADERGUARD10
+/* Don't warn if it actually isn't a valid header guard.  */
+#else
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h.jj	2024-09-11 19:26:39.930833835 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h	2024-09-11 19:25:30.972775022 +0200
@@ -0,0 +1,5 @@ 
+#ifndef WHEADER_GUARD_11
+#define WHEADERGUARD11
+/* Don't warn if it actually isn't a valid header guard.  */
+#elif 1
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h.jj	2024-09-11 19:26:39.932833808 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h	2024-09-11 19:25:56.790422645 +0200
@@ -0,0 +1,5 @@ 
+#ifndef WHEADER_GUARD_12
+#define WHEADERGUARD12
+/* Don't warn if it actually isn't a valid header guard.  */
+#endif
+#define ASOMETHING12
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c.jj	2024-09-11 19:28:02.611705351 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c	2024-09-11 20:12:47.124171614 +0200
@@ -0,0 +1,10 @@ 
+/* PR preprocessor/96842 */
+/* { dg-do preprocess } */
+/* { dg-options "-Wheader-guard" } */
+
+#include "Wheader-guard-2.h"
+
+int i;
+
+/* { dg-warning "header guard \"WHEADER_GUARD_2\" followed by \"#define\" of a different macro" "" { target *-*-* } 0 } */
+/* { dg-message "\"WHEADERGUARD2\" is defined here; did you mean \"WHEADER_GUARD_2\"\\\?" "" { target *-*-* } 0 } */
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h.jj	2024-09-11 19:28:45.893114621 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h	2024-09-11 19:28:40.376189917 +0200
@@ -0,0 +1,4 @@ 
+#ifndef WHEADER_GUARD_2
+#define WHEADERGUARD2
+#define SOMETHING2 123
+#endif
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c.jj	2024-09-11 19:29:52.333207805 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c	2024-09-11 20:13:17.867754901 +0200
@@ -0,0 +1,10 @@ 
+/* PR preprocessor/96842 */
+/* { dg-do preprocess } */
+/* { dg-options "-Wall" } */
+
+#include "Wheader-guard-3.h"
+
+int i;
+
+/* { dg-warning "header guard \"WHEADER_GUARD_3\" followed by \"#define\" of a different macro" "" { target *-*-* } 0 } */
+/* { dg-message "\"WHEADERGUARD3\" is defined here; did you mean \"WHEADER_GUARD_3\"\\\?" "" { target *-*-* } 0 } */
--- gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h.jj	2024-09-11 19:30:09.454974114 +0200
+++ gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h	2024-09-11 19:30:23.562781561 +0200
@@ -0,0 +1,4 @@ 
+#if !defined(WHEADER_GUARD_3)
+#define WHEADERGUARD3
+#define SOMETHING3 123
+#endif