diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc index c674610760b..e3f5ca3ec97 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 b5983093e24..77fe1b02bd2 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 47a54926363..4cd9a75b950 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 bd1208a62ee..e199522f62c 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 00000000000..1ac3fe865ad --- /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 00000000000..119a2a869b6 --- /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 00000000000..88820a51590 --- /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 00000000000..9233fbffea4 --- /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 00000000000..25d4fb5b2e3 --- /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 00000000000..d995e3f2cd4 --- /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 00000000000..6d031a5687a --- /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 00000000000..d691ba9f4f3 --- /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 00000000000..1057162da38 --- /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 00000000000..d0e0708ab66 --- /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 00000000000..2be231bc107 --- /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 00000000000..2152eafc89c --- /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 00000000000..6074d872afd --- /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 00000000000..9596fe7e3a0 --- /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 00000000000..c38d2d52b59 --- /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 00000000000..9fa4fbfa307 --- /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 00000000000..cb610ed4722 --- /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 866ac9a823d..c30087c7a1d 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 031169978e7..5f9fbc54e99 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 76e2437e06a..fbddc34b6af 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 b22481c2965..2c80d63a491 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 fa64a69954f..b69a0377f02 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',