X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fdiagnostic.c;h=8b70a16ad098e5a58705fd8a827ba95283652fb5;hb=3a37ecec8934dc378bfce06d9ea2325a98159f43;hp=54c2da7437457b8ef15c14b5412cc4defcc24e99;hpb=85790e667775932ee784b9c1636dafcc66ac32d3;p=gcc.git diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index 54c2da74374..8b70a16ad09 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -1,6 +1,5 @@ /* Language-independent diagnostic subroutines for the GNU Compiler Collection - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 - Free Software Foundation, Inc. + Copyright (C) 1999-2015 Free Software Foundation, Inc. Contributed by Gabriel Dos Reis This file is part of GCC. @@ -24,55 +23,54 @@ along with GCC; see the file COPYING3. If not see message module. */ #include "config.h" -#undef FLOAT /* This is for hpux. They should change hpux. */ -#undef FFS /* Some systems define this in param.h. */ #include "system.h" #include "coretypes.h" -#include "tm.h" -#include "tree.h" #include "version.h" -#include "tm_p.h" -#include "flags.h" +#include "demangle.h" #include "input.h" -#include "toplev.h" #include "intl.h" +#include "backtrace.h" #include "diagnostic.h" -#include "langhooks.h" -#include "langhooks-def.h" -#include "opts.h" +#include "diagnostic-color.h" -#define pedantic_warning_kind() (flag_pedantic_errors ? DK_ERROR : DK_WARNING) -#define permissive_error_kind() (flag_permissive ? DK_WARNING : DK_ERROR) +#ifdef HAVE_TERMIOS_H +# include +#endif -/* Prototypes. */ -static char *build_message_string (const char *, ...) ATTRIBUTE_PRINTF_1; +#ifdef GWINSZ_IN_SYS_IOCTL +# include +#endif + +#include // For placement new. -static void default_diagnostic_starter (diagnostic_context *, - diagnostic_info *); -static void default_diagnostic_finalizer (diagnostic_context *, - diagnostic_info *); +#define pedantic_warning_kind(DC) \ + ((DC)->pedantic_errors ? DK_ERROR : DK_WARNING) +#define permissive_error_kind(DC) ((DC)->permissive ? DK_WARNING : DK_ERROR) +#define permissive_error_option(DC) ((DC)->opt_permissive) +/* Prototypes. */ static void error_recursion (diagnostic_context *) ATTRIBUTE_NORETURN; -static void diagnostic_action_after_output (diagnostic_context *, - diagnostic_info *); static void real_abort (void) ATTRIBUTE_NORETURN; +/* Name of program invoked, sans directories. */ + +const char *progname; + /* A diagnostic_context surrogate for stderr. */ static diagnostic_context global_diagnostic_context; diagnostic_context *global_dc = &global_diagnostic_context; - /* Return a malloc'd string containing MSG formatted a la printf. The caller is responsible for freeing the memory. */ -static char * +char * build_message_string (const char *msg, ...) { char *str; va_list ap; va_start (ap, msg); - vasprintf (&str, msg, ap); + str = xvasprintf (msg, ap); va_end (ap); return str; @@ -80,39 +78,158 @@ build_message_string (const char *msg, ...) /* Same as diagnostic_build_prefix, but only the source FILE is given. */ char * -file_name_as_prefix (const char *f) +file_name_as_prefix (diagnostic_context *context, const char *f) { - return build_message_string ("%s: ", f); + const char *locus_cs + = colorize_start (pp_show_color (context->printer), "locus"); + const char *locus_ce = colorize_stop (pp_show_color (context->printer)); + return build_message_string ("%s%s:%s ", locus_cs, f, locus_ce); } +/* Return the value of the getenv("COLUMNS") as an integer. If the + value is not set to a positive integer, use ioctl to get the + terminal width. If it fails, return INT_MAX. */ +int +get_terminal_width (void) +{ + const char * s = getenv ("COLUMNS"); + if (s != NULL) { + int n = atoi (s); + if (n > 0) + return n; + } + +#ifdef TIOCGWINSZ + struct winsize w; + w.ws_col = 0; + if (ioctl (0, TIOCGWINSZ, &w) == 0 && w.ws_col > 0) + return w.ws_col; +#endif + + return INT_MAX; +} + +/* Set caret_max_width to value. */ +void +diagnostic_set_caret_max_width (diagnostic_context *context, int value) +{ + /* One minus to account for the leading empty space. */ + value = value ? value - 1 + : (isatty (fileno (pp_buffer (context->printer)->stream)) + ? get_terminal_width () - 1: INT_MAX); + + if (value <= 0) + value = INT_MAX; + + context->caret_max_width = value; +} + /* Initialize the diagnostic message outputting machinery. */ void -diagnostic_initialize (diagnostic_context *context) +diagnostic_initialize (diagnostic_context *context, int n_opts) { + int i; + /* Allocate a basic pretty-printer. Clients will replace this a much more elaborated pretty-printer if they wish. */ context->printer = XNEW (pretty_printer); - pp_construct (context->printer, NULL, 0); - /* By default, diagnostics are sent to stderr. */ - context->printer->buffer->stream = stderr; - /* By default, we emit prefixes once per message. */ - context->printer->wrapping.rule = DIAGNOSTICS_SHOW_PREFIX_ONCE; + new (context->printer) pretty_printer (); memset (context->diagnostic_count, 0, sizeof context->diagnostic_count); - context->issue_warnings_are_errors_message = true; + context->some_warnings_are_errors = false; context->warning_as_error_requested = false; - memset (context->classify_diagnostic, DK_UNSPECIFIED, - sizeof context->classify_diagnostic); + context->n_opts = n_opts; + context->classify_diagnostic = XNEWVEC (diagnostic_t, n_opts); + for (i = 0; i < n_opts; i++) + context->classify_diagnostic[i] = DK_UNSPECIFIED; + context->show_caret = false; + diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer)); + for (i = 0; i < MAX_LOCATIONS_PER_MESSAGE; i++) + context->caret_chars[i] = '^'; context->show_option_requested = false; context->abort_on_error = false; + context->show_column = false; + context->pedantic_errors = false; + context->permissive = false; + context->opt_permissive = 0; + context->fatal_errors = false; + context->dc_inhibit_warnings = false; + context->dc_warn_system_headers = false; + context->max_errors = 0; context->internal_error = NULL; diagnostic_starter (context) = default_diagnostic_starter; diagnostic_finalizer (context) = default_diagnostic_finalizer; + context->option_enabled = NULL; + context->option_state = NULL; + context->option_name = NULL; + context->last_location = UNKNOWN_LOCATION; context->last_module = 0; - context->last_function = NULL; + context->x_data = NULL; context->lock = 0; + context->inhibit_notes_p = false; +} + +/* Maybe initialize the color support. We require clients to do this + explicitly, since most clients don't want color. When called + without a VALUE, it initializes with DIAGNOSTICS_COLOR_DEFAULT. */ + +void +diagnostic_color_init (diagnostic_context *context, int value /*= -1 */) +{ + /* value == -1 is the default value. */ + if (value < 0) + { + /* If DIAGNOSTICS_COLOR_DEFAULT is -1, default to + -fdiagnostics-color=auto if GCC_COLORS is in the environment, + otherwise default to -fdiagnostics-color=never, for other + values default to that + -fdiagnostics-color={never,auto,always}. */ + if (DIAGNOSTICS_COLOR_DEFAULT == -1) + { + if (!getenv ("GCC_COLORS")) + return; + value = DIAGNOSTICS_COLOR_AUTO; + } + else + value = DIAGNOSTICS_COLOR_DEFAULT; + } + pp_show_color (context->printer) + = colorize_init ((diagnostic_color_rule_t) value); +} + +/* Do any cleaning up required after the last diagnostic is emitted. */ + +void +diagnostic_finish (diagnostic_context *context) +{ + /* Some of the errors may actually have been warnings. */ + if (context->some_warnings_are_errors) + { + /* -Werror was given. */ + if (context->warning_as_error_requested) + pp_verbatim (context->printer, + _("%s: all warnings being treated as errors"), + progname); + /* At least one -Werror= was given. */ + else + pp_verbatim (context->printer, + _("%s: some warnings being treated as errors"), + progname); + pp_newline_and_flush (context->printer); + } + + diagnostic_file_cache_fini (); + + XDELETEVEC (context->classify_diagnostic); + context->classify_diagnostic = NULL; + + /* diagnostic_initialize allocates context->printer using XNEW + and placement-new. */ + context->printer->~pretty_printer (); + XDELETE (context->printer); + context->printer = NULL; } /* Initialize DIAGNOSTIC, where the message MSG has already been @@ -125,7 +242,10 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg, diagnostic->message.err_no = errno; diagnostic->message.args_ptr = args; diagnostic->message.format_spec = msg; - diagnostic->location = location; + diagnostic->message.set_location (0, location); + for (int i = 1; i < MAX_LOCATIONS_PER_MESSAGE; i++) + diagnostic->message.set_location (i, UNKNOWN_LOCATION); + diagnostic->override_column = 0; diagnostic->kind = kind; diagnostic->option_index = 0; } @@ -143,33 +263,280 @@ diagnostic_set_info (diagnostic_info *diagnostic, const char *gmsgid, /* Return a malloc'd string describing a location. The caller is responsible for freeing the memory. */ char * -diagnostic_build_prefix (diagnostic_info *diagnostic) +diagnostic_build_prefix (diagnostic_context *context, + const diagnostic_info *diagnostic) { static const char *const diagnostic_kind_text[] = { -#define DEFINE_DIAGNOSTIC_KIND(K, T) (T), +#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T), #include "diagnostic.def" #undef DEFINE_DIAGNOSTIC_KIND "must-not-happen" }; - const char *text = _(diagnostic_kind_text[diagnostic->kind]); - expanded_location s = expand_location (diagnostic->location); + static const char *const diagnostic_kind_color[] = { +#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C), +#include "diagnostic.def" +#undef DEFINE_DIAGNOSTIC_KIND + NULL + }; gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND); + const char *text = _(diagnostic_kind_text[diagnostic->kind]); + const char *text_cs = "", *text_ce = ""; + const char *locus_cs, *locus_ce; + pretty_printer *pp = context->printer; + + if (diagnostic_kind_color[diagnostic->kind]) + { + text_cs = colorize_start (pp_show_color (pp), + diagnostic_kind_color[diagnostic->kind]); + text_ce = colorize_stop (pp_show_color (pp)); + } + locus_cs = colorize_start (pp_show_color (pp), "locus"); + locus_ce = colorize_stop (pp_show_color (pp)); + + expanded_location s = diagnostic_expand_location (diagnostic); return (s.file == NULL - ? build_message_string ("%s: %s", progname, text) - : flag_show_column && s.column != 0 - ? build_message_string ("%s:%d:%d: %s", s.file, s.line, s.column, text) - : build_message_string ("%s:%d: %s", s.file, s.line, text)); + ? build_message_string ("%s%s:%s %s%s%s", locus_cs, progname, locus_ce, + text_cs, text, text_ce) + : !strcmp (s.file, N_("")) + ? build_message_string ("%s%s:%s %s%s%s", locus_cs, s.file, locus_ce, + text_cs, text, text_ce) + : context->show_column + ? build_message_string ("%s%s:%d:%d:%s %s%s%s", locus_cs, s.file, s.line, + s.column, locus_ce, text_cs, text, text_ce) + : build_message_string ("%s%s:%d:%s %s%s%s", locus_cs, s.file, s.line, + locus_ce, text_cs, text, text_ce)); +} + +/* If LINE is longer than MAX_WIDTH, and COLUMN is not smaller than + MAX_WIDTH by some margin, then adjust the start of the line such + that the COLUMN is smaller than MAX_WIDTH minus the margin. The + margin is either CARET_LINE_MARGIN characters or the difference + between the column and the length of the line, whatever is smaller. + The length of LINE is given by LINE_WIDTH. */ +static const char * +adjust_line (const char *line, int line_width, + int max_width, int *column_p) +{ + int right_margin = CARET_LINE_MARGIN; + int column = *column_p; + + gcc_checking_assert (line_width >= column); + right_margin = MIN (line_width - column, right_margin); + right_margin = max_width - right_margin; + if (line_width >= max_width && column > right_margin) + { + line += column - right_margin; + *column_p = right_margin; + } + return line; +} + +/* Print the physical source line corresponding to the location of + this diagnostic, and a caret indicating the precise column. This + function only prints two caret characters if the two locations + given by DIAGNOSTIC are on the same line according to + diagnostic_same_line(). */ +void +diagnostic_show_locus (diagnostic_context * context, + const diagnostic_info *diagnostic) +{ + if (!context->show_caret + || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION + || diagnostic_location (diagnostic, 0) == context->last_location) + return; + + context->last_location = diagnostic_location (diagnostic, 0); + expanded_location s0 = diagnostic_expand_location (diagnostic, 0); + expanded_location s1 = { }; + /* Zero-initialized. This is checked later by diagnostic_print_caret_line. */ + + if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION) + s1 = diagnostic_expand_location (diagnostic, 1); + + diagnostic_print_caret_line (context, s0, s1, + context->caret_chars[0], + context->caret_chars[1]); +} + +/* Print (part) of the source line given by xloc1 with caret1 pointing + at the column. If xloc2.column != 0 and it fits within the same + line as xloc1 according to diagnostic_same_line (), then caret2 is + printed at xloc2.colum. Otherwise, the caller has to set up things + to print a second caret line for xloc2. */ +void +diagnostic_print_caret_line (diagnostic_context * context, + expanded_location xloc1, + expanded_location xloc2, + char caret1, char caret2) +{ + if (!diagnostic_same_line (context, xloc1, xloc2)) + /* This will mean ignore xloc2. */ + xloc2.column = 0; + else if (xloc1.column == xloc2.column) + xloc2.column++; + + int cmax = MAX (xloc1.column, xloc2.column); + int line_width; + const char *line = location_get_source_line (xloc1, &line_width); + if (line == NULL || cmax > line_width) + return; + + /* Center the interesting part of the source line to fit in + max_width, and adjust all columns accordingly. */ + int max_width = context->caret_max_width; + int offset = (int) cmax; + line = adjust_line (line, line_width, max_width, &offset); + offset -= cmax; + cmax += offset; + xloc1.column += offset; + if (xloc2.column) + xloc2.column += offset; + + /* Print the source line. */ + pp_newline (context->printer); + const char *saved_prefix = pp_get_prefix (context->printer); + pp_set_prefix (context->printer, NULL); + pp_space (context->printer); + while (max_width > 0 && line_width > 0) + { + char c = *line == '\t' ? ' ' : *line; + if (c == '\0') + c = ' '; + pp_character (context->printer, c); + max_width--; + line_width--; + line++; + } + pp_newline (context->printer); + + /* Print the caret under the line. */ + const char *caret_cs, *caret_ce; + caret_cs = colorize_start (pp_show_color (context->printer), "caret"); + caret_ce = colorize_stop (pp_show_color (context->printer)); + int cmin = xloc2.column + ? MIN (xloc1.column, xloc2.column) : xloc1.column; + int caret_min = cmin == xloc1.column ? caret1 : caret2; + int caret_max = cmin == xloc1.column ? caret2 : caret1; + + /* cmin is >= 1, but we indent with an extra space at the start like + we did above. */ + int i; + for (i = 0; i < cmin; i++) + pp_space (context->printer); + pp_printf (context->printer, "%s%c%s", caret_cs, caret_min, caret_ce); + + if (xloc2.column) + { + for (i++; i < cmax; i++) + pp_space (context->printer); + pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce); + } + pp_set_prefix (context->printer, saved_prefix); + pp_needs_newline (context->printer) = true; +} + +/* Functions at which to stop the backtrace print. It's not + particularly helpful to print the callers of these functions. */ + +static const char * const bt_stop[] = +{ + "main", + "toplev::main", + "execute_one_pass", + "compile_file", +}; + +/* A callback function passed to the backtrace_full function. */ + +static int +bt_callback (void *data, uintptr_t pc, const char *filename, int lineno, + const char *function) +{ + int *pcount = (int *) data; + + /* If we don't have any useful information, don't print + anything. */ + if (filename == NULL && function == NULL) + return 0; + + /* Skip functions in diagnostic.c. */ + if (*pcount == 0 + && filename != NULL + && strcmp (lbasename (filename), "diagnostic.c") == 0) + return 0; + + /* Print up to 20 functions. We could make this a --param, but + since this is only for debugging just use a constant for now. */ + if (*pcount >= 20) + { + /* Returning a non-zero value stops the backtrace. */ + return 1; + } + ++*pcount; + + char *alc = NULL; + if (function != NULL) + { + char *str = cplus_demangle_v3 (function, + (DMGL_VERBOSE | DMGL_ANSI + | DMGL_GNU_V3 | DMGL_PARAMS)); + if (str != NULL) + { + alc = str; + function = str; + } + + for (size_t i = 0; i < ARRAY_SIZE (bt_stop); ++i) + { + size_t len = strlen (bt_stop[i]); + if (strncmp (function, bt_stop[i], len) == 0 + && (function[len] == '\0' || function[len] == '(')) + { + if (alc != NULL) + free (alc); + /* Returning a non-zero value stops the backtrace. */ + return 1; + } + } + } + + fprintf (stderr, "0x%lx %s\n\t%s:%d\n", + (unsigned long) pc, + function == NULL ? "???" : function, + filename == NULL ? "???" : filename, + lineno); + + if (alc != NULL) + free (alc); + + return 0; +} + +/* A callback function passed to the backtrace_full function. This is + called if backtrace_full has an error. */ + +static void +bt_err_callback (void *data ATTRIBUTE_UNUSED, const char *msg, int errnum) +{ + if (errnum < 0) + { + /* This means that no debug info was available. Just quietly + skip printing backtrace info. */ + return; + } + fprintf (stderr, "%s%s%s\n", msg, errnum == 0 ? "" : ": ", + errnum == 0 ? "" : xstrerror (errnum)); } /* Take any action which is expected to happen after the diagnostic is written out. This function does not always return. */ -static void +void diagnostic_action_after_output (diagnostic_context *context, - diagnostic_info *diagnostic) + diagnostic_t diag_kind) { - switch (diagnostic->kind) + switch (diag_kind) { case DK_DEBUG: case DK_NOTE: @@ -181,26 +548,55 @@ diagnostic_action_after_output (diagnostic_context *context, case DK_SORRY: if (context->abort_on_error) real_abort (); - if (flag_fatal_errors) + if (context->fatal_errors) { fnotice (stderr, "compilation terminated due to -Wfatal-errors.\n"); + diagnostic_finish (context); + exit (FATAL_EXIT_CODE); + } + if (context->max_errors != 0 + && ((unsigned) (diagnostic_kind_count (context, DK_ERROR) + + diagnostic_kind_count (context, DK_SORRY) + + diagnostic_kind_count (context, DK_WERROR)) + >= context->max_errors)) + { + fnotice (stderr, + "compilation terminated due to -fmax-errors=%u.\n", + context->max_errors); + diagnostic_finish (context); exit (FATAL_EXIT_CODE); } break; case DK_ICE: - if (context->abort_on_error) - real_abort (); - - fnotice (stderr, "Please submit a full bug report,\n" - "with preprocessed source if appropriate.\n" - "See %s for instructions.\n", bug_report_url); - exit (ICE_EXIT_CODE); + case DK_ICE_NOBT: + { + struct backtrace_state *state = NULL; + if (diag_kind == DK_ICE) + state = backtrace_create_state (NULL, 0, bt_err_callback, NULL); + int count = 0; + if (state != NULL) + backtrace_full (state, 2, bt_callback, bt_err_callback, + (void *) &count); + + if (context->abort_on_error) + real_abort (); + + fnotice (stderr, "Please submit a full bug report,\n" + "with preprocessed source if appropriate.\n"); + if (count > 0) + fnotice (stderr, + ("Please include the complete backtrace " + "with any bug report.\n")); + fnotice (stderr, "See %s for instructions.\n", bug_report_url); + + exit (ICE_EXIT_CODE); + } case DK_FATAL: if (context->abort_on_error) real_abort (); - + diagnostic_finish (context); fnotice (stderr, "compilation terminated.\n"); exit (FATAL_EXIT_CODE); @@ -209,20 +605,10 @@ diagnostic_action_after_output (diagnostic_context *context, } } -/* Prints out, if necessary, the name of the current function - that caused an error. Called from all error and warning functions. */ void -diagnostic_report_current_function (diagnostic_context *context, - diagnostic_info *diagnostic) +diagnostic_report_current_module (diagnostic_context *context, location_t where) { - diagnostic_report_current_module (context); - lang_hooks.print_error_function (context, input_filename, diagnostic); -} - -void -diagnostic_report_current_module (diagnostic_context *context) -{ - const struct line_map *map; + const line_map_ordinary *map = NULL; if (pp_needs_newline (context->printer)) { @@ -230,25 +616,34 @@ diagnostic_report_current_module (diagnostic_context *context) pp_needs_newline (context->printer) = false; } - if (input_location <= BUILTINS_LOCATION) + if (where <= BUILTINS_LOCATION) return; - map = linemap_lookup (line_table, input_location); + linemap_resolve_location (line_table, where, + LRK_MACRO_DEFINITION_LOCATION, + &map); + if (map && diagnostic_last_module_changed (context, map)) { diagnostic_set_last_module (context, map); if (! MAIN_FILE_P (map)) { map = INCLUDED_FROM (line_table, map); - pp_verbatim (context->printer, - "In file included from %s:%d", - map->to_file, LAST_SOURCE_LINE (map)); + if (context->show_column) + pp_verbatim (context->printer, + "In file included from %r%s:%d:%d%R", "locus", + LINEMAP_FILE (map), + LAST_SOURCE_LINE (map), LAST_SOURCE_COLUMN (map)); + else + pp_verbatim (context->printer, + "In file included from %r%s:%d%R", "locus", + LINEMAP_FILE (map), LAST_SOURCE_LINE (map)); while (! MAIN_FILE_P (map)) { map = INCLUDED_FROM (line_table, map); pp_verbatim (context->printer, - ",\n from %s:%d", - map->to_file, LAST_SOURCE_LINE (map)); + ",\n from %r%s:%d%R", "locus", + LINEMAP_FILE (map), LAST_SOURCE_LINE (map)); } pp_verbatim (context->printer, ":"); pp_newline (context->printer); @@ -256,72 +651,150 @@ diagnostic_report_current_module (diagnostic_context *context) } } -static void +void default_diagnostic_starter (diagnostic_context *context, diagnostic_info *diagnostic) { - diagnostic_report_current_function (context, diagnostic); - pp_set_prefix (context->printer, diagnostic_build_prefix (diagnostic)); + diagnostic_report_current_module (context, diagnostic_location (diagnostic)); + pp_set_prefix (context->printer, diagnostic_build_prefix (context, + diagnostic)); } -static void +void default_diagnostic_finalizer (diagnostic_context *context, - diagnostic_info *diagnostic ATTRIBUTE_UNUSED) + diagnostic_info *diagnostic) { + diagnostic_show_locus (context, diagnostic); pp_destroy_prefix (context->printer); + pp_newline_and_flush (context->printer); } /* Interface to specify diagnostic kind overrides. Returns the previous setting, or DK_UNSPECIFIED if the parameters are out of - range. */ + range. If OPTION_INDEX is zero, the new setting is for all the + diagnostics. */ diagnostic_t diagnostic_classify_diagnostic (diagnostic_context *context, int option_index, - diagnostic_t new_kind) + diagnostic_t new_kind, + location_t where) { diagnostic_t old_kind; - if (option_index <= 0 - || option_index >= N_OPTS + if (option_index < 0 + || option_index >= context->n_opts || new_kind >= DK_LAST_DIAGNOSTIC_KIND) return DK_UNSPECIFIED; old_kind = context->classify_diagnostic[option_index]; - context->classify_diagnostic[option_index] = new_kind; + + /* Handle pragmas separately, since we need to keep track of *where* + the pragmas were. */ + if (where != UNKNOWN_LOCATION) + { + int i; + + /* Record the command-line status, so we can reset it back on DK_POP. */ + if (old_kind == DK_UNSPECIFIED) + { + old_kind = context->option_enabled (option_index, + context->option_state) + ? DK_WARNING : DK_IGNORED; + context->classify_diagnostic[option_index] = old_kind; + } + + for (i = context->n_classification_history - 1; i >= 0; i --) + if (context->classification_history[i].option == option_index) + { + old_kind = context->classification_history[i].kind; + break; + } + + i = context->n_classification_history; + context->classification_history = + (diagnostic_classification_change_t *) xrealloc (context->classification_history, (i + 1) + * sizeof (diagnostic_classification_change_t)); + context->classification_history[i].location = where; + context->classification_history[i].option = option_index; + context->classification_history[i].kind = new_kind; + context->n_classification_history ++; + } + else + context->classify_diagnostic[option_index] = new_kind; + return old_kind; } +/* Save all diagnostic classifications in a stack. */ +void +diagnostic_push_diagnostics (diagnostic_context *context, location_t where ATTRIBUTE_UNUSED) +{ + context->push_list = (int *) xrealloc (context->push_list, (context->n_push + 1) * sizeof (int)); + context->push_list[context->n_push ++] = context->n_classification_history; +} + +/* Restore the topmost classification set off the stack. If the stack + is empty, revert to the state based on command line parameters. */ +void +diagnostic_pop_diagnostics (diagnostic_context *context, location_t where) +{ + int jump_to; + int i; + + if (context->n_push) + jump_to = context->push_list [-- context->n_push]; + else + jump_to = 0; + + i = context->n_classification_history; + context->classification_history = + (diagnostic_classification_change_t *) xrealloc (context->classification_history, (i + 1) + * sizeof (diagnostic_classification_change_t)); + context->classification_history[i].location = where; + context->classification_history[i].option = jump_to; + context->classification_history[i].kind = DK_POP; + context->n_classification_history ++; +} + /* Report a diagnostic message (an error or a warning) as specified by DC. This function is *the* subroutine in terms of which front-ends should implement their specific diagnostic handling modules. The front-end independent format specifiers are exactly those described - in the documentation of output_format. + in the documentation of output_format. Return true if a diagnostic was printed, false otherwise. */ bool diagnostic_report_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic) { - location_t location = diagnostic->location; - bool maybe_print_warnings_as_errors_message = false; + location_t location = diagnostic_location (diagnostic); + diagnostic_t orig_diag_kind = diagnostic->kind; const char *saved_format_spec; /* Give preference to being able to inhibit warnings, before they get reclassified to something else. */ if ((diagnostic->kind == DK_WARNING || diagnostic->kind == DK_PEDWARN) - && !diagnostic_report_warnings_p (location)) + && !diagnostic_report_warnings_p (context, location)) + return false; + + if (diagnostic->kind == DK_PEDWARN) + { + diagnostic->kind = pedantic_warning_kind (context); + /* We do this to avoid giving the message for -pedantic-errors. */ + orig_diag_kind = diagnostic->kind; + } + + if (diagnostic->kind == DK_NOTE && context->inhibit_notes_p) return false; - if (diagnostic->kind == DK_PEDWARN) - diagnostic->kind = pedantic_warning_kind (); - if (context->lock > 0) { /* If we're reporting an ICE in the middle of some other error, try to flush out the previous error, then let this one through. Don't do this more than once. */ - if (diagnostic->kind == DK_ICE && context->lock == 1) - pp_flush (context->printer); + if ((diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT) + && context->lock == 1) + pp_newline_and_flush (context->printer); else error_recursion (context); } @@ -334,21 +807,53 @@ diagnostic_report_diagnostic (diagnostic_context *context, && diagnostic->kind == DK_WARNING) { diagnostic->kind = DK_ERROR; - maybe_print_warnings_as_errors_message = true; } - - if (diagnostic->option_index) + + if (diagnostic->option_index + && diagnostic->option_index != permissive_error_option (context)) { + diagnostic_t diag_class = DK_UNSPECIFIED; + /* This tests if the user provided the appropriate -Wfoo or -Wno-foo option. */ - if (! option_enabled (diagnostic->option_index)) + if (! context->option_enabled (diagnostic->option_index, + context->option_state)) return false; + + /* This tests for #pragma diagnostic changes. */ + if (context->n_classification_history > 0) + { + /* FIXME: Stupid search. Optimize later. */ + for (int i = context->n_classification_history - 1; i >= 0; i --) + { + if (linemap_location_before_p + (line_table, + context->classification_history[i].location, + location)) + { + if (context->classification_history[i].kind == (int) DK_POP) + { + i = context->classification_history[i].option; + continue; + } + int option = context->classification_history[i].option; + /* The option 0 is for all the diagnostics. */ + if (option == 0 || option == diagnostic->option_index) + { + diag_class = context->classification_history[i].kind; + if (diag_class != DK_UNSPECIFIED) + diagnostic->kind = diag_class; + break; + } + } + } + } /* This tests if the user provided the appropriate -Werror=foo option. */ - if (context->classify_diagnostic[diagnostic->option_index] != DK_UNSPECIFIED) + if (diag_class == DK_UNSPECIFIED + && context->classify_diagnostic[diagnostic->option_index] != DK_UNSPECIFIED) { diagnostic->kind = context->classify_diagnostic[diagnostic->option_index]; - maybe_print_warnings_as_errors_message = false; } /* This allows for future extensions, like temporarily disabling warnings for ranges of source code. */ @@ -356,19 +861,12 @@ diagnostic_report_diagnostic (diagnostic_context *context, return false; } - /* If we changed the kind due to -Werror, and didn't override it, we - need to print this message. */ - if (context->issue_warnings_are_errors_message - && maybe_print_warnings_as_errors_message) - { - pp_verbatim (context->printer, - "%s: warnings being treated as errors\n", progname); - context->issue_warnings_are_errors_message = false; - } + if (orig_diag_kind == DK_WARNING && diagnostic->kind == DK_ERROR) + context->some_warnings_are_errors = true; context->lock++; - if (diagnostic->kind == DK_ICE) + if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT) { #ifndef ENABLE_CHECKING /* When not checking, ICEs are converted to fatal errors when an @@ -378,35 +876,50 @@ diagnostic_report_diagnostic (diagnostic_context *context, || diagnostic_kind_count (context, DK_SORRY) > 0) && !context->abort_on_error) { - expanded_location s = expand_location (diagnostic->location); + expanded_location s + = expand_location (diagnostic_location (diagnostic)); fnotice (stderr, "%s:%d: confused by earlier errors, bailing out\n", s.file, s.line); exit (ICE_EXIT_CODE); } #endif if (context->internal_error) - (*context->internal_error) (diagnostic->message.format_spec, + (*context->internal_error) (context, + diagnostic->message.format_spec, diagnostic->message.args_ptr); } - ++diagnostic_kind_count (context, diagnostic->kind); - + if (diagnostic->kind == DK_ERROR && orig_diag_kind == DK_WARNING) + ++diagnostic_kind_count (context, DK_WERROR); + else + ++diagnostic_kind_count (context, diagnostic->kind); + saved_format_spec = diagnostic->message.format_spec; - if (context->show_option_requested && diagnostic->option_index) - diagnostic->message.format_spec - = ACONCAT ((diagnostic->message.format_spec, - " [", cl_options[diagnostic->option_index].opt_text, "]", NULL)); - - diagnostic->message.locus = &diagnostic->location; - diagnostic->message.abstract_origin = &diagnostic->abstract_origin; - diagnostic->abstract_origin = NULL; + if (context->show_option_requested) + { + char *option_text; + + option_text = context->option_name (context, diagnostic->option_index, + orig_diag_kind, diagnostic->kind); + + if (option_text) + { + diagnostic->message.format_spec + = ACONCAT ((diagnostic->message.format_spec, + " ", + "[", option_text, "]", + NULL)); + free (option_text); + } + } + diagnostic->message.x_data = &diagnostic->x_data; + diagnostic->x_data = NULL; pp_format (context->printer, &diagnostic->message); (*diagnostic_starter (context)) (context, diagnostic); pp_output_formatted_text (context->printer); (*diagnostic_finalizer (context)) (context, diagnostic); - pp_flush (context->printer); - diagnostic_action_after_output (context, diagnostic); + diagnostic_action_after_output (context, diagnostic->kind); diagnostic->message.format_spec = saved_format_spec; - diagnostic->abstract_origin = NULL; + diagnostic->x_data = NULL; context->lock--; @@ -458,66 +971,115 @@ verbatim (const char *gmsgid, ...) text.err_no = errno; text.args_ptr = ≈ text.format_spec = _(gmsgid); - text.locus = NULL; - text.abstract_origin = NULL; + text.x_data = NULL; pp_format_verbatim (global_dc->printer, &text); - pp_flush (global_dc->printer); + pp_newline_and_flush (global_dc->printer); + va_end (ap); +} + +/* Add a note with text GMSGID and with LOCATION to the diagnostic CONTEXT. */ +void +diagnostic_append_note (diagnostic_context *context, + location_t location, + const char * gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + const char *saved_prefix; + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE); + if (context->inhibit_notes_p) + { + va_end (ap); + return; + } + saved_prefix = pp_get_prefix (context->printer); + pp_set_prefix (context->printer, + diagnostic_build_prefix (context, &diagnostic)); + pp_newline (context->printer); + pp_format (context->printer, &diagnostic.message); + pp_output_formatted_text (context->printer); + pp_destroy_prefix (context->printer); + pp_set_prefix (context->printer, saved_prefix); + diagnostic_show_locus (context, &diagnostic); va_end (ap); } bool -emit_diagnostic (diagnostic_t kind, location_t location, int opt, +emit_diagnostic (diagnostic_t kind, location_t location, int opt, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + bool ret; va_start (ap, gmsgid); if (kind == DK_PERMERROR) { diagnostic_set_info (&diagnostic, gmsgid, &ap, location, - permissive_error_kind ()); - diagnostic.option_index = OPT_fpermissive; + permissive_error_kind (global_dc)); + diagnostic.option_index = permissive_error_option (global_dc); } else { diagnostic_set_info (&diagnostic, gmsgid, &ap, location, kind); if (kind == DK_WARNING || kind == DK_PEDWARN) diagnostic.option_index = opt; } - va_end (ap); - return report_diagnostic (&diagnostic); + ret = report_diagnostic (&diagnostic); + va_end (ap); + return ret; } -/* An informative note. Use this for additional details on an error +/* An informative note at LOCATION. Use this for additional details on an error message. */ void -inform (const char *gmsgid, ...) +inform (location_t location, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_NOTE); + diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE); + report_diagnostic (&diagnostic); + va_end (ap); +} + +/* An informative note at LOCATION. Use this for additional details on an + error message. */ +void +inform_n (location_t location, int n, const char *singular_gmsgid, + const char *plural_gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + + va_start (ap, plural_gmsgid); + diagnostic_set_info_translated (&diagnostic, + ngettext (singular_gmsgid, plural_gmsgid, n), + &ap, location, DK_NOTE); report_diagnostic (&diagnostic); va_end (ap); } /* A warning at INPUT_LOCATION. Use this for code which is correct according - to the relevant language specification but is likely to be buggy anyway. + to the relevant language specification but is likely to be buggy anyway. Returns true if the warning was printed, false if it was inhibited. */ bool warning (int opt, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + bool ret; va_start (ap, gmsgid); diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING); diagnostic.option_index = opt; + ret = report_diagnostic (&diagnostic); va_end (ap); - return report_diagnostic (&diagnostic); + return ret; } /* A warning at LOCATION. Use this for code which is correct according to the @@ -529,53 +1091,64 @@ warning_at (location_t location, int opt, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + bool ret; va_start (ap, gmsgid); diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING); diagnostic.option_index = opt; + ret = report_diagnostic (&diagnostic); va_end (ap); - return report_diagnostic (&diagnostic); + return ret; } -/* A "pedantic" warning at LOCATION: issues a warning unless - -pedantic-errors was given on the command line, in which case it - issues an error. Use this for diagnostics required by the relevant - language standard, if you have chosen not to make them errors. - - Note that these diagnostics are issued independent of the setting - of the -pedantic command-line switch. To get a warning enabled - only with that switch, use either "if (pedantic) pedwarn - (OPT_pedantic,...)" or just "pedwarn (OPT_pedantic,..)". To get a - pedwarn independently of the -pedantic switch use "pedwarn (0,...)". - +/* A warning at LOCATION. Use this for code which is correct according to the + relevant language specification but is likely to be buggy anyway. Returns true if the warning was printed, false if it was inhibited. */ bool -pedwarn_at (location_t location, int opt, const char *gmsgid, ...) +warning_n (location_t location, int opt, int n, const char *singular_gmsgid, + const char *plural_gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + bool ret; - va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN); + va_start (ap, plural_gmsgid); + diagnostic_set_info_translated (&diagnostic, + ngettext (singular_gmsgid, plural_gmsgid, n), + &ap, location, DK_WARNING); diagnostic.option_index = opt; + ret = report_diagnostic (&diagnostic); va_end (ap); - return report_diagnostic (&diagnostic); + return ret; } -/* Equivalent to pedwarn_at using INPUT_LOCATION. */ +/* A "pedantic" warning at LOCATION: issues a warning unless + -pedantic-errors was given on the command line, in which case it + issues an error. Use this for diagnostics required by the relevant + language standard, if you have chosen not to make them errors. + + Note that these diagnostics are issued independent of the setting + of the -Wpedantic command-line switch. To get a warning enabled + only with that switch, use either "if (pedantic) pedwarn + (OPT_Wpedantic,...)" or just "pedwarn (OPT_Wpedantic,..)". To get a + pedwarn independently of the -Wpedantic switch use "pedwarn (0,...)". + + Returns true if the warning was printed, false if it was inhibited. */ bool -pedwarn (int opt, const char *gmsgid, ...) +pedwarn (location_t location, int opt, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + bool ret; va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_PEDWARN); + diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN); diagnostic.option_index = opt; + ret = report_diagnostic (&diagnostic); va_end (ap); - return report_diagnostic (&diagnostic); + return ret; } /* A "permissive" error at LOCATION: issues an error unless @@ -586,46 +1159,61 @@ pedwarn (int opt, const char *gmsgid, ...) Returns true if the warning was printed, false if it was inhibited. */ bool -permerror_at (location_t location, const char *gmsgid, ...) +permerror (location_t location, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + bool ret; va_start (ap, gmsgid); diagnostic_set_info (&diagnostic, gmsgid, &ap, location, - permissive_error_kind ()); - diagnostic.option_index = OPT_fpermissive; + permissive_error_kind (global_dc)); + diagnostic.option_index = permissive_error_option (global_dc); + ret = report_diagnostic (&diagnostic); va_end (ap); - return report_diagnostic (&diagnostic); + return ret; } -/* Equivalent to permerror_at (input_location, ...). */ - -bool -permerror (const char *gmsgid, ...) +/* A hard error: the code is definitely ill-formed, and an object file + will not be produced. */ +void +error (const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, - permissive_error_kind ()); - diagnostic.option_index = OPT_fpermissive; + diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR); + report_diagnostic (&diagnostic); va_end (ap); - return report_diagnostic (&diagnostic); } - /* A hard error: the code is definitely ill-formed, and an object file will not be produced. */ void -error (const char *gmsgid, ...) +error_n (location_t location, int n, const char *singular_gmsgid, + const char *plural_gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + + va_start (ap, plural_gmsgid); + diagnostic_set_info_translated (&diagnostic, + ngettext (singular_gmsgid, plural_gmsgid, n), + &ap, location, DK_ERROR); + report_diagnostic (&diagnostic); + va_end (ap); +} + +/* Same as ebove, but use location LOC instead of input_location. */ +void +error_at (location_t loc, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR); + diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_ERROR); report_diagnostic (&diagnostic); va_end (ap); } @@ -645,17 +1233,25 @@ sorry (const char *gmsgid, ...) va_end (ap); } +/* Return true if an error or a "sorry" has been seen. Various + processing is disabled after errors. */ +bool +seen_error (void) +{ + return errorcount || sorrycount; +} + /* An error which is severe enough that we make no attempt to continue. Do not use this for internal consistency checks; that's internal_error. Use of this function should be rare. */ void -fatal_error (const char *gmsgid, ...) +fatal_error (location_t loc, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_FATAL); + diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_FATAL); report_diagnostic (&diagnostic); va_end (ap); @@ -679,6 +1275,23 @@ internal_error (const char *gmsgid, ...) gcc_unreachable (); } + +/* Like internal_error, but no backtrace will be printed. Used when + the internal error does not happen at the current location, but happened + somewhere else. */ +void +internal_error_no_backtrace (const char *gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE_NOBT); + report_diagnostic (&diagnostic); + va_end (ap); + + gcc_unreachable (); +} /* Special case error functions. Most are implemented in terms of the above, or should be. */ @@ -703,18 +1316,15 @@ fnotice (FILE *file, const char *cmsgid, ...) static void error_recursion (diagnostic_context *context) { - diagnostic_info diagnostic; - if (context->lock < 3) - pp_flush (context->printer); + pp_newline_and_flush (context->printer); fnotice (stderr, "Internal compiler error: Error reporting routines re-entered.\n"); /* Call diagnostic_action_after_output to get the "please submit a bug - report" message. It only looks at the kind field of diagnostic_info. */ - diagnostic.kind = DK_ICE; - diagnostic_action_after_output (context, &diagnostic); + report" message. */ + diagnostic_action_after_output (context, DK_ICE); /* Do not use gcc_unreachable here; that goes through internal_error and therefore would cause infinite recursion. */