gcc/gcc/diagnostic-core.h
Lewis Hyatt 44ba7bcb75 diagnostics: Fix behavior of permerror options after diagnostic pop [PR111918]
When a diagnostic pragma changes the classification of a given diagnostic,
the global options flags (such as warn_narrowing, etc.) may get changed too.
Specifically, if a warning was not enabled initially and was later enabled
by a pragma, then the corresponding global flag will change from false to
true when the pragma is processed. That change is permanent and is not
undone by a subsequent `#pragma GCC diagnostic pop'; the warning flag needs
to remain enabled since a diagnostic could be generated later on for a
source location prior to the pop.

So in order to support popping to the initial classification, given that the
global options flags no longer reflect that state, the diagnostic_context
object itself remembers the way things were before it changed anything. The
current implementation works fine for diagnostics that are always errors or
always warnings, but it doesn't do the right thing for diagnostics that
could be either, such as -Wnarrowing. The classification of that diagnostic
(or any permerror diagnostic) depends on the state of -fpermissive; for the
particular case of -Wnarrowing it also matters whether a compile-time or
run-time narrowing is being diagnosed.

The problem is that the current implementation insists on recording whether
an enabled diagnostic should be a DK_WARNING or a DK_ERROR, and then, after
popping to the initial state, it overrides it always to that type only. Fix
that up by adding a new internal diagnostic type DK_ANY. This just indicates
that the diagnostic is enabled without mandating exactly what type of
diagnostic it should be. Then the diagnostic can be emitted with whatever
type the frontend asks for.

Incidentally, while making this change, I noticed that classify_diagnostic()
spends some time computing a return value (the old classification kind) that
is not used anywhere. The computed value seems to have some problems, mainly
that it does not take into account `#pragma GCC diagnostic pop' at all, and
so the returned value doesn't seem like it could make sense in many
contexts. Given it would also not be desirable to leak the new internal-only
DK_ANY type to outside callers, I think it would make sense in a subsequent
cleanup patch to remove the return value altogether.

gcc/ChangeLog:

	PR c++/111918
	* diagnostic-core.h (enum diagnostic_t): Add DK_ANY special flag.
	* diagnostic.cc (diagnostic_option_classifier::classify_diagnostic):
	Make use of DK_ANY to indicate a diagnostic was initially enabled.
	(diagnostic_context::diagnostic_enabled): Do not change the type of
	a diagnostic if the saved classification is type DK_ANY.

gcc/testsuite/ChangeLog:

	PR c++/111918
	* g++.dg/cpp0x/Wnarrowing21a.C: New test.
	* g++.dg/cpp0x/Wnarrowing21b.C: New test.
	* g++.dg/cpp0x/Wnarrowing21c.C: New test.
	* g++.dg/cpp0x/Wnarrowing21d.C: New test.
2024-03-19 20:33:27 -04:00

144 lines
5.7 KiB
C++

/* Declarations of core diagnostic functionality for code that does
not need to deal with diagnostic contexts or diagnostic info
structures.
Copyright (C) 1998-2024 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_DIAGNOSTIC_CORE_H
#define GCC_DIAGNOSTIC_CORE_H
#include "bversion.h"
/* Constants used to discriminate diagnostics. */
typedef enum
{
#define DEFINE_DIAGNOSTIC_KIND(K, msgid, C) K,
#include "diagnostic.def"
#undef DEFINE_DIAGNOSTIC_KIND
DK_LAST_DIAGNOSTIC_KIND,
/* This is used for tagging pragma pops in the diagnostic
classification history chain. */
DK_POP,
/* This is used internally to note that a diagnostic is enabled
without mandating any specific type. */
DK_ANY,
} diagnostic_t;
/* RAII-style class for grouping related diagnostics. */
class auto_diagnostic_group
{
public:
auto_diagnostic_group ();
~auto_diagnostic_group ();
};
/* Forward decl. */
class diagnostic_metadata; /* See diagnostic-metadata.h. */
extern const char *progname;
extern const char *trim_filename (const char *);
/* If we haven't already defined a front-end-specific diagnostics
style, use the generic one. */
#ifndef GCC_DIAG_STYLE
#define GCC_DIAG_STYLE __gcc_tdiag__
#endif
/* None of these functions are suitable for ATTRIBUTE_PRINTF, because
each language front end can extend them with its own set of format
specifiers. We must use custom format checks. */
#if (CHECKING_P && GCC_VERSION >= 4001) || GCC_VERSION == BUILDING_GCC_VERSION
#define ATTRIBUTE_GCC_DIAG(m, n) __attribute__ ((__format__ (GCC_DIAG_STYLE, m, n))) ATTRIBUTE_NONNULL(m)
#else
#define ATTRIBUTE_GCC_DIAG(m, n) ATTRIBUTE_NONNULL(m)
#endif
extern void internal_error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2)
ATTRIBUTE_NORETURN;
extern void internal_error_no_backtrace (const char *, ...)
ATTRIBUTE_GCC_DIAG(1,2) ATTRIBUTE_NORETURN;
/* Pass one of the OPT_W* from options.h as the first parameter. */
extern bool warning (int, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
extern bool warning_n (location_t, int, unsigned HOST_WIDE_INT,
const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(4,6) ATTRIBUTE_GCC_DIAG(5,6);
extern bool warning_n (rich_location *, int, unsigned HOST_WIDE_INT,
const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(4, 6) ATTRIBUTE_GCC_DIAG(5, 6);
extern bool warning_at (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool warning_at (rich_location *, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool warning_meta (rich_location *,
const diagnostic_metadata &, int,
const char *, ...)
ATTRIBUTE_GCC_DIAG(4,5);
extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
const char *, ...)
ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void error_at (rich_location *, const char *, ...)
ATTRIBUTE_GCC_DIAG(2,3);
extern void error_meta (rich_location *, const diagnostic_metadata &,
const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
ATTRIBUTE_NORETURN;
/* Pass one of the OPT_W* from options.h as the second parameter. */
extern bool pedwarn (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool pedwarn (rich_location *, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
extern bool permerror (rich_location *, const char *,
...) ATTRIBUTE_GCC_DIAG(2,3);
extern bool permerror_opt (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool permerror_opt (rich_location *, int, const char *,
...) ATTRIBUTE_GCC_DIAG(3,4);
extern void sorry (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void sorry_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void inform (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void inform (rich_location *, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void inform_n (location_t, unsigned HOST_WIDE_INT, const char *,
const char *, ...)
ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern bool emit_diagnostic (diagnostic_t, location_t, int,
const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
extern bool emit_diagnostic (diagnostic_t, rich_location *, int,
const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *,
va_list *) ATTRIBUTE_GCC_DIAG (4,0);
extern bool emit_diagnostic_valist_meta (diagnostic_t,
rich_location *,
const diagnostic_metadata *,
int,
const char *,
va_list *) ATTRIBUTE_GCC_DIAG (5,0);
extern bool seen_error (void);
#ifdef BUFSIZ
/* N.B. Unlike all the others, fnotice is just gettext+fprintf, and
therefore it can have ATTRIBUTE_PRINTF. */
extern void fnotice (FILE *, const char *, ...)
ATTRIBUTE_PRINTF_2;
#endif
#endif /* ! GCC_DIAGNOSTIC_CORE_H */