Skip to content

Commit f337e41

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

File tree

4 files changed

+165
-4
lines changed

4 files changed

+165
-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: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "symbol_table.h"
44

55
#include <util/invariant.h>
6+
#include <util/validate.h>
67

78
/// Move or copy a new symbol to the symbol table
89
/// \remark: This is a nicer interface than move and achieves the same
@@ -114,3 +115,94 @@ void symbol_tablet::erase(const symbolst::const_iterator &entry)
114115

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

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)