diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 40e34dd884c..c17f3884378 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,33 @@ +2016-07-11 David Malcolm + + * 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. + 2016-07-11 H.J. Lu PR target/71801 diff --git a/gcc/input.c b/gcc/input.c index 3fb4a2586b0..a916597a78c 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,172 @@ 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); + cpp_destroy (parser); +} + +/* 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 c6becdd01df..967e76bd2c8 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -129,13 +129,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/ChangeLog b/gcc/testsuite/ChangeLog index 4bfbba596fd..b4ddd499777 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-07-11 David Malcolm + + * 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. + 2016-07-11 H.J. Lu PR target/71801 diff --git a/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c b/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c index 1c140d81f51..3644d9fd82e 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/ChangeLog b/libcpp/ChangeLog index 7695a425911..168e08c15cf 100644 --- a/libcpp/ChangeLog +++ b/libcpp/ChangeLog @@ -1,3 +1,12 @@ +2016-07-11 David Malcolm + + * 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. + 2016-06-22 David Malcolm * directives.c (do_include_common): Pass on "location" to diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index 416419c59d9..43576761e63 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 8dea0d381c5..141af9d2cde 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;