Ping. I believe I need review of the selftest.h change; the rest I think I can self-approve, if need be.
https://gcc.gnu.org/ml/gcc-patches/2016-06/msg01340.html On Fri, 2016-06-17 at 17:41 -0400, David Malcolm wrote: > This patch adds explicit testing of lexing a source file, > generalizing this (and the test of ordinary line maps) over > a 2-dimensional test matrix covering: > > (1) line_table->default_range_bits: some frontends use a non-zero value > and others use zero > > (2) the fallback modes within line-map.c: there are various threshold > values for source_location/location_t beyond line-map.c changes > behavior (disabling of the range-packing optimization, disabling > of column-tracking). We exercise these by starting the line_table > at interesting values at or near these thresholds. > > This helps ensures that location data works in all of these states, > and that (I hope) we don't have lingering bugs relating to the > transition between line_table states. > > Successfully bootstrapped®rtested on x86_64-pc-linux-gnu; > Successful -fself-test of stage1 on powerpc-ibm-aix7.1.3.0. > > OK for trunk? (I can self-approve much of this, but it's probably > worth having another pair of eyes look at it, if nothing else). > > gcc/ChangeLog: > > * input.c: Include cpplib.h. > > (selftest::temp_source_file): New class. > > (selftest::temp_source_file::temp_source_file): New ctor. > > (selftest::temp_source_file::~temp_source_file): New dtor. > > (selftest::should_have_column_data_p): New function. > > (selftest::test_should_have_column_data_p): New function. > > (selftest::temp_line_table): New class. > > (selftest::temp_line_table::temp_line_table): New ctor. > > (selftest::temp_line_table::~temp_line_table): New dtor. > > (selftest::test_accessing_ordinary_linemaps): Add case_ param; use > > it to create a temp_line_table. > > (selftest::assert_loceq): Only verify LOCATION_COLUMN for > > locations that are known to have column data. > > (selftest::line_table_case): New struct. > > (selftest::test_reading_source_line): Move tempfile handling > > to class temp_source_file. > > (ASSERT_TOKEN_AS_TEXT_EQ): New macro. > > (selftest::assert_token_loc_eq): New function. > > (ASSERT_TOKEN_LOC_EQ): New macro. > > (selftest::test_lexer): New function. > > (selftest::boundary_locations): New array. > > (selftest::input_c_tests): Call test_should_have_column_data_p. > > Loop over a test matrix of interesting values of location and > > default_range_bits, calling test_lexer on each case in the matrix. > > Move call to test_accessing_ordinary_linemaps into the matrix. > > * selftest.h (ASSERT_EQ): Reimplement in terms of... > > (ASSERT_EQ_AT): New macro. > > gcc/testsuite/ChangeLog: > > * gcc.dg/plugin/location_overflow_plugin.c (plugin_init): Avoid > > hardcoding the values of LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES > > and LINE_MAP_MAX_LOCATION_WITH_COLS. > > libcpp/ChangeLog: > > * include/line-map.h (LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES): > > Move here from line-map.c. > > (LINE_MAP_MAX_LOCATION_WITH_COLS): Likewise. > > * line-map.c (LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES): Move from > > here to line-map.h. > > (LINE_MAP_MAX_LOCATION_WITH_COLS): Likewise. > --- > gcc/input.c | 323 > +++++++++++++++++++-- > gcc/selftest.h | 12 +- > .../gcc.dg/plugin/location_overflow_plugin.c | 4 +- > libcpp/include/line-map.h | 10 + > libcpp/line-map.c | 12 - > 5 files changed, 327 insertions(+), 34 deletions(-) > > diff --git a/gcc/input.c b/gcc/input.c > index 3fb4a25..0016555 100644 > --- a/gcc/input.c > +++ b/gcc/input.c > @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see > #include "intl.h" > #include "diagnostic-core.h" > #include "selftest.h" > +#include "cpplib.h" > > /* This is a cache used by get_next_line to store the content of a > file to be searched for file lines. */ > @@ -1144,6 +1145,74 @@ namespace selftest { > > /* Selftests of location handling. */ > > +/* A class for writing out a temporary sourcefile for use in selftests > + of input handling. */ > + > +class temp_source_file > +{ > + public: > + temp_source_file (const location &loc, const char *suffix, > +> > > const char *content); > + ~temp_source_file (); > + > + const char *get_filename () const { return m_filename; } > + > + private: > + char *m_filename; > +}; > + > +/* Constructor. Create a tempfile using SUFFIX, and write CONTENT to > + it. Abort if anything goes wrong, using LOC as the effective > + location in the problem report. */ > + > +temp_source_file::temp_source_file (const location &loc, const char *suffix, > +> > > > > const char *content) > +{ > + m_filename = make_temp_file (suffix); > + ASSERT_NE (m_filename, NULL); > + > + FILE *out = fopen (m_filename, "w"); > + if (!out) > + ::selftest::fail_formatted (loc, "unable to open tempfile: %s", > +> > > > > m_filename); > + fprintf (out, content); > + fclose (out); > +} > + > +/* Destructor. Delete the tempfile. */ > + > +temp_source_file::~temp_source_file () > +{ > + unlink (m_filename); > + free (m_filename); > +} > + > +/* Helper function for verifying location data: when location_t > + values are > LINE_MAP_MAX_LOCATION_WITH_COLS, they are treated > + as having column 0. */ > + > +static bool > +should_have_column_data_p (location_t loc) > +{ > + if (IS_ADHOC_LOC (loc)) > + loc = get_location_from_adhoc_loc (line_table, loc); > + if (loc > LINE_MAP_MAX_LOCATION_WITH_COLS) > + return false; > + return true; > +} > + > +/* Selftest for should_have_column_data_p. */ > + > +static void > +test_should_have_column_data_p () > +{ > + ASSERT_TRUE (should_have_column_data_p (RESERVED_LOCATION_COUNT)); > + ASSERT_TRUE > + (should_have_column_data_p (LINE_MAP_MAX_LOCATION_WITH_COLS)); > + ASSERT_FALSE > + (should_have_column_data_p (LINE_MAP_MAX_LOCATION_WITH_COLS + 1)); > +} > + > /* Verify the result of LOCATION_FILE/LOCATION_LINE/LOCATION_COLUMN > on LOC. */ > > @@ -1153,14 +1222,87 @@ assert_loceq (const char *exp_filename, int > exp_linenum, int exp_colnum, > { > ASSERT_STREQ (exp_filename, LOCATION_FILE (loc)); > ASSERT_EQ (exp_linenum, LOCATION_LINE (loc)); > - ASSERT_EQ (exp_colnum, LOCATION_COLUMN (loc)); > + /* If location_t values are sufficiently high, then column numbers > + will be unavailable and LOCATION_COLUMN (loc) will be 0. > + When close to the threshold, column numbers *may* be present: if > + the final linemap before the threshold contains a line that straddles > + the threshold, locations in that line have column information. */ > + if (should_have_column_data_p (loc)) > + ASSERT_EQ (exp_colnum, LOCATION_COLUMN (loc)); > +} > + > +/* Various selftests in this file involve constructing a line table > + and one or more line maps within it. > + > + For maximum test coverage we want to run these tests with a variety > + of situations: > + - line_table->default_range_bits: some frontends use a non-zero value > + and others use zero > + - the fallback modes within line-map.c: there are various threshold > + values for source_location/location_t beyond line-map.c changes > + behavior (disabling of the range-packing optimization, disabling > + of column-tracking). We can exercise these by starting the line_table > + at interesting values at or near these thresholds. > + > + The following struct describes a particular case within our test > + matrix. */ > + > +struct line_table_case > +{ > + line_table_case (int default_range_bits, int base_location) > + : m_default_range_bits (default_range_bits), > + m_base_location (base_location) > + {} > + > + int m_default_range_bits; > + int m_base_location; > +}; > + > +/* A class for overriding the global "line_table" within a selftest, > + restoring its value afterwards. */ > + > +class temp_line_table > +{ > + public: > + temp_line_table (const line_table_case &); > + ~temp_line_table (); > + > + private: > + line_maps *m_old_line_table; > +}; > + > +/* Constructor. Store the old value of line_table, and create a new > + one, using the sitation described in CASE_. */ > + > +temp_line_table::temp_line_table (const line_table_case &case_) > + : m_old_line_table (line_table) > +{ > + line_table = ggc_alloc (); > + linemap_init (line_table, BUILTINS_LOCATION); > + line_table->reallocator = m_old_line_table->reallocator; > + line_table->round_alloc_size = m_old_line_table->round_alloc_size; > + line_table->default_range_bits = case_.m_default_range_bits; > + if (case_.m_base_location) > + { > + line_table->highest_location = case_.m_base_location; > + line_table->highest_line = case_.m_base_location; > + } > +} > + > +/* Destructor. Restore the old value of line_table. */ > + > +temp_line_table::~temp_line_table () > +{ > + line_table = m_old_line_table; > } > > /* Verify basic operation of ordinary linemaps. */ > > static void > -test_accessing_ordinary_linemaps () > +test_accessing_ordinary_linemaps (const line_table_case &case_) > { > + temp_line_table tmp_lt (case_); > + > /* Build a simple linemap describing some locations. */ > linemap_add (line_table, LC_ENTER, false, "foo.c", 0); > > @@ -1220,21 +1362,15 @@ static void > test_reading_source_line () > { > /* Create a tempfile and write some text to it. */ > - char *filename = make_temp_file (".txt"); > - ASSERT_NE (filename, NULL); > - FILE *out = fopen (filename, "w"); > - if (!out) > - ::selftest::fail_formatted (SELFTEST_LOCATION, > -> > > > > "unable to open tempfile: %s", filename); > - fprintf (out, > -> > "01234567890123456789\n" > -> > "This is the test text\n" > -> > "This is the 3rd line\n"); > - fclose (out); > + temp_source_file tmp (SELFTEST_LOCATION, ".txt", > +> > > > "01234567890123456789\n" > +> > > > "This is the test text\n" > +> > > > "This is the 3rd line\n"); > > /* Read back a specific line from the tempfile. */ > int line_size; > - const char *source_line = location_get_source_line (filename, 2, > &line_size); > + const char *source_line = location_get_source_line (tmp.get_filename (), > +> > > > > > > 2, &line_size); > ASSERT_TRUE (source_line != NULL); > ASSERT_EQ (21, line_size); > if (!strncmp ("This is the test text", > @@ -1245,18 +1381,171 @@ test_reading_source_line () > ::selftest::fail (SELFTEST_LOCATION, > > > > "source_line did not match expected value"); > > - unlink (filename); > - free (filename); > } > > +/* Tests of lexing. */ > + > +/* Verify that token TOK from PARSER has cpp_token_as_text > + equal to EXPECTED_TEXT. */ > + > +#define ASSERT_TOKEN_AS_TEXT_EQ(PARSER, TOK, EXPECTED_TEXT)> > > > \ > + SELFTEST_BEGIN_STMT> > > > > > > > > \ > + unsigned char *actual_txt = cpp_token_as_text ((PARSER), (TOK));> > > \ > + ASSERT_STREQ ((EXPECTED_TEXT), (const char *)actual_txt);> > > > \ > + SELFTEST_END_STMT > + > +/* Verify that TOK's src_loc is within EXP_FILENAME at EXP_LINENUM, > + and ranges from EXP_START_COL to EXP_FINISH_COL. > + Use LOC as the effective location of the selftest. */ > + > +static void > +assert_token_loc_eq (const location &loc, > +> > > const cpp_token *tok, > +> > > const char *exp_filename, int exp_linenum, > +> > > int exp_start_col, int exp_finish_col) > +{ > + location_t tok_loc = tok->src_loc; > + ASSERT_STREQ_AT (loc, exp_filename, LOCATION_FILE (tok_loc)); > + ASSERT_EQ_AT (loc, exp_linenum, LOCATION_LINE (tok_loc)); > + > + /* If location_t values are sufficiently high, then column numbers > + will be unavailable. */ > + if (!should_have_column_data_p (tok_loc)) > + return; > + > + ASSERT_EQ_AT (loc, exp_start_col, LOCATION_COLUMN (tok_loc)); > + source_range tok_range = get_range_from_loc (line_table, tok_loc); > + ASSERT_EQ_AT (loc, exp_start_col, LOCATION_COLUMN (tok_range.m_start)); > + ASSERT_EQ_AT (loc, exp_finish_col, LOCATION_COLUMN (tok_range.m_finish)); > +} > + > +/* Use assert_token_loc_eq to verify the TOK->src_loc, using > + SELFTEST_LOCATION as the effective location of the selftest. */ > + > +#define ASSERT_TOKEN_LOC_EQ(TOK, EXP_FILENAME, EXP_LINENUM, \ > +> > > > EXP_START_COL, EXP_FINISH_COL) \ > + assert_token_loc_eq (SELFTEST_LOCATION, (TOK), (EXP_FILENAME), \ > +> > > (EXP_LINENUM), (EXP_START_COL), (EXP_FINISH_COL)) > + > +/* Test of lexing a file using libcpp, verifying tokens and their > + location information. */ > + > +static void > +test_lexer (const line_table_case &case_) > +{ > + /* Create a tempfile and write some text to it. */ > + const char *content = > + /*00000000011111111112222222222333333.3333444444444.455555555556 > + 12345678901234567890123456789012345.6789012345678.901234567890. */ > + ("test_name /* c-style comment */\n" > + " \"test literal\"\n" > + " // test c++-style comment\n" > + " 42\n"); > + temp_source_file tmp (SELFTEST_LOCATION, ".txt", content); > + > + temp_line_table tmp_lt (case_); > + > + cpp_reader *parser = cpp_create_reader (CLK_GNUC89, NULL, line_table); > + > + const char *fname = cpp_read_main_file (parser, tmp.get_filename ()); > + ASSERT_NE (fname, NULL); > + > + /* Verify that we get the expected tokens back, with the correct > + location information. */ > + > + location_t loc; > + const cpp_token *tok; > + tok = cpp_get_token_with_location (parser, &loc); > + ASSERT_NE (tok, NULL); > + ASSERT_EQ (tok->type, CPP_NAME); > + ASSERT_TOKEN_AS_TEXT_EQ (parser, tok, "test_name"); > + ASSERT_TOKEN_LOC_EQ (tok, tmp.get_filename (), 1, 1, 9); > + > + tok = cpp_get_token_with_location (parser, &loc); > + ASSERT_NE (tok, NULL); > + ASSERT_EQ (tok->type, CPP_STRING); > + ASSERT_TOKEN_AS_TEXT_EQ (parser, tok, "\"test literal\""); > + ASSERT_TOKEN_LOC_EQ (tok, tmp.get_filename (), 2, 35, 48); > + > + tok = cpp_get_token_with_location (parser, &loc); > + ASSERT_NE (tok, NULL); > + ASSERT_EQ (tok->type, CPP_NUMBER); > + ASSERT_TOKEN_AS_TEXT_EQ (parser, tok, "42"); > + ASSERT_TOKEN_LOC_EQ (tok, tmp.get_filename (), 4, 4, 5); > + > + tok = cpp_get_token_with_location (parser, &loc); > + ASSERT_NE (tok, NULL); > + ASSERT_EQ (tok->type, CPP_EOF); > + > + cpp_finish (parser, NULL); > +} > + > +/* A table of interesting location_t values, giving one axis of our test > + matrix. */ > + > +static const location_t boundary_locations[] = { > + /* Zero means "don't override the default values for a new line_table". */ > + 0, > + > + /* An arbitrary non-zero value that isn't close to one of > + the boundary values below. */ > + 0x10000, > + > + /* Values near LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES. */ > + LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES - 0x100, > + LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES - 1, > + LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES, > + LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES + 1, > + LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES + 0x100, > + > + /* Values near LINE_MAP_MAX_LOCATION_WITH_COLS. */ > + LINE_MAP_MAX_LOCATION_WITH_COLS - 0x100, > + LINE_MAP_MAX_LOCATION_WITH_COLS - 1, > + LINE_MAP_MAX_LOCATION_WITH_COLS, > + LINE_MAP_MAX_LOCATION_WITH_COLS + 1, > + LINE_MAP_MAX_LOCATION_WITH_COLS + 0x100, > +}; > + > /* Run all of the selftests within this file. */ > > void > input_c_tests () > { > - test_accessing_ordinary_linemaps (); > + test_should_have_column_data_p (); > test_unknown_location (); > test_builtins (); > + > + /* As noted above in the description of struct line_table_case, > + we want to explore a test matrix of interesting line_table > + situations, running various selftests for each case within the > + matrix. */ > + > + /* Run all tests with: > + (a) line_table->default_range_bits == 0, and > + (b) line_table->default_range_bits == 5. */ > + int num_cases_tested = 0; > + for (int default_range_bits = 0; default_range_bits <= 5; > + default_range_bits += 5) > + { > + /* ...and use each of the "interesting" location values as > +> > the starting location within line_table. */ > + const int num_boundary_locations > +> > = sizeof (boundary_locations) / sizeof (boundary_locations[0]); > + for (int loc_idx = 0; loc_idx < num_boundary_locations; loc_idx++) > +> > { > +> > line_table_case c (default_range_bits, boundary_locations[loc_idx]); > + > +> > /* Run all tests for the given case within the test matrix. */ > +> > test_accessing_ordinary_linemaps (c); > +> > test_lexer (c); > + > +> > num_cases_tested++; > +> > } > + } > + > + /* Verify that we fully covered the test matrix. */ > + ASSERT_EQ (num_cases_tested, 2 * 12); > + > test_reading_source_line (); > } > > diff --git a/gcc/selftest.h b/gcc/selftest.h > index e719f5f..3906bed 100644 > --- a/gcc/selftest.h > +++ b/gcc/selftest.h > @@ -127,13 +127,19 @@ extern int num_passes; > ::selftest::pass if they are equal, > ::selftest::fail if they are non-equal. */ > > -#define ASSERT_EQ(EXPECTED, ACTUAL)> > > > \ > +#define ASSERT_EQ(EXPECTED, ACTUAL) \ > + ASSERT_EQ_AT ((SELFTEST_LOCATION), (EXPECTED), (ACTUAL)) > + > +/* Like ASSERT_EQ, but treat LOC as the effective location of the > + selftest. */ > + > +#define ASSERT_EQ_AT(LOC, EXPECTED, ACTUAL)> > > \ > SELFTEST_BEGIN_STMT> > > > > > \ > const char *desc = "ASSERT_EQ (" #EXPECTED ", " #ACTUAL ")"; \ > if ((EXPECTED) == (ACTUAL))> > > > > \ > - ::selftest::pass (SELFTEST_LOCATION, desc);> > > > > \ > + ::selftest::pass ((LOC), desc);> > > > \ > else> > > > > > > > \ > - ::selftest::fail (SELFTEST_LOCATION, desc);> > > > > \ > + ::selftest::fail ((LOC), desc);> > > > \ > SELFTEST_END_STMT > > /* Evaluate EXPECTED and ACTUAL and compare them with !=, calling > diff --git a/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c > b/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c > index 1c140d8..3644d9f 100644 > --- a/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c > +++ b/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c > @@ -87,11 +87,11 @@ plugin_init (struct plugin_name_args *plugin_info, > original_finalizer = diagnostic_finalizer (global_dc); > switch (base_location) > { > - case 0x50000001: > + case LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES + 1: > diagnostic_finalizer (global_dc) = verify_unpacked_ranges; > break; > > - case 0x60000001: > + case LINE_MAP_MAX_LOCATION_WITH_COLS + 1: > diagnostic_finalizer (global_dc) = verify_no_columns; > break; > > diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h > index 0f4b522..08c5aef 100644 > --- a/libcpp/include/line-map.h > +++ b/libcpp/include/line-map.h > @@ -260,6 +260,16 @@ typedef unsigned int linenum_type; > worked example in libcpp/location-example.txt. */ > typedef unsigned int source_location; > > +/* Do not pack ranges if locations get higher than this. > + If you change this, update: > + gcc.dg/plugin/location-overflow-test-*.c. */ > +const source_location LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES = 0x50000000; > + > +/* Do not track column numbers if locations get higher than this. > + If you change this, update: > + gcc.dg/plugin/location-overflow-test-*.c. */ > +const source_location LINE_MAP_MAX_LOCATION_WITH_COLS = 0x60000000; > + > /* A range of source locations. > > Ranges are closed: > diff --git a/libcpp/line-map.c b/libcpp/line-map.c > index 2e61895..d7c0d08 100644 > --- a/libcpp/line-map.c > +++ b/libcpp/line-map.c > @@ -31,18 +31,6 @@ along with this program; see the file COPYING3. If not see > disabled). */ > const unsigned int LINE_MAP_MAX_COLUMN_NUMBER = (1U << 12); > > -/* Do not pack ranges if locations get higher than this. > - If you change this, update: > - gcc.dg/plugin/location_overflow_plugin.c > - gcc.dg/plugin/location-overflow-test-*.c. */ > -const source_location LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES = 0x50000000; > - > -/* Do not track column numbers if locations get higher than this. > - If you change this, update: > - gcc.dg/plugin/location_overflow_plugin.c > - gcc.dg/plugin/location-overflow-test-*.c. */ > -const source_location LINE_MAP_MAX_LOCATION_WITH_COLS = 0x60000000; > - > /* Highest possible source location encoded within an ordinary or > macro map. */ > const source_location LINE_MAP_MAX_SOURCE_LOCATION = 0x70000000;