c++, c: Introduce -Wkeyword-macro warning/pedwarn - part of C++26 P2843R3 [PR120778]

The following patch introduces a -Wkeyword-macro warning that clang has
since 2014 to implement part of C++26 P2843R3 Preprocessing is never undefined
paper.
The relevant change in the paper is moving [macro.names]/2 paragraph to
https://eel.is/c++draft/cpp.replace.general#9 :
"A translation unit shall not #define or #undef names lexically identical to
keywords, to the identifiers listed in Table 4, or to the attribute-tokens
described in [dcl.attr], except that the names likely and unlikely may be
defined as function-like macros."

Now, my understanding of the paper is that in [macro.names] and surrounding
sections the word shall bears different meaning from [cpp.replace.general],
where only the latter location implies ill-formed, diagnostic required.

The warning in clang when introduced diagnosed all #define/#undef directives
on keywords, but shortly after introduction has been changed not to
diagnose #undef at all (with "#undef a keyword is generally harmless but used
often in configuration scripts" message) and later on even the #define
part tweaked - not warn about say
  #define inline
(or const, extern, static), or
  #define keyword keyword
or
  #define keyword __keyword
or
  #define keyword __keyword__
Later on the warning has been moved to be only pedantic diagnostic unless
requested by users.  Clearly some code in the wild does e.g.
  #define private public
and similar games, or e.g. Linux kernel (sure, C) does
  #define inline __inline__ __attribute__((__always_inline__))
etc.
Now, I believe at least with the current C++26 wording such exceptions
aren't allowed (unless it is changed to IFNDR).  But given that this is just
pedantic stuff, the following patch makes the warning off by default for
C and C++ before C++26 and even for C++26 it enables it by default only
if -pedantic/-pedantic-errors (in that case it pedwarns, otherwise it
warns).  And it diagnoses both #define and #undef without exceptions.

From what I can see, all the current NODE_WARN cases are macros starting
with __ with one exception (_Pragma).  As the NODE_* flags seem to be a
limited resource, I chose to just use NODE_WARN as well and differentiate
on the node names (if they don't start with __ or _P, they are considered
to be -Wkeyword-macro registered ones, otherwise old NODE_WARN cases,
typically builtin macros or __STDC* macros).

2025-08-07  Jakub Jelinek  <jakub@redhat.com>

	PR preprocessor/120778
gcc/
	* doc/invoke.texi (Wkeyword-macro): Document.
gcc/c-family/
	* c.opt (Wkeyword-macro): New option.
	* c.opt.urls: Regenerate.
	* c-common.h (cxx_dialect): Comment formatting fix.
	* c-opts.cc (c_common_post_options): Default to
	-Wkeyword-macro for C++26 if pedantic.
gcc/c/
	* c-decl.cc (c_init_decl_processing): Mark cpp nodes corresponding
	to keywords as NODE_WARN if warn_keyword_macro.
gcc/cp/
	* lex.cc (cxx_init): Mark cpp nodes corresponding
	to keywords, identifiers with special meaning and standard
	attribute identifiers as NODE_WARN if warn_keyword_macro.
gcc/testsuite/
	* gcc.dg/Wkeyword-macro-1.c: New test.
	* gcc.dg/Wkeyword-macro-2.c: New test.
	* gcc.dg/Wkeyword-macro-3.c: New test.
	* gcc.dg/Wkeyword-macro-4.c: New test.
	* gcc.dg/Wkeyword-macro-5.c: New test.
	* gcc.dg/Wkeyword-macro-6.c: New test.
	* gcc.dg/Wkeyword-macro-7.c: New test.
	* gcc.dg/Wkeyword-macro-8.c: New test.
	* gcc.dg/Wkeyword-macro-9.c: New test.
	* g++.dg/warn/Wkeyword-macro-1.C: New test.
	* g++.dg/warn/Wkeyword-macro-2.C: New test.
	* g++.dg/warn/Wkeyword-macro-3.C: New test.
	* g++.dg/warn/Wkeyword-macro-4.C: New test.
	* g++.dg/warn/Wkeyword-macro-5.C: New test.
	* g++.dg/warn/Wkeyword-macro-6.C: New test.
	* g++.dg/warn/Wkeyword-macro-7.C: New test.
	* g++.dg/warn/Wkeyword-macro-8.C: New test.
	* g++.dg/warn/Wkeyword-macro-9.C: New test.
	* g++.dg/warn/Wkeyword-macro-10.C: New test.
	* g++.dg/opt/pr82577.C: Don't #define register to nothing for
	C++17 and later.  Instead define reg macro to nothing for C++17
	and later or to register and use it instead of register.
	* g++.dg/modules/atom-preamble-3.C: Add -Wno-keyword-macro to
	dg-additional-options.
	* g++.dg/template/sfinae17.C (static_assert): Rename macro to ...
	(my_static_assert): ... this.
	(main): Use my_static_assert instead of static_assert.
libcpp/
	* include/cpplib.h (struct cpp_options): Add cpp_warn_keyword_macro.
	(enum cpp_warning_reason): Add CPP_W_KEYWORD_MACRO enumerator.
	(cpp_keyword_p): New inline function.
	* directives.cc (do_undef): Support -Wkeyword-macro diagnostics.
	* macro.cc (warn_of_redefinition): Ignore NODE_WARN flag on nodes
	registered for -Wkeyword-macro.
	(_cpp_create_definition): Support -Wkeyword-macro diagnostics.
	Formatting fixes.
This commit is contained in:
Jakub Jelinek
2025-08-07 08:47:44 +02:00
committed by Jakub Jelinek
parent 48787c734e
commit 64859dc6e2
32 changed files with 1161 additions and 16 deletions

View File

@@ -620,6 +620,9 @@ struct cpp_options
/* True if -finput-charset= option has been used explicitly. */
bool cpp_input_charset_explicit;
/* True if -Wkeyword-macro. */
bool cpp_warn_keyword_macro;
/* -Wleading-whitespace= value. */
unsigned char cpp_warn_leading_whitespace;
@@ -757,7 +760,8 @@ enum cpp_warning_reason {
CPP_W_HEADER_GUARD,
CPP_W_PRAGMA_ONCE_OUTSIDE_HEADER,
CPP_W_LEADING_WHITESPACE,
CPP_W_TRAILING_WHITESPACE
CPP_W_TRAILING_WHITESPACE,
CPP_W_KEYWORD_MACRO
};
/* Callback for header lookup for HEADER, which is the name of a
@@ -1250,6 +1254,17 @@ inline bool cpp_fun_like_macro_p (cpp_hashnode *node)
return cpp_user_macro_p (node) && node->value.macro->fun_like;
}
/* Return true for nodes marked for -Wkeyword-macro diagnostics. */
inline bool cpp_keyword_p (cpp_hashnode *node)
{
/* As keywords are marked identifiers which don't start with underscore
or start with underscore followed by capital letter (except for
_Pragma). */
return ((node->flags & NODE_WARN)
&& (NODE_NAME (node)[0] != '_'
|| (NODE_NAME (node)[1] != '_' && NODE_NAME (node)[1] != 'P')));
}
extern const unsigned char *cpp_macro_definition (cpp_reader *, cpp_hashnode *);
extern const unsigned char *cpp_macro_definition (cpp_reader *, cpp_hashnode *,
const cpp_macro *);