diffblue-cbmc/unit/compound_block_locations.cpp

352 lines
12 KiB
C++

/*******************************************************************\
Module: Test that compound blocks get a location
Author: Kareem Khazem <karkhaz@karkhaz.com>, 2018
\*******************************************************************/
#include "compound_block_locations.h"
#include <testing-utils/use_catch.h>
#include <fstream>
#include <utility>
#include <ansi-c/ansi_c_language.h>
#include <cbmc/cbmc_parse_options.h>
#include <goto-instrument/goto_program2code.h>
#include <goto-programs/goto_model.h>
#include <langapi/mode.h>
#include <util/cmdline.h>
#include <util/config.h>
#include <util/cout_message.h>
#include <util/options.h>
#include <util/tempfile.h>
SCENARIO("Compound blocks should have a location")
{
compound_block_locationst checker;
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ x = 1; \n"
"/* 6 */ } \n",
{{ID_ifthenelse, 4}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ { \n"
"/* 6 */ x = 1; \n"
"/* 7 */ x = 1; \n"
"/* 8 */ } \n"
"/* 9 */ } \n",
{{ID_ifthenelse, 4}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ x = 1; \n"
"/* 6 */ else \n"
"/* 7 */ x = 1; \n"
"/* 8 */ } \n",
{{ID_ifthenelse, 4}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ x = 1; \n"
"/* 6 */ else if(x == 2) \n"
"/* 7 */ { \n"
"/* 8 */ x = 1; \n"
"/* 9 */ x = 1; \n"
"/* 10 */ } \n"
"/* 11 */ else \n"
"/* 12 */ x = 1; \n"
"/* 13 */ } \n",
{{ID_ifthenelse, 4}, {ID_ifthenelse, 6}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ while(1) \n"
"/* 4 */ { \n"
"/* 5 */ int x = 2; \n"
"/* 6 */ } \n"
"/* 7 */ } \n",
{{ID_while, 3}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ while(1) \n"
"/* 4 */ { \n"
"/* 5 */ while(1) \n"
"/* 5 */ { \n"
"/* 6 */ int x = 1; \n"
"/* 7 */ } \n"
"/* 8 */ } \n"
"/* 9 */ } \n",
{{ID_while, 3}, {ID_while, 5}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ while(1) \n"
"/* 4 */ { \n"
"/* 5 */ int x; \n"
"/* 6 */ if(x) \n"
"/* 7 */ int x = 1; \n"
"/* 8 */ } \n"
"/* 9 */ } \n",
{{ID_while, 3}, {ID_ifthenelse, 6}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ { \n"
"/* 6 */ while(1) \n"
"/* 7 */ { \n"
"/* 8 */ int y = 1; \n"
"/* 9 */ } \n"
"/* 10 */ } \n"
"/* 11 */ } \n",
{{ID_ifthenelse, 4}, {ID_while, 6}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ { \n"
"/* 6 */ while(1) \n"
"/* 7 */ { \n"
"/* 8 */ int y = 1; \n"
"/* 9 */ } \n"
"/* 10 */ } \n"
"/* 11 */ else \n"
"/* 12 */ { \n"
"/* 13 */ while(1) \n"
"/* 14 */ { \n"
"/* 15 */ while(1) \n"
"/* 16 */ { \n"
"/* 17 */ int y = 1; \n"
"/* 18 */ } \n"
"/* 19 */ } \n"
"/* 20 */ } \n"
"/* 21 */ } \n",
{{ID_ifthenelse, 4}, {ID_while, 6}, {ID_while, 13}, {ID_while, 15}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ for(int i = 0; i < 1; ++i) \n"
"/* 4 */ { \n"
"/* 5 */ int x; \n"
"/* 6 */ } \n"
"/* 7 */ } \n",
{{ID_for, 3}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ for(int i = 0; i < 1; ++i) \n"
"/* 4 */ { \n"
"/* 5 */ int x; \n"
"/* 6 */ for(int j = 0; j < 3; ++j) \n"
"/* 7 */ { \n"
"/* 8 */ int y; \n"
"/* 9 */ } \n"
"/* 10 */ } \n"
"/* 11 */ } \n",
{{ID_for, 3}, {ID_for, 6}});
// In the absence of a better place to put it, the source location of a
// do-while block is equal to the source location of its first statement.
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ do \n"
"/* 4 */ { \n"
"/* 5 */ int x; \n"
"/* 6 */ } while(1); \n"
"/* 7 */ } \n",
{{ID_dowhile, 5}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ do \n"
"/* 4 */ { \n"
"/* 5 */ int x; \n"
"/* 6 */ int y; \n"
"/* 7 */ int z = x + y; \n"
"/* 8 */ } while(1); \n"
"/* 9 */ } \n",
{{ID_dowhile, 5}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ do \n"
"/* 5 */ { \n"
"/* 6 */ int x; \n"
"/* 7 */ int y; \n"
"/* 8 */ int z = x + y; \n"
"/* 9 */ } while(x); \n"
"/* 10 */ } \n",
{{ID_dowhile, 6}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ if(x) \n"
"/* 5 */ { \n"
"/* 6 */ do \n"
"/* 7 */ { \n"
"/* 8 */ int y; \n"
"/* 9 */ } while(x); \n"
"/* 10 */ } \n"
"/* 11 */ } \n",
{{ID_ifthenelse, 4}, {ID_dowhile, 8}});
checker.check(
"/* 1 */ int main() \n"
"/* 2 */ { \n"
"/* 3 */ int x; \n"
"/* 4 */ switch(x) \n"
"/* 5 */ { \n"
"/* 6 */ case 1: \n"
"/* 7 */ int y = 1; \n"
"/* 8 */ break; \n"
"/* 9 */ case 2: \n"
"/* 10 */ int y = 2; \n"
"/* 11 */ int z = 2; \n"
"/* 12 */ break; \n"
"/* 13 */ case 3: \n"
"/* 14 */ int y = 3; \n"
"/* 15 */ case 4: \n"
"/* 16 */ int y = 4; \n"
"/* 17 */ break; \n"
"/* 18 */ default: \n"
"/* 19 */ int y = 5; \n"
"/* 20 */ } \n"
"/* 21 */ } \n",
{{ID_switch, 6}});
}
void compound_block_locationst::check(
const std::string &program,
const std::list<std::pair<const irep_idt, const unsigned>> &expected)
{
INFO("Given the following program:\n" + program);
check_compound_block_locations(program, expected);
}
void compound_block_locationst::check_compound_block_locations(
const std::string &program,
const std::list<std::pair<const irep_idt, const unsigned>> &expected)
{
temporary_filet tmp("compound-block-locations_", ".c");
std::ofstream of(tmp().c_str());
REQUIRE(of.is_open());
of << program << std::endl;
of.close();
register_language(new_ansi_c_language);
cmdlinet cmdline;
cmdline.args.push_back(tmp());
config.main = std::string("main");
config.set(cmdline);
optionst opts;
cbmc_parse_optionst::set_default_options(opts);
ui_message_handlert mh(cmdline, "compound-block-locations");
mh.set_verbosity(0);
messaget log(mh);
goto_modelt gm;
int ret;
ret = cbmc_parse_optionst::get_goto_program(gm, opts, cmdline, mh);
REQUIRE(ret == -1);
const auto found = gm.goto_functions.function_map.find("main");
REQUIRE(found != gm.goto_functions.function_map.end());
code_blockt block;
symbol_tablet st_copy(gm.symbol_table);
std::list<irep_idt> local_static, type_names;
std::unordered_set<irep_idt> typedef_names;
std::set<std::string> system_headers;
goto_program2codet g2c(
"main",
found->second.body,
st_copy,
block,
local_static,
type_names,
typedef_names,
system_headers);
g2c();
std::list<std::pair<const irep_idt, const unsigned>> copy(expected);
recurse_on_block(block, copy);
std::stringstream ss;
ss << "Expected but did not observe the following lines to have blocks with "
<< "locations: ";
for(const auto &pair : copy)
ss << "'" << types.at(pair.first) << "' at line "
<< std::to_string(pair.second) << ", ";
INFO(ss.str());
REQUIRE(copy.empty());
}
void compound_block_locationst::recurse_on_block(
const exprt &expr,
std::list<std::pair<const irep_idt, const unsigned>> &expected)
{
if(expr.id() == ID_code)
{
const codet code = to_code(expr);
for(const auto &pair : types)
{
if(code.get_statement() != pair.first)
continue;
INFO("Found an instruction of type " + pair.second);
const auto &top = expected.front();
REQUIRE(code.get_statement() == top.first);
const source_locationt &loc = code.source_location();
REQUIRE(loc.is_not_nil());
REQUIRE(std::stoul(loc.get_line().c_str()) == top.second);
expected.pop_front();
break;
}
}
for(const auto &child : expr.operands())
recurse_on_block(child, expected);
}