blob: 4aa2bef2c0fd740763b67ee371734143056b3f32 [file] [log] [blame]
/* Preprocess only, using cpplib.
Copyright (C) 1995-2023 Free Software Foundation, Inc.
Written by Per Bothner, 1994-95.
This program 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.
This program 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 this program; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "c-common.h" /* For flags. */
#include "../libcpp/internal.h"
#include "langhooks.h"
#include "c-pragma.h" /* For parse_in. */
#include "file-prefix-map.h" /* remap_macro_filename() */
class token_streamer;
/* Encapsulates state used to convert a stream of tokens into a text
file. */
static struct
{
FILE *outf; /* Stream to write to. */
const cpp_token *prev; /* Previous token. */
const cpp_token *source; /* Source token for spacing. */
unsigned src_line; /* Line number currently being written. */
bool printed; /* True if something output at line. */
bool first_time; /* pp_file_change hasn't been called yet. */
bool prev_was_system_token; /* True if the previous token was a
system token.*/
const char *src_file; /* Current source file. */
token_streamer *streamer; /* Instance of class token_streamer using this
object. */
} print;
/* Defined and undefined macros being queued for output with -dU at
the next newline. */
struct macro_queue
{
struct macro_queue *next; /* Next macro in the list. */
char *macro; /* The name of the macro if not
defined, the full definition if
defined. */
};
static macro_queue *define_queue, *undef_queue;
/* General output routines. */
static void scan_translation_unit (cpp_reader *);
static void scan_translation_unit_directives_only (cpp_reader *);
static void scan_translation_unit_trad (cpp_reader *);
static void account_for_newlines (const unsigned char *, size_t);
static int dump_macro (cpp_reader *, cpp_hashnode *, void *);
static void dump_queued_macros (cpp_reader *);
static bool print_line_1 (location_t, const char*, FILE *);
static bool print_line (location_t, const char *);
static bool maybe_print_line_1 (location_t, FILE *);
static bool maybe_print_line (location_t);
static bool do_line_change (cpp_reader *, const cpp_token *,
location_t, int);
/* Callback routines for the parser. Most of these are active only
in specific modes. */
static void cb_line_change (cpp_reader *, const cpp_token *, int);
static void cb_define (cpp_reader *, location_t, cpp_hashnode *);
static void cb_undef (cpp_reader *, location_t, cpp_hashnode *);
static void cb_used_define (cpp_reader *, location_t, cpp_hashnode *);
static void cb_used_undef (cpp_reader *, location_t, cpp_hashnode *);
static void cb_include (cpp_reader *, location_t, const unsigned char *,
const char *, int, const cpp_token **);
static void cb_ident (cpp_reader *, location_t, const cpp_string *);
static void cb_def_pragma (cpp_reader *, location_t);
static void cb_read_pch (cpp_reader *pfile, const char *name,
int fd, const char *orig_name);
/* Preprocess and output. */
void
preprocess_file (cpp_reader *pfile)
{
/* A successful cpp_read_main_file guarantees that we can call
cpp_scan_nooutput or cpp_get_token next. */
if (flag_no_output && pfile->buffer)
{
/* Scan -included buffers, then the main file. */
while (pfile->buffer->prev)
cpp_scan_nooutput (pfile);
cpp_scan_nooutput (pfile);
}
else if (cpp_get_options (pfile)->traditional)
scan_translation_unit_trad (pfile);
else if (cpp_get_options (pfile)->directives_only
&& !cpp_get_options (pfile)->preprocessed)
scan_translation_unit_directives_only (pfile);
else
scan_translation_unit (pfile);
/* -dM command line option. Should this be elsewhere? */
if (flag_dump_macros == 'M')
cpp_forall_identifiers (pfile, dump_macro, NULL);
/* Flush any pending output. */
if (print.printed)
putc ('\n', print.outf);
}
/* Don't emit #pragma or #ident directives if we are processing
assembly language; the assembler may choke on them. */
static bool
should_output_pragmas ()
{
return cpp_get_options (parse_in)->lang != CLK_ASM;
}
/* Set up the callbacks as appropriate. */
void
init_pp_output (FILE *out_stream)
{
cpp_callbacks *cb = cpp_get_callbacks (parse_in);
if (!flag_no_output)
{
cb->line_change = cb_line_change;
if (should_output_pragmas ())
{
cb->ident = cb_ident;
cb->def_pragma = cb_def_pragma;
}
}
if (flag_dump_includes)
cb->include = cb_include;
if (flag_pch_preprocess)
{
cb->valid_pch = c_common_valid_pch;
cb->read_pch = cb_read_pch;
}
if (flag_dump_macros == 'N' || flag_dump_macros == 'D')
{
cb->define = cb_define;
cb->undef = cb_undef;
}
if (flag_dump_macros == 'U')
{
cb->before_define = dump_queued_macros;
cb->used_define = cb_used_define;
cb->used_undef = cb_used_undef;
}
cb->has_attribute = c_common_has_attribute;
cb->has_builtin = c_common_has_builtin;
cb->get_source_date_epoch = cb_get_source_date_epoch;
cb->remap_filename = remap_macro_filename;
/* Initialize the print structure. */
print.src_line = 1;
print.printed = false;
print.prev = 0;
print.outf = out_stream;
print.first_time = 1;
print.src_file = "";
print.prev_was_system_token = false;
print.streamer = nullptr;
}
// FIXME: Ideally we'd just turn the entirety of the print struct into
// an encapsulated streamer ...
class token_streamer
{
bool avoid_paste;
bool do_line_adjustments;
bool in_pragma;
public:
token_streamer (cpp_reader *pfile)
:avoid_paste (false),
do_line_adjustments (cpp_get_options (pfile)->lang != CLK_ASM
&& !flag_no_line_commands),
in_pragma (false)
{
gcc_assert (!print.streamer);
print.streamer = this;
}
void begin_pragma ()
{
in_pragma = true;
}
void stream (cpp_reader *pfile, const cpp_token *tok, location_t);
};
void
token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
location_t loc)
{
/* Keep input_location up to date, since it is needed for processing early
pragmas such as #pragma GCC diagnostic. */
input_location = loc;
if (token->type == CPP_PADDING)
{
avoid_paste = true;
if (print.source == NULL
|| (!(print.source->flags & PREV_WHITE)
&& token->val.source == NULL))
print.source = token->val.source;
return;
}
if (token->type == CPP_EOF)
return;
/* Keep track when we move into and out of system locations. */
const bool is_system_token = in_system_header_at (loc);
const bool system_state_changed
= (is_system_token != print.prev_was_system_token);
print.prev_was_system_token = is_system_token;
/* Subtle logic to output a space if and only if necessary. */
bool line_marker_emitted = false;
if (avoid_paste)
{
unsigned src_line = LOCATION_LINE (loc);
if (print.source == NULL)
print.source = token;
if (src_line != print.src_line
&& do_line_adjustments
&& !in_pragma)
{
line_marker_emitted = do_line_change (pfile, token, loc, false);
putc (' ', print.outf);
print.printed = true;
}
else if (print.source->flags & PREV_WHITE
|| (print.prev
&& cpp_avoid_paste (pfile, print.prev, token))
|| (print.prev == NULL && token->type == CPP_HASH))
{
putc (' ', print.outf);
print.printed = true;
}
}
else if (token->flags & PREV_WHITE && token->type != CPP_PRAGMA)
{
unsigned src_line = LOCATION_LINE (loc);
if (src_line != print.src_line
&& do_line_adjustments
&& !in_pragma)
line_marker_emitted = do_line_change (pfile, token, loc, false);
putc (' ', print.outf);
print.printed = true;
}
avoid_paste = false;
print.source = NULL;
print.prev = token;
if (token->type == CPP_PRAGMA)
{
in_pragma = true;
if (should_output_pragmas ())
{
const char *space;
const char *name;
line_marker_emitted = maybe_print_line (token->src_loc);
fputs ("#pragma ", print.outf);
c_pp_lookup_pragma (token->val.pragma, &space, &name);
if (space)
fprintf (print.outf, "%s %s", space, name);
else
fprintf (print.outf, "%s", name);
print.printed = true;
}
if (token->val.pragma >= PRAGMA_FIRST_EXTERNAL)
c_pp_invoke_early_pragma_handler (token->val.pragma);
}
else if (token->type == CPP_PRAGMA_EOL)
{
if (should_output_pragmas ())
maybe_print_line (UNKNOWN_LOCATION);
in_pragma = false;
}
else
{
if (cpp_get_options (parse_in)->debug)
linemap_dump_location (line_table, token->src_loc, print.outf);
if (do_line_adjustments
&& !in_pragma
&& !line_marker_emitted
&& system_state_changed
&& !is_location_from_builtin_token (loc))
/* The system-ness of this token is different from the one of
the previous token. Let's emit a line change to mark the
new system-ness before we emit the token. */
{
line_marker_emitted = do_line_change (pfile, token, loc, false);
}
if (!in_pragma || should_output_pragmas ())
{
cpp_output_token (token, print.outf);
print.printed = true;
}
}
/* CPP_COMMENT tokens and raw-string literal tokens can have
embedded new-line characters. Rather than enumerating all the
possible token types just check if token uses val.str union
member. */
if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR)
account_for_newlines (token->val.str.text, token->val.str.len);
}
/* Writes out the preprocessed file, handling spacing and paste
avoidance issues. */
static void
scan_translation_unit (cpp_reader *pfile)
{
token_streamer streamer (pfile);
uintptr_t filter = 0;
if (lang_hooks.preprocess_token)
filter = lang_hooks.preprocess_token (pfile, NULL, filter);
print.source = NULL;
for (;;)
{
location_t spelling_loc;
const cpp_token *token
= cpp_get_token_with_location (pfile, &spelling_loc);
streamer.stream (pfile, token, spelling_loc);
if (filter)
{
unsigned flags = lang_hooks.preprocess_token (pfile, token, filter);
if (flags & lang_hooks::PT_begin_pragma)
streamer.begin_pragma ();
}
if (token->type == CPP_EOF)
break;
}
if (filter)
lang_hooks.preprocess_token (pfile, NULL, filter);
}
class do_streamer : public token_streamer
{
public:
uintptr_t filter;
do_streamer (cpp_reader *pfile, uintptr_t filter)
:token_streamer (pfile), filter (filter)
{
}
};
static void
directives_only_cb (cpp_reader *pfile, CPP_DO_task task, void *data_, ...)
{
va_list args;
va_start (args, data_);
do_streamer *streamer = reinterpret_cast <do_streamer *> (data_);
switch (task)
{
default:
gcc_unreachable ();
case CPP_DO_print:
{
print.src_line += va_arg (args, unsigned);
const void *buf = va_arg (args, const void *);
size_t size = va_arg (args, size_t);
fwrite (buf, 1, size, print.outf);
}
break;
case CPP_DO_location:
maybe_print_line (va_arg (args, location_t));
break;
case CPP_DO_token:
{
const cpp_token *token = va_arg (args, const cpp_token *);
location_t spelling_loc = va_arg (args, location_t);
streamer->stream (pfile, token, spelling_loc);
if (streamer->filter)
{
unsigned flags = lang_hooks.preprocess_token
(pfile, token, streamer->filter);
if (flags & lang_hooks::PT_begin_pragma)
streamer->begin_pragma ();
}
}
break;
}
va_end (args);
}
/* Writes out the preprocessed file, handling spacing and paste
avoidance issues. */
static void
scan_translation_unit_directives_only (cpp_reader *pfile)
{
uintptr_t filter = 0;
if (lang_hooks.preprocess_token)
filter = lang_hooks.preprocess_token (pfile, NULL, filter);
do_streamer streamer (pfile, filter);
cpp_directive_only_process (pfile, &streamer, directives_only_cb);
if (streamer.filter)
lang_hooks.preprocess_token (pfile, NULL, streamer.filter);
}
/* Adjust print.src_line for newlines embedded in output. For example, if a raw
string literal contains newlines, then we need to increment our notion of the
current line to keep in sync and avoid outputting a line marker
unnecessarily. If a raw string literal containing newlines is the result of
macro expansion, then we have the opposite problem, where the token takes up
more lines in the output than it did in the input, and hence a line marker is
needed to restore the correct state for subsequent lines. In this case,
incrementing print.src_line still does the job, because it will cause us to
emit the line marker the next time a token is streamed. */
static void
account_for_newlines (const unsigned char *str, size_t len)
{
while (len--)
if (*str++ == '\n')
print.src_line++;
}
/* Writes out a traditionally preprocessed file. */
static void
scan_translation_unit_trad (cpp_reader *pfile)
{
while (_cpp_read_logical_line_trad (pfile))
{
size_t len = pfile->out.cur - pfile->out.base;
maybe_print_line (pfile->out.first_line);
fwrite (pfile->out.base, 1, len, print.outf);
print.printed = true;
if (!CPP_OPTION (pfile, discard_comments))
account_for_newlines (pfile->out.base, len);
}
}
/* If the token read on logical line LINE needs to be output on a
different line to the current one, output the required newlines or
a line marker. If a line marker was emitted, return TRUE otherwise
return FALSE. */
static bool
maybe_print_line_1 (location_t src_loc, FILE *stream)
{
bool emitted_line_marker = false;
unsigned src_line = LOCATION_LINE (src_loc);
const char *src_file = LOCATION_FILE (src_loc);
/* End the previous line of text. */
if (print.printed)
{
putc ('\n', stream);
print.src_line++;
print.printed = false;
}
if (!flag_no_line_commands
&& src_line >= print.src_line
&& src_line < print.src_line + 8
&& src_loc != UNKNOWN_LOCATION
&& strcmp (src_file, print.src_file) == 0)
{
while (src_line > print.src_line)
{
putc ('\n', stream);
print.src_line++;
}
}
else
emitted_line_marker = print_line_1 (src_loc, "", stream);
return emitted_line_marker;
}
/* If the token read on logical line LINE needs to be output on a
different line to the current one, output the required newlines or
a line marker. If a line marker was emitted, return TRUE otherwise
return FALSE. */
static bool
maybe_print_line (location_t src_loc)
{
if (cpp_get_options (parse_in)->debug)
linemap_dump_location (line_table, src_loc,
print.outf);
return maybe_print_line_1 (src_loc, print.outf);
}
/* Output a line marker for logical line LINE. Special flags are "1"
or "2" indicating entering or leaving a file. If the line marker
was effectively emitted, return TRUE otherwise return FALSE. */
static bool
print_line_1 (location_t src_loc, const char *special_flags, FILE *stream)
{
bool emitted_line_marker = false;
/* End any previous line of text. */
if (print.printed)
putc ('\n', stream);
print.printed = false;
if (src_loc != UNKNOWN_LOCATION && !flag_no_line_commands)
{
const char *file_path = LOCATION_FILE (src_loc);
size_t to_file_len = strlen (file_path);
unsigned char *to_file_quoted =
(unsigned char *) alloca (to_file_len * 4 + 1);
/* cpp_quote_string does not nul-terminate, so we have to do it
ourselves. */
unsigned char *p = cpp_quote_string (to_file_quoted,
(const unsigned char *) file_path,
to_file_len);
*p = '\0';
print.src_line = LOCATION_LINE (src_loc);
print.src_file = file_path;
fprintf (stream, "# %u \"%s\"%s",
print.src_line, to_file_quoted, special_flags);
int sysp = in_system_header_at (src_loc);
if (sysp == 2)
fputs (" 3 4", stream);
else if (sysp == 1)
fputs (" 3", stream);
putc ('\n', stream);
emitted_line_marker = true;
}
return emitted_line_marker;
}
/* Output a line marker for logical line LINE. Special flags are "1"
or "2" indicating entering or leaving a file. Return TRUE if a
line marker was effectively emitted, FALSE otherwise. */
static bool
print_line (location_t src_loc, const char *special_flags)
{
if (cpp_get_options (parse_in)->debug)
linemap_dump_location (line_table, src_loc,
print.outf);
return print_line_1 (src_loc, special_flags, print.outf);
}
/* Helper function for cb_line_change and scan_translation_unit.
Return TRUE if a line marker is emitted, FALSE otherwise. */
static bool
do_line_change (cpp_reader *pfile, const cpp_token *token,
location_t src_loc, int parsing_args)
{
bool emitted_line_marker = false;
if (define_queue || undef_queue)
dump_queued_macros (pfile);
if (token->type == CPP_EOF || parsing_args)
return false;
emitted_line_marker = maybe_print_line (src_loc);
print.prev = 0;
print.source = 0;
/* Supply enough spaces to put this token in its original column,
one space per column greater than 2, since scan_translation_unit
will provide a space if PREV_WHITE. Don't bother trying to
reconstruct tabs; we can't get it right in general, and nothing
ought to care. Some things do care; the fault lies with them.
Also do not output the spaces if this is a CPP_PRAGMA token. In this
case, libcpp has provided the location of the first token after #pragma,
so we would start at the wrong column. */
if (!CPP_OPTION (pfile, traditional) && token->type != CPP_PRAGMA)
{
int spaces = LOCATION_COLUMN (src_loc) - 2;
print.printed = true;
while (-- spaces >= 0)
putc (' ', print.outf);
}
return emitted_line_marker;
}
/* Called when a line of output is started. TOKEN is the first token
of the line, and at end of file will be CPP_EOF. */
static void
cb_line_change (cpp_reader *pfile, const cpp_token *token,
int parsing_args)
{
do_line_change (pfile, token, token->src_loc, parsing_args);
}
static void
cb_ident (cpp_reader *pfile ATTRIBUTE_UNUSED, location_t line,
const cpp_string *str)
{
maybe_print_line (line);
fprintf (print.outf, "#ident %s\n", str->text);
print.src_line++;
}
static void
cb_define (cpp_reader *pfile, location_t line, cpp_hashnode *node)
{
const line_map_ordinary *map;
maybe_print_line (line);
fputs ("#define ", print.outf);
/* 'D' is whole definition; 'N' is name only. */
if (flag_dump_macros == 'D')
fputs ((const char *) cpp_macro_definition (pfile, node),
print.outf);
else
fputs ((const char *) NODE_NAME (node), print.outf);
putc ('\n', print.outf);
print.printed = false;
linemap_resolve_location (line_table, line,
LRK_MACRO_DEFINITION_LOCATION,
&map);
print.src_line++;
}
static void
cb_undef (cpp_reader *pfile, location_t line, cpp_hashnode *node)
{
if (lang_hooks.preprocess_undef)
lang_hooks.preprocess_undef (pfile, line, node);
maybe_print_line (line);
fprintf (print.outf, "#undef %s\n", NODE_NAME (node));
print.src_line++;
}
static void
cb_used_define (cpp_reader *pfile, location_t line ATTRIBUTE_UNUSED,
cpp_hashnode *node)
{
if (cpp_user_macro_p (node))
{
macro_queue *q;
q = XNEW (macro_queue);
q->macro = xstrdup ((const char *) cpp_macro_definition (pfile, node));
q->next = define_queue;
define_queue = q;
}
}
static void
cb_used_undef (cpp_reader *pfile ATTRIBUTE_UNUSED,
location_t line ATTRIBUTE_UNUSED,
cpp_hashnode *node)
{
macro_queue *q;
q = XNEW (macro_queue);
q->macro = xstrdup ((const char *) NODE_NAME (node));
q->next = undef_queue;
undef_queue = q;
}
static void
dump_queued_macros (cpp_reader *pfile ATTRIBUTE_UNUSED)
{
macro_queue *q;
/* End the previous line of text. */
if (print.printed)
{
putc ('\n', print.outf);
print.src_line++;
print.printed = false;
}
for (q = define_queue; q;)
{
macro_queue *oq;
fputs ("#define ", print.outf);
fputs (q->macro, print.outf);
putc ('\n', print.outf);
print.printed = false;
print.src_line++;
oq = q;
q = q->next;
free (oq->macro);
free (oq);
}
define_queue = NULL;
for (q = undef_queue; q;)
{
macro_queue *oq;
fprintf (print.outf, "#undef %s\n", q->macro);
print.src_line++;
oq = q;
q = q->next;
free (oq->macro);
free (oq);
}
undef_queue = NULL;
}
static void
cb_include (cpp_reader *pfile ATTRIBUTE_UNUSED, location_t line,
const unsigned char *dir, const char *header, int angle_brackets,
const cpp_token **comments)
{
maybe_print_line (line);
if (angle_brackets)
fprintf (print.outf, "#%s <%s>", dir, header);
else
fprintf (print.outf, "#%s \"%s\"", dir, header);
if (comments != NULL)
{
while (*comments != NULL)
{
if ((*comments)->flags & PREV_WHITE)
putc (' ', print.outf);
cpp_output_token (*comments, print.outf);
++comments;
}
}
putc ('\n', print.outf);
print.printed = false;
print.src_line++;
}
/* Callback called when -fworking-director and -E to emit working
directory in cpp output file. */
void
pp_dir_change (cpp_reader *pfile ATTRIBUTE_UNUSED, const char *dir)
{
size_t to_file_len = strlen (dir);
unsigned char *to_file_quoted =
(unsigned char *) alloca (to_file_len * 4 + 1);
unsigned char *p;
/* cpp_quote_string does not nul-terminate, so we have to do it ourselves. */
p = cpp_quote_string (to_file_quoted, (const unsigned char *) dir, to_file_len);
*p = '\0';
fprintf (print.outf, "# 1 \"%s//\"\n", to_file_quoted);
}
/* The file name, line number or system header flags have changed, as
described in MAP. */
void
pp_file_change (const line_map_ordinary *map)
{
const char *flags = "";
if (flag_no_line_commands)
return;
if (map != NULL)
{
input_location = map->start_location;
if (print.first_time)
{
/* Avoid printing foo.i when the main file is foo.c. */
if (!cpp_get_options (parse_in)->preprocessed)
print_line (map->start_location, flags);
print.first_time = 0;
}
else
{
/* Bring current file to correct line when entering a new file. */
if (map->reason == LC_ENTER)
{
maybe_print_line (linemap_included_from (map));
flags = " 1";
}
else if (map->reason == LC_LEAVE)
flags = " 2";
print_line (map->start_location, flags);
}
}
}
/* Copy a #pragma directive to the preprocessed output. */
static void
cb_def_pragma (cpp_reader *pfile, location_t line)
{
maybe_print_line (line);
fputs ("#pragma ", print.outf);
cpp_output_line (pfile, print.outf);
print.printed = false;
print.src_line++;
}
/* Stream a token as if we had seen it directly ourselves; needed
in case a token was lexed externally, e.g. while processing a
pragma. */
void
c_pp_stream_token (cpp_reader *pfile, const cpp_token *tok, location_t loc)
{
gcc_assert (print.streamer);
print.streamer->stream (pfile, tok, loc);
}
/* Dump out the hash table. */
static int
dump_macro (cpp_reader *pfile, cpp_hashnode *node, void *v ATTRIBUTE_UNUSED)
{
if (cpp_user_macro_p (node))
{
fputs ("#define ", print.outf);
fputs ((const char *) cpp_macro_definition (pfile, node),
print.outf);
putc ('\n', print.outf);
print.printed = false;
print.src_line++;
}
return 1;
}
/* Load in the PCH file NAME, open on FD. It was originally searched for
by ORIG_NAME. Also, print out a #include command so that the PCH
file can be loaded when the preprocessed output is compiled. */
static void
cb_read_pch (cpp_reader *pfile, const char *name,
int fd, const char *orig_name ATTRIBUTE_UNUSED)
{
c_common_read_pch (pfile, name, fd, orig_name);
fprintf (print.outf, "#pragma GCC pch_preprocess \"%s\"\n", name);
print.src_line++;
}