vec.o input.o hash-table.o ggc-none.o memory-block.o \
selftest.o selftest-diagnostic.o sort.o \
selftest-diagnostic-path.o \
+ selftest-json.o \
selftest-logical-location.o \
text-art/box-drawing.o \
text-art/canvas.o \
#include "ordered-hash-map.h"
#include "sbitmap.h"
#include "make-unique.h"
+#include "selftest.h"
+#include "selftest-diagnostic.h"
+#include "selftest-diagnostic-show-locus.h"
+#include "selftest-json.h"
+#include "text-range-label.h"
/* Forward decls. */
class sarif_builder;
+class content_renderer;
+ class escape_nonascii_renderer;
/* Subclasses of sarif_object.
Keep these in order of their descriptions in the specification. */
sarif_builder &builder);
};
+/* Abstract base class for use when making an "artifactContent"
+ object (SARIF v2.1.0 section 3.3): generate a value for the
+ 3.3.4 "rendered" property.
+ Can return nullptr, for "no property". */
+
+class content_renderer
+{
+public:
+ virtual ~content_renderer () {}
+
+ virtual std::unique_ptr<sarif_multiformat_message_string>
+ render (const sarif_builder &builder) const = 0;
+};
+
/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
and -fdiagnostics-format=sarif-file).
property (SARIF v2.1.0 section 3.14.11), as invocation objects
(SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
toplev::main, and the response files.
- - doesn't capture escape_on_output_p
- doesn't capture secondary locations within a rich_location
(perhaps we should use the "relatedLocations" property: SARIF v2.1.0
section 3.27.22)
std::unique_ptr<sarif_physical_location>
maybe_make_physical_location_object (location_t loc,
enum diagnostic_artifact_role role,
- int column_override);
+ int column_override,
+ const content_renderer *snippet_renderer);
std::unique_ptr<sarif_artifact_location>
make_artifact_location_object (location_t loc);
std::unique_ptr<sarif_artifact_location>
maybe_make_region_object (location_t loc,
int column_override) const;
std::unique_ptr<sarif_region>
- maybe_make_region_object_for_context (location_t loc) const;
+ maybe_make_region_object_for_context (location_t loc,
+ const content_renderer *snippet_renderer) const;
std::unique_ptr<sarif_region>
make_region_object_for_hint (const fixit_hint &hint) const;
std::unique_ptr<sarif_multiformat_message_string>
make_run_object (std::unique_ptr<sarif_invocation> invocation_obj,
std::unique_ptr<json::array> results);
std::unique_ptr<sarif_tool>
- make_tool_object () const;
+ make_tool_object ();
std::unique_ptr<sarif_tool_component>
- make_driver_tool_component_object () const;
+ make_driver_tool_component_object ();
std::unique_ptr<json::array> maybe_make_taxonomies_array () const;
std::unique_ptr<sarif_tool_component>
maybe_make_cwe_taxonomy_object () const;
std::unique_ptr<sarif_artifact_content>
maybe_make_artifact_content_object (const char *filename,
int start_line,
- int end_line) const;
+ int end_line,
+ const content_renderer *r) const;
std::unique_ptr<sarif_fix>
make_fix_object (const rich_location &rich_loc);
std::unique_ptr<sarif_artifact_change>
bool m_seen_any_relative_paths;
hash_set <free_string_hash> m_rule_id_set;
- json::array *m_rules_arr;
+ std::unique_ptr<json::array> m_rules_arr;
/* The set of all CWE IDs we've seen, if any. */
hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
const logical_location *logical_loc,
enum diagnostic_artifact_role role)
{
+ class escape_nonascii_renderer : public content_renderer
+ {
+ public:
+ escape_nonascii_renderer (const rich_location &richloc,
+ enum diagnostics_escape_format escape_format)
+ : m_richloc (richloc),
+ m_escape_format (escape_format)
+ {}
+
+ std::unique_ptr<sarif_multiformat_message_string>
+ render (const sarif_builder &builder) const final override
+ {
+ diagnostic_context dc;
+ diagnostic_initialize (&dc, 0);
+ dc.m_source_printing.enabled = true;
+ dc.m_source_printing.colorize_source_p = false;
+ dc.m_source_printing.show_labels_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+
+ rich_location my_rich_loc (m_richloc);
+ my_rich_loc.set_escape_on_output (true);
+
+ dc.set_escape_format (m_escape_format);
+ diagnostic_show_locus (&dc, &my_rich_loc, DK_ERROR);
+
+ std::unique_ptr<sarif_multiformat_message_string> result
+ = builder.make_multiformat_message_string
+ (pp_formatted_text (dc.printer));
+
+ diagnostic_finish (&dc);
+
+ return result;
+ }
+ private:
+ const rich_location &m_richloc;
+ enum diagnostics_escape_format m_escape_format;
+ } the_renderer (rich_loc,
+ m_context.get_escape_format ());
+
auto location_obj = ::make_unique<sarif_location> ();
/* Get primary loc from RICH_LOC. */
location_t loc = rich_loc.get_loc ();
/* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
+ const content_renderer *snippet_renderer
+ = rich_loc.escape_on_output_p () ? &the_renderer : nullptr;
if (auto phs_loc_obj
= maybe_make_physical_location_object (loc, role,
- rich_loc.get_column_override ()))
+ rich_loc.get_column_override (),
+ snippet_renderer))
location_obj->set<sarif_physical_location> ("physicalLocation",
std::move (phs_loc_obj));
/* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
set_any_logical_locs_arr (*location_obj, logical_loc);
+ /* A flag for hinting that the diagnostic involves issues at the
+ level of character encodings (such as homoglyphs, or misleading
+ bidirectional control codes), and thus that it will be helpful
+ to the user if we show some representation of
+ how the characters in the pertinent source lines are encoded. */
+ if (rich_loc.escape_on_output_p ())
+ {
+ sarif_property_bag &bag = location_obj->get_or_create_properties ();
+ bag.set_bool ("gcc/escapeNonAscii", rich_loc.escape_on_output_p ());
+ }
+
return location_obj;
}
/* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
location_t loc = event.get_location ();
- if (auto phs_loc_obj = maybe_make_physical_location_object (loc, role, 0))
+ if (auto phs_loc_obj
+ = maybe_make_physical_location_object (loc, role, 0, nullptr))
location_obj->set<sarif_physical_location> ("physicalLocation",
std::move (phs_loc_obj));
sarif_builder::
maybe_make_physical_location_object (location_t loc,
enum diagnostic_artifact_role role,
- int column_override)
+ int column_override,
+ const content_renderer *snippet_renderer)
{
if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == nullptr)
return nullptr;
phys_loc_obj->set<sarif_region> ("region", std::move (region_obj));
/* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
- if (auto context_region_obj = maybe_make_region_object_for_context (loc))
+ if (auto context_region_obj
+ = maybe_make_region_object_for_context (loc, snippet_renderer))
phys_loc_obj->set<sarif_region> ("contextRegion",
std::move (context_region_obj));
the pertinent source. */
std::unique_ptr<sarif_region>
-sarif_builder::maybe_make_region_object_for_context (location_t loc) const
+sarif_builder::
+maybe_make_region_object_for_context (location_t loc,
+ const content_renderer *snippet_renderer)
+ const
{
location_t caret_loc = get_pure_location (loc);
if (auto artifact_content_obj
= maybe_make_artifact_content_object (exploc_start.file,
exploc_start.line,
- exploc_finish.line))
+ exploc_finish.line,
+ snippet_renderer))
region_obj->set<sarif_artifact_content> ("snippet",
std::move (artifact_content_obj));
/* Make a "tool" object (SARIF v2.1.0 section 3.18). */
std::unique_ptr<sarif_tool>
-sarif_builder::make_tool_object () const
+sarif_builder::make_tool_object ()
{
auto tool_obj = ::make_unique<sarif_tool> ();
calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
std::unique_ptr<sarif_tool_component>
-sarif_builder::make_driver_tool_component_object () const
+sarif_builder::make_driver_tool_component_object ()
{
auto driver_obj = ::make_unique<sarif_tool_component> ();
}
/* "rules" property (SARIF v2.1.0 section 3.19.23). */
- driver_obj->set ("rules", m_rules_arr);
+ driver_obj->set<json::array> ("rules", std::move (m_rules_arr));
return driver_obj;
}
}
/* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the given
- run of lines within FILENAME (including the endpoints). */
+ run of lines within FILENAME (including the endpoints).
+ If R is non-NULL, use it to potentially set the "rendered"
+ property (3.3.4). */
std::unique_ptr<sarif_artifact_content>
-sarif_builder::maybe_make_artifact_content_object (const char *filename,
- int start_line,
- int end_line) const
+sarif_builder::
+maybe_make_artifact_content_object (const char *filename,
+ int start_line,
+ int end_line,
+ const content_renderer *r) const
{
char *text_utf8 = get_source_lines (filename, start_line, end_line);
artifact_content_obj->set_string ("text", text_utf8);
free (text_utf8);
+ /* 3.3.4 "rendered" property. */
+ if (r)
+ if (std::unique_ptr<sarif_multiformat_message_string> rendered
+ = r->render (*this))
+ artifact_content_obj->set ("rendered", std::move (rendered));
+
return artifact_content_obj;
}
formatted,
stream));
}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_make_location_object (const line_table_case &case_)
+{
+ diagnostic_show_locus_fixture_one_liner_utf8 f (case_);
+ location_t line_end = linemap_position_for_column (line_table, 31);
+
+ /* Don't attempt to run the tests if column data might be unavailable. */
+ if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
+ return;
+
+ test_diagnostic_context dc;
+
+ sarif_builder builder (dc, "MAIN_INPUT_FILENAME", true);
+
+ const location_t foo
+ = make_location (linemap_position_for_column (line_table, 1),
+ linemap_position_for_column (line_table, 1),
+ linemap_position_for_column (line_table, 8));
+ const location_t bar
+ = make_location (linemap_position_for_column (line_table, 12),
+ linemap_position_for_column (line_table, 12),
+ linemap_position_for_column (line_table, 17));
+ const location_t field
+ = make_location (linemap_position_for_column (line_table, 19),
+ linemap_position_for_column (line_table, 19),
+ linemap_position_for_column (line_table, 30));
+
+ text_range_label label0 ("label0");
+ text_range_label label1 ("label1");
+ text_range_label label2 ("label2");
+
+ rich_location richloc (line_table, foo, &label0, nullptr);
+ richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
+ richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
+ richloc.set_escape_on_output (true);
+
+ std::unique_ptr<sarif_location> location_obj
+ = builder.make_location_object
+ (richloc, nullptr, diagnostic_artifact_role::analysis_target);
+ ASSERT_NE (location_obj, nullptr);
+
+ auto physical_location
+ = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location_obj.get (),
+ "physicalLocation");
+ {
+ auto region
+ = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location, "region");
+ ASSERT_JSON_INT_PROPERTY_EQ (region, "startLine", 1);
+ ASSERT_JSON_INT_PROPERTY_EQ (region, "startColumn", 1);
+ ASSERT_JSON_INT_PROPERTY_EQ (region, "endColumn", 7);
+ }
+ {
+ auto context_region
+ = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location,
+ "contextRegion");
+ ASSERT_JSON_INT_PROPERTY_EQ (context_region, "startLine", 1);
+
+ {
+ auto snippet
+ = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region, "snippet");
+
+ /* We expect the snippet's "text" to be a copy of the content. */
+ ASSERT_JSON_STRING_PROPERTY_EQ (snippet, "text", f.m_content);
+
+ /* We expect the snippet to have a "rendered" whose "text" has a
+ pure ASCII escaped copy of the line (with labels, etc). */
+ {
+ auto rendered
+ = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (snippet, "rendered");
+ ASSERT_JSON_STRING_PROPERTY_EQ
+ (rendered, "text",
+ "1 | <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
+ " | ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
+ " | | | |\n"
+ " | label0 label1 label2\n");
+ }
+ }
+ }
+}
+
+/* Run all of the selftests within this file. */
+
+void
+diagnostic_format_sarif_cc_tests ()
+{
+ for_each_line_table_case (test_make_location_object);
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
#include "diagnostic.h"
#include "diagnostic-color.h"
#include "gcc-rich-location.h"
+#include "text-range-label.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
+#include "selftest-diagnostic-show-locus.h"
#include "cpplib.h"
#include "text-art/types.h"
#include "text-art/theme.h"
/* Selftests for diagnostic_show_locus. */
+diagnostic_show_locus_fixture::
+diagnostic_show_locus_fixture (const line_table_case &case_,
+ const char *content)
+: m_content (content),
+ m_tmp_source_file (SELFTEST_LOCATION, ".c", content),
+ m_ltt (case_),
+ m_fc ()
+{
+ linemap_add (line_table, LC_ENTER, false,
+ m_tmp_source_file.get_filename (), 1);
+}
+
/* Verify that cpp_display_width correctly handles escaping. */
static void
no multibyte characters earlier on the line. */
const int emoji_col = 102;
- temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
- file_cache fc;
- line_table_test ltt (case_);
+ diagnostic_show_locus_fixture f (case_, content);
- linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
+ linemap_add (line_table, LC_ENTER, false, f.get_filename (), 1);
location_t line_end = linemap_position_for_column (line_table, line_bytes);
if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
return;
- ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
+ ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
ASSERT_EQ (1, LOCATION_LINE (line_end));
ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
- char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
+ char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1);
ASSERT_EQ (line_display_cols,
cpp_display_width (lspan.get_buffer (), lspan.length (),
def_policy ()));
ASSERT_EQ (line_display_cols,
- location_compute_display_column (fc,
+ location_compute_display_column (f.m_fc,
expand_location (line_end),
def_policy ()));
ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
....................0000000001111111.
....................1234567890123456. */
const char *content = "foo = bar.field;\n";
- temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
- line_table_test ltt (case_);
- linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
+ diagnostic_show_locus_fixture f (case_, content);
location_t line_end = linemap_position_for_column (line_table, 16);
if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
return;
- ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
+ ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
ASSERT_EQ (1, LOCATION_LINE (line_end));
ASSERT_EQ (16, LOCATION_COLUMN (line_end));
test_one_liner_labels ();
}
-/* Version of all one-liner tests exercising multibyte awareness. For
- simplicity we stick to using two multibyte characters in the test, U+1F602
- == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
- == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
- below asserts would be easier to read if we used UTF-8 directly in the
- string constants, but it seems better not to demand the host compiler
- support this, when it isn't otherwise necessary. Instead, whenever an
- extended character appears in a string, we put a line break after it so that
- all succeeding characters can appear visually at the correct display column.
+/* Version of all one-liner tests exercising multibyte awareness.
+ These are all called from test_diagnostic_show_locus_one_liner,
+ which uses diagnostic_show_locus_fixture_one_liner_utf8 to create
+ the test file; see the notes in diagnostic-show-locus-selftest.h.
- All of these work on the following 1-line source file:
-
- .0000000001111111111222222 display
- .1234567890123456789012345 columns
- "SS_foo = P_bar.SS_fieldP;\n"
- .0000000111111111222222223 byte
- .1356789012456789134567891 columns
-
- which is set up by test_diagnostic_show_locus_one_liner and calls
- them. Here SS represents the two display columns for the U+1F602 emoji and
- P represents the one display column for the U+03C0 pi symbol. */
+ Note: all of the below asserts would be easier to read if we used UTF-8
+ directly in the string constants, but it seems better not to demand the
+ host compiler support this, when it isn't otherwise necessary. Instead,
+ whenever an extended character appears in a string, we put a line break
+ after it so that all succeeding characters can appear visually at the
+ correct display column. */
/* Just a caret. */
ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
}
+static const char * const one_liner_utf8_content
+ /* Display columns.
+ 0000000000000000000000011111111111111111111111111111112222222222222
+ 1111111122222222345678900000000123456666666677777777890123444444445 */
+ = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
+ /* 0000000000000000000001111111111111111111222222222222222222222233333
+ 1111222233334444567890122223333456789999000011112222345678999900001
+ Byte columns. */
+
+diagnostic_show_locus_fixture_one_liner_utf8::
+diagnostic_show_locus_fixture_one_liner_utf8 (const line_table_case &case_)
+: diagnostic_show_locus_fixture (case_, one_liner_utf8_content)
+{
+}
+
/* Run the various one-liner tests. */
static void
test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
{
- /* Create a tempfile and write some text to it. */
- const char *content
- /* Display columns.
- 0000000000000000000000011111111111111111111111111111112222222222222
- 1111111122222222345678900000000123456666666677777777890123444444445 */
- = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
- /* 0000000000000000000001111111111111111111222222222222222222222233333
- 1111222233334444567890122223333456789999000011112222345678999900001
- Byte columns. */
- temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
- file_cache fc;
- line_table_test ltt (case_);
-
- linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
+ diagnostic_show_locus_fixture_one_liner_utf8 f (case_);
location_t line_end = linemap_position_for_column (line_table, 31);
if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
return;
- ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
+ ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
ASSERT_EQ (1, LOCATION_LINE (line_end));
ASSERT_EQ (31, LOCATION_COLUMN (line_end));
- char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
+ char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1);
ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
def_policy ()));
- ASSERT_EQ (25, location_compute_display_column (fc,
+ ASSERT_EQ (25, location_compute_display_column (f.m_fc,
expand_location (line_end),
def_policy ()));
location_t indent);
};
-/* Concrete subclass of libcpp's range_label.
- Simple implementation using a string literal. */
-
-class text_range_label : public range_label
-{
- public:
- text_range_label (const char *text) : m_text (text) {}
-
- label_text get_text (unsigned /*range_idx*/) const final override
- {
- return label_text::borrow (m_text);
- }
-
- private:
- const char *m_text;
-};
-
#endif /* GCC_RICH_LOCATION_H */
--- /dev/null
+/* Support for selftests involving diagnostic_show_locus.
+ Copyright (C) 1999-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_SELFTEST_DIAGNOSTIC_SHOW_LOCUS_H
+#define GCC_SELFTEST_DIAGNOSTIC_SHOW_LOCUS_H
+
+#include "selftest.h"
+
+/* The selftest code should entirely disappear in a production
+ configuration, hence we guard all of it with #if CHECKING_P. */
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* RAII class for use in selftests involving diagnostic_show_locus.
+
+ Manages creating and cleaning up the following:
+ - writing out a temporary .c file containing CONTENT
+ - temporarily override the global "line_table" (using CASE_) and
+ push a line_map starting at the first line of the temporary file
+ - provide a file_cache. */
+
+struct diagnostic_show_locus_fixture
+{
+ diagnostic_show_locus_fixture (const line_table_case &case_,
+ const char *content);
+
+ const char *get_filename () const
+ {
+ return m_tmp_source_file.get_filename ();
+ }
+
+ const char *m_content;
+ temp_source_file m_tmp_source_file;
+ line_table_test m_ltt;
+ file_cache m_fc;
+};
+
+/* Fixture for one-liner tests exercising multibyte awareness. For
+ simplicity we stick to using two multibyte characters in the test, U+1F602
+ == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
+ == "\xcf\x80", which uses 2 bytes and 1 display column.
+
+ This works with the following 1-line source file:
+
+ .0000000001111111111222222 display
+ .1234567890123456789012345 columns
+ "SS_foo = P_bar.SS_fieldP;\n"
+ .0000000111111111222222223 byte
+ .1356789012456789134567891 columns
+
+ Here SS represents the two display columns for the U+1F602 emoji and
+ P represents the one display column for the U+03C0 pi symbol. */
+
+struct diagnostic_show_locus_fixture_one_liner_utf8
+ : public diagnostic_show_locus_fixture
+{
+ diagnostic_show_locus_fixture_one_liner_utf8 (const line_table_case &case_);
+};
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
+
+#endif /* GCC_SELFTEST_DIAGNOSTIC_SHOW_LOCUS_H */
--- /dev/null
+/* Selftest support for JSON.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+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/>. */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "selftest.h"
+#include "selftest-json.h"
+
+/* The selftest code should entirely disappear in a production
+ configuration, hence we guard all of it with #if CHECKING_P. */
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Assert that VALUE is a non-null json::object,
+ returning it as such, failing at LOC if this isn't the case. */
+
+const json::object *
+expect_json_object (const location &loc,
+ const json::value *value)
+{
+ ASSERT_NE_AT (loc, value, nullptr);
+ ASSERT_EQ_AT (loc, value->get_kind (), json::JSON_OBJECT);
+ return static_cast<const json::object *> (value);
+}
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME.
+ Return the value of the property.
+ Use LOC for any failures. */
+
+const json::value *
+expect_json_object_with_property (const location &loc,
+ const json::value *value,
+ const char *property_name)
+{
+ const json::object *obj = expect_json_object (loc, value);
+ const json::value *property_value = obj->get (property_name);
+ ASSERT_NE_AT (loc, property_value, nullptr);
+ return property_value;
+}
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME, and that the value of that property is a non-null
+ json::integer_number equalling EXPECTED_VALUE.
+ Use LOC for any failures. */
+
+void
+assert_json_int_property_eq (const location &loc,
+ const json::value *value,
+ const char *property_name,
+ long expected_value)
+{
+ const json::value *property_value
+ = expect_json_object_with_property (loc, value, property_name);
+ ASSERT_EQ_AT (loc, property_value->get_kind (), json::JSON_INTEGER);
+ long actual_value
+ = static_cast<const json::integer_number *> (property_value)->get ();
+ ASSERT_EQ_AT (loc, expected_value, actual_value);
+}
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME, and that the property value is a non-null JSON object.
+ Return the value of the property as a json::object.
+ Use LOC for any failures. */
+
+const json::object *
+expect_json_object_with_object_property (const location &loc,
+ const json::value *value,
+ const char *property_name)
+{
+ const json::value *property_value
+ = expect_json_object_with_property (loc, value, property_name);
+ ASSERT_EQ_AT (loc, property_value->get_kind (), json::JSON_OBJECT);
+ return static_cast<const json::object *> (property_value);
+}
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME, and that the value of that property is a non-null
+ JSON string equalling EXPECTED_VALUE.
+ Use LOC for any failures. */
+
+void
+assert_json_string_property_eq (const location &loc,
+ const json::value *value,
+ const char *property_name,
+ const char *expected_value)
+{
+ const json::value *property_value
+ = expect_json_object_with_property (loc, value, property_name);
+ ASSERT_EQ_AT (loc, property_value->get_kind (), json::JSON_STRING);
+ const json::string *str = static_cast<const json::string *> (property_value);
+ ASSERT_STREQ_AT (loc, expected_value, str->get_string ());
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
--- /dev/null
+/* Selftest support for JSON.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+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_SELFTEST_JSON_H
+#define GCC_SELFTEST_JSON_H
+
+#include "json.h"
+
+/* The selftest code should entirely disappear in a production
+ configuration, hence we guard all of it with #if CHECKING_P. */
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Assert that VALUE is a non-null json::object,
+ returning it as such, failing at LOC if this isn't the case. */
+
+const json::object *
+expect_json_object (const location &loc,
+ const json::value *value);
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME.
+ Return the value of the property.
+ Use LOC for any failures. */
+
+const json::value *
+expect_json_object_with_property (const location &loc,
+ const json::value *value,
+ const char *property_name);
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME, and that the value of that property is a non-null
+ json::integer_number equalling EXPECTED_VALUE.
+ Use LOC for any failures. */
+
+void
+assert_json_int_property_eq (const location &loc,
+ const json::value *value,
+ const char *property_name,
+ long expected_value);
+#define ASSERT_JSON_INT_PROPERTY_EQ(JSON_VALUE, PROPERTY_NAME, EXPECTED_VALUE) \
+ assert_json_int_property_eq ((SELFTEST_LOCATION), \
+ (JSON_VALUE), \
+ (PROPERTY_NAME), \
+ (EXPECTED_VALUE))
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME, and that the property value is a non-null JSON object.
+ Return the value of the property as a json::object.
+ Use LOC for any failures. */
+
+const json::object *
+expect_json_object_with_object_property (const location &loc,
+ const json::value *value,
+ const char *property_name);
+#define EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY(JSON_VALUE, PROPERTY_NAME) \
+ expect_json_object_with_object_property ((SELFTEST_LOCATION), \
+ (JSON_VALUE), \
+ (PROPERTY_NAME))
+
+/* Assert that VALUE is a non-null json::object that has property
+ PROPERTY_NAME, and that the value of that property is a non-null
+ JSON string equalling EXPECTED_VALUE.
+ Use LOC for any failures. */
+
+void
+assert_json_string_property_eq (const location &loc,
+ const json::value *value,
+ const char *property_name,
+ const char *expected_value);
+#define ASSERT_JSON_STRING_PROPERTY_EQ(JSON_VALUE, PROPERTY_NAME, EXPECTED_VALUE) \
+ assert_json_string_property_eq ((SELFTEST_LOCATION), \
+ (JSON_VALUE), \
+ (PROPERTY_NAME), \
+ (EXPECTED_VALUE))
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
+
+#endif /* GCC_SELFTEST_JSON_H */
diagnostic_color_cc_tests ();
diagnostic_show_locus_cc_tests ();
diagnostic_format_json_cc_tests ();
+ diagnostic_format_sarif_cc_tests ();
edit_context_cc_tests ();
fold_const_cc_tests ();
spellcheck_cc_tests ();
extern void convert_cc_tests ();
extern void diagnostic_color_cc_tests ();
extern void diagnostic_format_json_cc_tests ();
+extern void diagnostic_format_sarif_cc_tests ();
extern void diagnostic_path_cc_tests ();
extern void diagnostic_show_locus_cc_tests ();
extern void digraph_cc_tests ();
{ dg-final { scan-sarif-file {"text": "unpaired UTF-8 bidirectional control characters detected"} } }
{ dg-final { scan-sarif-file {"text": "unpaired UTF-8 bidirectional control characters detected"} } }
+
+ Verify that the expected property bag property is present.
+ { dg-final { scan-sarif-file {"gcc/escapeNonAscii": true} } }
+
+ Verify that the snippets have a "rendered" property.
+ We check the contents of the property via a selftest.
+
+ { dg-final { scan-sarif-file {"rendered": } } }
+
*/
#include "context.h"
#include "print-tree.h"
#include "gcc-rich-location.h"
+#include "text-range-label.h"
int plugin_is_GPL_compatible;
--- /dev/null
+/* Simple implementation of range_label.
+ Copyright (C) 2014-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_TEXT_RANGE_LABEL_H
+#define GCC_TEXT_RANGE_LABEL_H
+
+#include "rich-location.h"
+
+/* Concrete subclass of libcpp's range_label.
+ Simple implementation using a string literal. */
+
+class text_range_label : public range_label
+{
+ public:
+ text_range_label (const char *text) : m_text (text) {}
+
+ label_text get_text (unsigned /*range_idx*/) const final override
+ {
+ return label_text::borrow (m_text);
+ }
+
+ private:
+ const char *m_text;
+};
+
+#endif /* GCC_TEXT_RANGE_LABEL_H */
public:
semi_embedded_vec ();
~semi_embedded_vec ();
+ semi_embedded_vec (const semi_embedded_vec &other);
unsigned int count () const { return m_num; }
T& operator[] (int idx);
{
}
+/* Copy constructor for semi_embedded_vec. */
+
+template <typename T, int NUM_EMBEDDED>
+semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec (const semi_embedded_vec &other)
+: m_num (0),
+ m_alloc (other.m_alloc),
+ m_extra (nullptr)
+{
+ if (other.m_extra)
+ m_extra = XNEWVEC (T, m_alloc);
+
+ for (int i = 0; i < other.m_num; i++)
+ push (other[i]);
+}
+
/* semi_embedded_vec's dtor. Release any dynamically-allocated memory. */
template <typename T, int NUM_EMBEDDED>
/* Destructor. */
~rich_location ();
- /* The class manages the memory pointed to by the elements of
- the M_FIXIT_HINTS vector and is not meant to be copied or
- assigned. */
- rich_location (const rich_location &) = delete;
- void operator= (const rich_location &) = delete;
+ rich_location (const rich_location &);
+ rich_location (rich_location &&) = delete;
+ rich_location &operator= (const rich_location &) = delete;
+ rich_location &operator= (rich_location &&) = delete;
/* Accessors. */
location_t get_loc () const { return get_loc (0); }
mutable expanded_location m_expanded_location;
+ /* The class manages the memory pointed to by the elements of
+ the m_fixit_hints vector. */
static const int MAX_STATIC_FIXIT_HINTS = 2;
semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
fixit_hint (location_t start,
location_t next_loc,
const char *new_content);
+ fixit_hint (const fixit_hint &other);
+ fixit_hint (fixit_hint &&other) = delete;
~fixit_hint () { free (m_bytes); }
+ fixit_hint &operator= (const fixit_hint &) = delete;
+ fixit_hint &operator= (fixit_hint &&) = delete;
bool affects_line_p (const line_maps *set,
const char *file,
add_range (loc, SHOW_RANGE_WITH_CARET, label, label_highlight_color);
}
+/* Copy ctor for rich_location.
+ Take a deep copy of the fixit hints, which are owneed;
+ everything else is borrowed. */
+
+rich_location::rich_location (const rich_location &other)
+: m_line_table (other.m_line_table),
+ m_ranges (other.m_ranges),
+ m_column_override (other.m_column_override),
+ m_have_expanded_location (other.m_have_expanded_location),
+ m_seen_impossible_fixit (other.m_seen_impossible_fixit),
+ m_fixits_cannot_be_auto_applied (other.m_fixits_cannot_be_auto_applied),
+ m_escape_on_output (other.m_escape_on_output),
+ m_expanded_location (other.m_expanded_location),
+ m_fixit_hints (),
+ m_path (other.m_path)
+{
+ for (unsigned i = 0; i < other.m_fixit_hints.count (); i++)
+ m_fixit_hints.push (new fixit_hint (*other.m_fixit_hints[i]));
+}
+
/* The destructor for class rich_location. */
rich_location::~rich_location ()
{
}
+fixit_hint::fixit_hint (const fixit_hint &other)
+: m_start (other.m_start),
+ m_next_loc (other.m_next_loc),
+ m_bytes (xstrdup (other.m_bytes)),
+ m_len (other.m_len)
+{
+}
+
/* Does this fix-it hint affect the given line? */
bool