https://gcc.gnu.org/g:5943a2fa1bc5407332a91976c145446cdb8ded7b

commit r15-4010-g5943a2fa1bc5407332a91976c145446cdb8ded7b
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Wed Oct 2 10:53:35 2024 +0200

    libcpp: Implement clang -Wheader-guard warning [PR96842]
    
    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.
    
    2024-10-02  Jakub Jelinek  <ja...@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.

Diff:
---
 gcc/c-family/c-ppoutput.cc                         |  1 +
 gcc/c-family/c.opt                                 |  4 +++
 gcc/c-family/c.opt.urls                            |  3 ++
 gcc/doc/invoke.texi                                | 15 +++++++-
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h |  5 +++
 .../c-c++-common/cpp/Wheader-guard-1-10.h          |  5 +++
 .../c-c++-common/cpp/Wheader-guard-1-11.h          |  5 +++
 .../c-c++-common/cpp/Wheader-guard-1-12.h          |  5 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h |  4 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h |  4 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h |  3 ++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h |  5 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h |  8 +++++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h |  4 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h |  5 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h |  5 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c   | 19 ++++++++++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c   | 10 ++++++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h   |  4 +++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c   | 10 ++++++
 gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h   |  4 +++
 libcpp/directives.cc                               | 42 +++++++++++++++++++---
 libcpp/files.cc                                    | 21 ++++++++++-
 libcpp/include/cpplib.h                            |  7 +++-
 libcpp/init.cc                                     |  1 +
 libcpp/internal.h                                  |  7 ++--
 26 files changed, 197 insertions(+), 9 deletions(-)

diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc
index c674610760b6..e3f5ca3ec97c 100644
--- a/gcc/c-family/c-ppoutput.cc
+++ b/gcc/c-family/c-ppoutput.cc
@@ -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.  */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index b5983093e24a..77fe1b02bd2f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -828,6 +828,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.
diff --git a/gcc/c-family/c.opt.urls b/gcc/c-family/c.opt.urls
index 47a54926363b..4cd9a75b950c 100644
--- a/gcc/c-family/c.opt.urls
+++ b/gcc/c-family/c.opt.urls
@@ -415,6 +415,9 @@ UrlSuffix(gcc/Warning-Options.html#index-Wframe-address)
 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)
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index bd1208a62ee1..e199522f62c7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -372,7 +372,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
@@ -10115,6 +10115,19 @@ Do not warn if certain built-in macros are redefined.  
This suppresses
 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 an object-like macro
