Skip to content

Commit a622be9

Browse files
Add validity checks for symbol table mappings
1 parent c728f63 commit a622be9

File tree

4 files changed

+170
-4
lines changed

4 files changed

+170
-4
lines changed

src/goto-programs/goto_model.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class goto_modelt : public abstract_goto_modelt
103103
void validate(const namespacet &ns, const validation_modet vm) const
104104
{
105105
goto_functions.validate(ns, vm);
106-
symbol_table.validate();
106+
symbol_table.validate(vm);
107107
}
108108
};
109109

src/util/symbol_table.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
#include "symbol_table.h"
44

5+
#include <algorithm>
56
#include <util/invariant.h>
7+
#include <util/validate.h>
68

79
/// Move or copy a new symbol to the symbol table
810
/// \remark: This is a nicer interface than move and achieves the same
@@ -114,3 +116,98 @@ void symbol_tablet::erase(const symbolst::const_iterator &entry)
114116

115117
internal_symbols.erase(entry);
116118
}
119+
120+
/// Check whether the symbol table is in a valid state
121+
/// \param vm Determine whether to throw exceptions or trigger INVARIANT
122+
/// when validation fails
123+
void symbol_tablet::validate(const validation_modet vm) const
124+
{
125+
// Check that identifiers are mapped to the correct symbol
126+
for(const auto &elem : symbols)
127+
{
128+
const auto symbol_key = elem.first;
129+
const auto &symbol = elem.second;
130+
131+
// Check that symbols[id].name == id
132+
DATA_CHECK_WITH_DIAGNOSTICS(
133+
symbol.name == symbol_key,
134+
"Symbol table entry must map to a symbol with the correct identifier",
135+
"Symbol table key '",
136+
symbol_key,
137+
"' maps to symbol '",
138+
symbol.name,
139+
"'");
140+
141+
// Check that the symbol basename is mapped to its full name
142+
if(!symbol.base_name.empty())
143+
{
144+
const auto base_map_search =
145+
symbol_base_map.equal_range(symbol.base_name);
146+
const bool base_map_matches_symbol =
147+
std::find_if(
148+
base_map_search.first,
149+
base_map_search.second,
150+
[&symbol_key](const typename symbol_base_mapt::value_type &match) {
151+
return match.second == symbol_key;
152+
}) != symbol_base_map.end();
153+
154+
DATA_CHECK_WITH_DIAGNOSTICS(
155+
base_map_matches_symbol,
156+
"The base_name of a symbol should map to itself",
157+
"Symbol table key '",
158+
symbol_key,
159+
"' has a base_name '",
160+
symbol.base_name,
161+
"' which does not map to itself");
162+
}
163+
164+
// Check that the module name of the symbol is mapped to the full name
165+
if(!symbol.module.empty())
166+
{
167+
auto module_map_search =
168+
symbol_module_map.equal_range(symbol.module);
169+
bool module_map_matches_symbol =
170+
std::find_if(
171+
module_map_search.first,
172+
module_map_search.second,
173+
[&symbol_key](const typename symbol_module_mapt::value_type &match) {
174+
return match.second == symbol_key;
175+
}) != symbol_module_map.end();
176+
177+
DATA_CHECK_WITH_DIAGNOSTICS(
178+
module_map_matches_symbol,
179+
"Symbol table module map should map to symbol",
180+
"Symbol table key '",
181+
symbol_key,
182+
"' has a module name of '",
183+
symbol.module,
184+
"' which does not map to itself");
185+
}
186+
}
187+
188+
// Check that all base name map entries point to a symbol entry
189+
for(auto base_map_entry : symbol_base_map)
190+
{
191+
DATA_CHECK_WITH_DIAGNOSTICS(
192+
has_symbol(base_map_entry.second),
193+
"Symbol table base_name map entries must map to a symbol name",
194+
"base_name map entry '",
195+
base_map_entry.first,
196+
"' maps to non-existant symbol name '",
197+
base_map_entry.second,
198+
"'");
199+
}
200+
201+
// Check that all module map entries point to a symbol entry
202+
for(auto module_map_entry : symbol_module_map)
203+
{
204+
DATA_CHECK_WITH_DIAGNOSTICS(
205+
has_symbol(module_map_entry.second),
206+
"Symbol table module map entries must map to a symbol name",
207+
"base_name map entry '",
208+
module_map_entry.first,
209+
"' maps to non-existant symbol name '",
210+
module_map_entry.second,
211+
"'");
212+
}
213+
}

