mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
486 lines
12 KiB
C++
486 lines
12 KiB
C++
/* Implementation detail of pp_format.
|
|
Copyright (C) 2002-2026 Free Software Foundation, Inc.
|
|
Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
|
|
|
|
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_PRETTY_PRINT_FORMAT_IMPL_H
|
|
#define GCC_PRETTY_PRINT_FORMAT_IMPL_H
|
|
|
|
#include "pretty-print.h"
|
|
#include "diagnostics/event-id.h"
|
|
|
|
/* A struct representing a pending item to be printed within
|
|
pp_format.
|
|
|
|
These can represent:
|
|
- a run of text within one of the output_buffers's obstacks
|
|
- begin/end named color
|
|
- open/close quote
|
|
- begin/end URL
|
|
- event IDs
|
|
- custom data (for the formatter, for the pretty_printer,
|
|
or the output format)
|
|
|
|
These are built into pp_token_list instances.
|
|
|
|
Doing so allows for interaction between:
|
|
|
|
- pretty_printer formatting codes (such as C++'s %H and %I,
|
|
which can't be printed until we've seen both)
|
|
|
|
- output formats, such as text vs SARIF (so each can handle URLs
|
|
and event IDs it its own way)
|
|
|
|
- optimization records, where we want to stash data into the
|
|
formatted messages
|
|
|
|
- urlifiers: these can be run in phase 3 of formatting
|
|
|
|
without needing lots of fragile logic on char pointers.
|
|
|
|
To avoid needing lots of heap allocation/deallocation, pp_token
|
|
instances are allocated in the pretty_printer's chunk_obstack:
|
|
they must not outlive phase 3 of formatting of the given
|
|
pp_formatted_chunks level. */
|
|
|
|
struct pp_token
|
|
{
|
|
public:
|
|
enum class kind
|
|
{
|
|
text,
|
|
|
|
begin_color,
|
|
end_color,
|
|
|
|
begin_quote,
|
|
end_quote,
|
|
|
|
begin_url,
|
|
end_url,
|
|
|
|
event_id,
|
|
|
|
custom_data,
|
|
|
|
NUM_KINDS
|
|
};
|
|
|
|
pp_token (enum kind k);
|
|
|
|
pp_token (const pp_token &) = delete;
|
|
pp_token (pp_token &&) = delete;
|
|
|
|
virtual ~pp_token () = default;
|
|
|
|
pp_token &operator= (const pp_token &) = delete;
|
|
pp_token &operator= (pp_token &&) = delete;
|
|
|
|
void dump (FILE *out) const;
|
|
void DEBUG_FUNCTION dump () const { dump (stderr); }
|
|
|
|
static void *operator new (size_t sz, obstack &s);
|
|
static void operator delete (void *);
|
|
|
|
enum kind m_kind;
|
|
|
|
// Intrusive doubly-linked list
|
|
pp_token *m_prev;
|
|
pp_token *m_next;
|
|
};
|
|
|
|
/* Subclasses of pp_token for the various kinds of token. */
|
|
|
|
struct pp_token_text : public pp_token
|
|
{
|
|
pp_token_text (label_text &&value)
|
|
: pp_token (kind::text),
|
|
m_value (std::move (value))
|
|
{
|
|
gcc_assert (m_value.get ());
|
|
}
|
|
|
|
label_text m_value;
|
|
};
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <pp_token_text *>::test (pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::text;
|
|
}
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <const pp_token_text *>::test (const pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::text;
|
|
}
|
|
|
|
struct pp_token_begin_color : public pp_token
|
|
{
|
|
pp_token_begin_color (label_text &&value)
|
|
: pp_token (kind::begin_color),
|
|
m_value (std::move (value))
|
|
{
|
|
gcc_assert (m_value.get ());
|
|
}
|
|
|
|
label_text m_value;
|
|
};
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <pp_token_begin_color *>::test (pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::begin_color;
|
|
}
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <const pp_token_begin_color *>::test (const pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::begin_color;
|
|
}
|
|
|
|
struct pp_token_end_color : public pp_token
|
|
{
|
|
pp_token_end_color ()
|
|
: pp_token (kind::end_color)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct pp_token_begin_quote : public pp_token
|
|
{
|
|
pp_token_begin_quote ()
|
|
: pp_token (kind::begin_quote)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct pp_token_end_quote : public pp_token
|
|
{
|
|
pp_token_end_quote ()
|
|
: pp_token (kind::end_quote)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct pp_token_begin_url : public pp_token
|
|
{
|
|
pp_token_begin_url (label_text &&value)
|
|
: pp_token (kind::begin_url),
|
|
m_value (std::move (value))
|
|
{
|
|
gcc_assert (m_value.get ());
|
|
}
|
|
|
|
label_text m_value;
|
|
};
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <pp_token_begin_url*>::test (pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::begin_url;
|
|
}
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <const pp_token_begin_url*>::test (const pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::begin_url;
|
|
}
|
|
|
|
struct pp_token_end_url : public pp_token
|
|
{
|
|
pp_token_end_url ()
|
|
: pp_token (kind::end_url)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct pp_token_event_id : public pp_token
|
|
{
|
|
pp_token_event_id (diagnostics::paths::event_id_t event_id)
|
|
: pp_token (kind::event_id),
|
|
m_event_id (event_id)
|
|
{
|
|
gcc_assert (event_id.known_p ());
|
|
}
|
|
|
|
diagnostics::paths::event_id_t m_event_id;
|
|
};
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <pp_token_event_id *>::test (pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::event_id;
|
|
}
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <const pp_token_event_id *>::test (const pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::event_id;
|
|
}
|
|
|
|
struct pp_token_custom_data : public pp_token
|
|
{
|
|
class value
|
|
{
|
|
public:
|
|
virtual ~value () {}
|
|
virtual void dump (FILE *out) const = 0;
|
|
|
|
/* Hook for lowering a custom_data token to standard tokens.
|
|
Return true and write to OUT if possible.
|
|
Return false for custom_data that is to be handled by
|
|
the token_printer. */
|
|
virtual bool as_standard_tokens (pp_token_list &out) = 0;
|
|
};
|
|
|
|
pp_token_custom_data (std::unique_ptr<value> val)
|
|
: pp_token (kind::custom_data),
|
|
m_value (std::move (val))
|
|
{
|
|
gcc_assert (m_value.get ());
|
|
}
|
|
|
|
std::unique_ptr<value> m_value;
|
|
};
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <pp_token_custom_data *>::test (pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::custom_data;
|
|
}
|
|
|
|
template <>
|
|
template <>
|
|
inline bool
|
|
is_a_helper <const pp_token_custom_data *>::test (const pp_token *tok)
|
|
{
|
|
return tok->m_kind == pp_token::kind::custom_data;
|
|
}
|
|
|
|
/* A list of pp_token, with ownership of the tokens, using
|
|
a particular obstack to allocate its tokens. These are
|
|
also allocated on the obstack during formatting (or, occasionally,
|
|
the stack). */
|
|
|
|
class pp_token_list
|
|
{
|
|
public:
|
|
// Allocate a new pp_token_list within S.
|
|
static pp_token_list *make (obstack &s)
|
|
{
|
|
return new (s) pp_token_list (s);
|
|
}
|
|
static void *operator new (size_t sz, obstack &s);
|
|
static void operator delete (void *);
|
|
|
|
pp_token_list (obstack &s);
|
|
pp_token_list (const pp_token_list &) = delete;
|
|
pp_token_list (pp_token_list &&);
|
|
|
|
~pp_token_list ();
|
|
|
|
pp_token &operator= (const pp_token_list &) = delete;
|
|
pp_token &operator= (pp_token_list &&) = delete;
|
|
|
|
/* Make a pp_token of the given subclass, using the relevant obstack to provide
|
|
the memory. The pp_token must therefore not outlive the current
|
|
pp_formatted_chunks level during formatting. */
|
|
template<typename Subclass, typename... Args>
|
|
std::unique_ptr<pp_token>
|
|
make_token (Args&&... args)
|
|
{
|
|
return std::unique_ptr<pp_token>
|
|
(new (m_obstack) Subclass (std::forward<Args> (args)...));
|
|
}
|
|
|
|
template<typename Subclass, typename... Args>
|
|
void push_back (Args&&... args)
|
|
{
|
|
auto tok = make_token<Subclass> (std::forward<Args> (args)...);
|
|
push_back (std::move (tok));
|
|
}
|
|
void push_back_text (label_text &&text);
|
|
void push_back_byte (char ch);
|
|
void push_back (std::unique_ptr<pp_token> tok);
|
|
void push_back_list (pp_token_list &&list);
|
|
|
|
std::unique_ptr<pp_token> pop_front ();
|
|
|
|
std::unique_ptr<pp_token> remove_token (pp_token *tok);
|
|
|
|
void insert_after (std::unique_ptr<pp_token> new_tok,
|
|
pp_token *relative_tok);
|
|
|
|
void replace_custom_tokens ();
|
|
void merge_consecutive_text_tokens ();
|
|
void apply_urlifier (const urlifier &urlifier);
|
|
|
|
void dump (FILE *out) const;
|
|
void DEBUG_FUNCTION dump () const { dump (stderr); }
|
|
|
|
obstack &m_obstack;
|
|
|
|
pp_token *m_first;
|
|
pp_token *m_end;
|
|
};
|
|
|
|
/* The pp_formatted_chunks data structure forms a stack of the results from the
|
|
first phase of formatting (pp_format) which have not yet been
|
|
output (pp_output_formatted_text). A stack is necessary because
|
|
the diagnostic starter may decide to generate its own output by way
|
|
of the formatter. */
|
|
class pp_formatted_chunks
|
|
{
|
|
friend class pretty_printer;
|
|
friend class pp_markup::context;
|
|
friend class output_buffer;
|
|
|
|
public:
|
|
pp_token_list * const * get_token_lists () const { return m_args; }
|
|
|
|
void append_formatted_chunk (obstack &s, const char *content);
|
|
|
|
void dump (FILE *out, int indent) const;
|
|
void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
|
|
|
|
// For use in selftests
|
|
pp_formatted_chunks *get_prev () const { return m_prev; }
|
|
|
|
private:
|
|
/* Pointer to previous level on the stack. */
|
|
pp_formatted_chunks *m_prev;
|
|
|
|
/* Array of chunks to output. Each chunk is a doubly-linked list of
|
|
pp_token.
|
|
|
|
The chunks can be printed via pp_formatted_chunks::dump ().
|
|
|
|
In the first phase of formatting, even-numbered chunks are
|
|
to be output verbatim, odd-numbered chunks are format specifiers.
|
|
For example, given:
|
|
pp_format (pp,
|
|
"foo: %i, bar: %s, opt: %qs",
|
|
42, "baz", "-foption");
|
|
|
|
after phase 1 we might have:
|
|
(gdb) call buffer->cur_chunk_array->dump()
|
|
0: [TEXT("foo: ")]
|
|
1: [TEXT("i")]
|
|
2: [TEXT(", bar: ")]
|
|
3: [TEXT("s")]
|
|
4: [TEXT(", opt: ")]
|
|
5: [TEXT("qs")]
|
|
|
|
The second phase replaces all odd-numbered chunks with formatted
|
|
token lists. In the above example, after phase 2 we might have:
|
|
(gdb) call pp->m_buffer->cur_chunk_array->dump()
|
|
0: [TEXT("foo: ")]
|
|
1: [TEXT("42")]
|
|
2: [TEXT(", bar: ")]
|
|
3: [TEXT("baz")]
|
|
4: [TEXT(", opt: ")]
|
|
5: [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
|
|
For example the %qs has become the three tokens:
|
|
[BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
|
|
|
|
The third phase (in pp_output_formatted_text):
|
|
|
|
(1) merges the tokens from all the chunks into one list,
|
|
giving e.g.
|
|
(gdb) call tokens.dump()
|
|
[TEXT("foo: "), TEXT("42"), TEXT(", bar: "), TEXT("baz"),
|
|
TEXT(", opt: "), BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
|
|
|
|
(2) lowers some custom tokens into non-custom tokens
|
|
|
|
(3) merges consecutive text tokens, giving e.g.:
|
|
(gdb) call tokens.dump()
|
|
[TEXT("foo: 42, bar: baz, option: "),
|
|
BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
|
|
|
|
(4) if provided with a urlifier, tries to apply it to quoted text,
|
|
giving e.g:
|
|
(gdb) call tokens.dump()
|
|
[TEXT("foo: 42, bar: baz, option: "), BEGIN_QUOTE,
|
|
BEGIN_URL("http://example.com"), TEXT("-foption"), END_URL, END_QUOTE]
|
|
|
|
(5) emits all tokens in sequence with appropriate line-wrapping. This
|
|
can be overridded via the pretty_printer's token_printer, allowing for
|
|
output formats to e.g. override how URLs are handled, or to handle
|
|
custom_data that wasn't lowered in (2) above, e.g. for handling JSON
|
|
output of optimization records. */
|
|
pp_token_list *m_args[PP_NL_ARGMAX * 2];
|
|
|
|
/* The pp_tokens, pp_token_lists, and the accumulated text buffers are
|
|
allocated within the output_buffer's chunk_obstack. In the above
|
|
example, the in-memory layout of the chunk_obstack might look like
|
|
this after phase 1:
|
|
|
|
+ pp_formatted_chunks instance <--- START of pp_formatted_chunks level
|
|
|
|
|
+ pp_token_list for chunk 0 (m_first: *)
|
|
| |
|
|
+ "foo: \0" <-------------\ |
|
|
| | |
|
|
+ pp_token_text (borrowed: *) <-------/
|
|
|
|
|
+ pp_token_list for chunk 1
|
|
|
|
|
+ "i\0" <------------------\
|
|
| |
|
|
+ pp_token_text (borrowed: *)
|
|
|
|
|
+ ...etc for chunks 2 to 4...
|
|
|
|
|
+ pp_token_list for chunk 5
|
|
|
|
|
+ "qs\0" <-----------------\
|
|
| |
|
|
+ pp_token_text (borrowed: *)
|
|
|
|
|
|
|
|
V
|
|
obstack grows this way
|
|
|
|
At each stage, allocation of additional text buffers, tokens, and lists
|
|
grow forwards in the obstack (though the internal pointers in linked
|
|
lists might point backwards to earlier objects within the same
|
|
pp_formatted_chunks level). */
|
|
};
|
|
|
|
#endif /* GCC_PRETTY_PRINT_FORMAT_IMPL_H */
|