+with empty definition.  In such case, it often is 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)}
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h
new file mode 100644
index 000000000000..1ac3fe865ad4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-1.h
@@ -0,0 +1,5 @@
+#ifndef WHEADER_GUARD_1
+#define WHEADER_GUARD_1
+/* This is what header guards should look like.  */
+#define SOMETHING1 123
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h
new file mode 100644
index 000000000000..119a2a869b64
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-10.h
@@ -0,0 +1,5 @@
+#ifndef WHEADER_GUARD_10
+#define WHEADERGUARD10
+/* Don't warn if it actually isn't a valid header guard.  */
+#else
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h
new file mode 100644
index 000000000000..88820a51590f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-11.h
@@ -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
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h
new file mode 100644
index 000000000000..9233fbffea40
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-12.h
@@ -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
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h
new file mode 100644
index 000000000000..25d4fb5b2e3f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-2.h
@@ -0,0 +1,4 @@
+#ifndef WHEADER_GUARD_2
+#define WHEADERGUARD2 1
+/* Don't warn if the different macro defines some tokens.  */
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h
new file mode 100644
index 000000000000..d995e3f2cd4b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-3.h
@@ -0,0 +1,4 @@
+#ifndef WHEADER_GUARD_3
+#define WHEADERGUARD3()
+/* Won't warn if it is a function-like macro.  */
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h
new file mode 100644
index 000000000000..6d031a5687a7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-4.h
@@ -0,0 +1,3 @@
+#ifndef WHEADER_GUARD_4
+/* Don't warn if there is no define after #ifndef.  */
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h
new file mode 100644
index 000000000000..d691ba9f4f3f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-5.h
@@ -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
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h
new file mode 100644
index 000000000000..1057162da387
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-6.h
@@ -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
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h
new file mode 100644
index 000000000000..d0e0708ab662
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-7.h
@@ -0,0 +1,4 @@
+#ifndef WHEADER_GUARD_7
+#define SOMETHING7
+/* Don't warn if the two macros don't have similar names.  */
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h
new file mode 100644
index 000000000000..2be231bc1078
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-8.h
@@ -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
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h
new file mode 100644
index 000000000000..2152eafc89ce
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1-9.h
@@ -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
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c
new file mode 100644
index 000000000000..6074d872afd5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-1.c
@@ -0,0 +1,19 @@
+/* PR preprocessor/96842 */
+/* { dg-do preprocess } */
+/* { dg-options "-Wall" } */
+
+#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;
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c
new file mode 100644
index 000000000000..9596fe7e3a09
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.c
@@ -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 } */
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h
new file mode 100644
index 000000000000..c38d2d52b599
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-2.h
@@ -0,0 +1,4 @@
+#ifndef WHEADER_GUARD_2
+#define WHEADERGUARD2
+#define SOMETHING2 123
+#endif
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c
new file mode 100644
index 000000000000..9fa4fbfa3076
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.c
@@ -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 } */
diff --git a/gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h 
b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h
new file mode 100644
index 000000000000..cb610ed47223
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/Wheader-guard-3.h
@@ -0,0 +1,4 @@
+#if !defined(WHEADER_GUARD_3)
+#define WHEADERGUARD3
+#define SOMETHING3 123
+#endif
diff --git a/libcpp/directives.cc b/libcpp/directives.cc
index 866ac9a823d0..c30087c7a1d9 100644
--- a/libcpp/directives.cc
+++ b/libcpp/directives.cc
@@ -31,7 +31,11 @@ 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 the 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 the 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 +148,7 @@ static void cpp_pop_definition (cpp_reader *, struct 
def_pragma_macro *);
    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)              \
@@ -680,8 +684,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);
@@ -691,7 +695,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.  */
@@ -2711,6 +2736,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 = nullptr;
+         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;
@@ -2732,6 +2764,7 @@ push_conditional (cpp_reader *pfile, int skip, int type,
 
   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;
@@ -2741,6 +2774,7 @@ push_conditional (cpp_reader *pfile, int skip, int type,
     ifs->mi_cmacro = cmacro;
   else
     ifs->mi_cmacro = 0;
+  ifs->mi_def_cmacro = nullptr;
 
   pfile->state.skipping = skip;
   buffer->if_stack = ifs;
diff --git a/libcpp/files.cc b/libcpp/files.cc
index 031169978e72..5f9fbc54e999 100644
--- a/libcpp/files.cc
+++ b/libcpp/files.cc
@@ -2253,7 +2253,26 @@ _cpp_pop_file_buffer (cpp_reader *pfile, _cpp_file *file,
   /* 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)
+       {
+         auto mi_cmacro = (const char *) NODE_NAME (pfile->mi_cmacro);
+         auto mi_def_cmacro = (const char *) NODE_NAME (pfile->mi_def_cmacro);
+         const char *names[] = { mi_def_cmacro, NULL };
+         if (pfile->cb.get_suggestion (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",
+                                       mi_cmacro))
+           cpp_error_at (pfile, CPP_DL_NOTE, pfile->mi_def_loc,
+                         "\"%s\" is defined here; did you mean \"%s\"?",
+                         mi_def_cmacro, mi_cmacro);
+       }
+    }
 
   /* Invalidate control macros in the #including file.  */
   pfile->mi_valid = false;
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 76e2437e06a1..fbddc34b6af0 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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;
@@ -709,7 +713,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
diff --git a/libcpp/init.cc b/libcpp/init.cc
index b22481c29653..2c80d63a491d 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -230,6 +230,7 @@ cpp_create_reader (enum c_lang lang, cpp_hash_table *table,
   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
diff --git a/libcpp/internal.h b/libcpp/internal.h
index fa64a69954ff..b69a0377f024 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -495,9 +495,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.  */
@@ -696,7 +698,8 @@ _cpp_in_main_source_file (cpp_reader *pfile)
 }
 
 /* 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',

Reply via email to