src/util/symbol_table.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,7 @@ class symbol_tablet : public symbol_table_baset
111111
}
112112

113113
/// Check that the symbol table is well-formed
114-
void validate() const
115-
{
116-
}
114+
void validate(const validation_modet vm = validation_modet::INVARIANT) const;
117115
};
118116

119117
#endif // CPROVER_UTIL_SYMBOL_TABLE_H

unit/util/symbol_table.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/// \file Tests for symbol_tablet
44

55
#include <testing-utils/catch.hpp>
6+
#include <util/exception_utils.h>
67
#include <util/journalling_symbol_table.h>
78

89
TEST_CASE("Iterating through a symbol table", "[core][utils][symbol_tablet]")
@@ -24,6 +25,76 @@ TEST_CASE("Iterating through a symbol table", "[core][utils][symbol_tablet]")
2425
REQUIRE(counter == 1);
2526
}
2627

28+
SCENARIO(
29+
"symbol_table_validity_checks",
30+
"[core][utils][symbol_table_validity_checks]")
31+
{
32+
GIVEN("A valid symbol table")
33+
{
34+
symbol_tablet symbol_table;
35+
36+
symbolt symbol;
37+
irep_idt symbol_name = "Test";
38+
symbol.name = symbol_name;
39+
symbol.base_name = "TestBase";
40+
symbol.module = "TestModule";
41+
42+
symbol_table.insert(symbol);
43+
44+
THEN("validate() should return")
45+
{
46+
symbol_table.validate(validation_modet::EXCEPTION);
47+
}
48+
WHEN("A symbol name is transformed without updating the symbol table")
49+
{
50+
symbolt &transformed_symbol = symbol_table.get_writeable_ref(symbol_name);
51+
transformed_symbol.name = "TransformTest";
52+
53+
THEN("validate() should throw an exception")
54+
{
55+
REQUIRE_THROWS_AS(
56+
symbol_table.validate(validation_modet::EXCEPTION),
57+
incorrect_goto_program_exceptiont);
58+
}
59+
// Reset symbol to a valid name after the previous test
60+
transformed_symbol.name = symbol_name;
61+
}
62+
WHEN(
63+
"A symbol base_name is transformed without updating the base_name "
64+
"mapping")
65+
{
66+
symbolt &transformed_symbol = symbol_table.get_writeable_ref(symbol_name);
67+
// Transform the symbol
68+
transformed_symbol.base_name = "TransformTestBase";
69+
70+
THEN("validate() should throw an exception")
71+
{
72+
REQUIRE_THROWS_AS(
73+
symbol_table.validate(validation_modet::EXCEPTION),
74+
incorrect_goto_program_exceptiont);
75+
}
76+
// Reset symbol to a valid base_name after the previous test
77+
transformed_symbol.base_name = "TestBase";
78+
}
79+
WHEN(
80+
"A symbol module identifier is transformed without updating the module "
81+
"mapping")
82+
{
83+
symbolt &transformed_symbol = symbol_table.get_writeable_ref(symbol_name);
84+
transformed_symbol.module = "TransformTestModule";
85+
86+
THEN("validate() should throw an exception")
87+
{
88+
REQUIRE_THROWS_AS(
89+
symbol_table.validate(validation_modet::EXCEPTION),
90+
incorrect_goto_program_exceptiont);
91+
}
92+
// Reset symbol to a valid module name
93+
transformed_symbol.module = "TestModule";
94+
}
95+
}
96+
}
97+
2798
SCENARIO("journalling_symbol_table_writer",
2899
"[core][utils][journalling_symbol_table_writer]")
29100
{

0 commit comments

Comments
 (0)