diff --git a/scripts/cpplint.py b/scripts/cpplint.py index d34fd63f6b8..ac39bcc34d6 100755 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -3928,60 +3928,6 @@ def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): """ line = clean_lines.elided[linenum] - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces when they are delimiting blocks, classes, namespaces etc. - # And since you should never have braces at the beginning of a line, - # this is an easy test. Except that braces used for initialization don't - # follow the same rule; we often don't want spaces before those. - match = Match(r'^(.*[^ ({>]){', line) - - if match: - # Try a bit harder to check for brace initialization. This - # happens in one of the following forms: - # Constructor() : initializer_list_{} { ... } - # Constructor{}.MemberFunction() - # Type variable{}; - # FunctionCall(type{}, ...); - # LastArgument(..., type{}); - # LOG(INFO) << type{} << " ..."; - # map_of_type[{...}] = ...; - # ternary = expr ? new type{} : nullptr; - # OuterTemplate{}> - # - # We check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<>]:". - # - # To account for nested initializer list, we allow any number of - # closing braces up to "{;,)<". We can't simply silence the - # warning on first sight of closing brace, because that would - # cause false negatives for things that are not initializer lists. - # Silence this: But not this: - # Outer{ if (...) { - # Inner{...} if (...){ // Missing space before { - # }; } - # - # There is a false negative with this approach if people inserted - # spurious semicolons, e.g. "if (cond){};", but we will catch the - # spurious semicolon with a separate check. - leading_text = match.group(1) - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - trailing_text = '' - if endpos > -1: - trailing_text = endline[endpos:] - for offset in xrange(endlinenum + 1, - min(endlinenum + 3, clean_lines.NumLines() - 1)): - trailing_text += clean_lines.elided[offset] - # We also suppress warnings for `uint64_t{expression}` etc., as the style - # guide recommends brace initialization for integral types to avoid - # overflow/truncation. - if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) - and not _IsType(clean_lines, nesting_state, leading_text)): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. diff --git a/src/util/json.cpp b/src/util/json.cpp index 5fa037273fa..5b1e3610a0d 100644 --- a/src/util/json.cpp +++ b/src/util/json.cpp @@ -9,6 +9,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "json.h" #include +#include const jsont jsont::null_json_object(jsont::kindt::J_NULL); @@ -162,3 +163,44 @@ void jsont::swap(jsont &other) other.object.swap(object); other.value.swap(value); } + +bool operator==(const jsont &left, const jsont &right) +{ + if(left.kind != right.kind) + return false; + switch(left.kind) + { + case jsont::kindt::J_NULL: + return true; + case jsont::kindt::J_TRUE: + return true; + case jsont::kindt::J_FALSE: + return true; + case jsont::kindt::J_NUMBER: + return left.value == right.value; + case jsont::kindt::J_STRING: + return left.value == right.value; + case jsont::kindt::J_ARRAY: + { + const auto &left_array = static_cast(left); + const auto &right_array = static_cast(right); + return left_array.size() == right_array.size() && + std::equal( + left_array.begin(), left_array.end(), right_array.begin()); + } + case jsont::kindt::J_OBJECT: + { + const auto &left_object = static_cast(left); + const auto &right_object = static_cast(right); + if(left_object.size() != left_object.size()) + return false; + return std::all_of( + left_object.begin(), + left_object.end(), + [&](const std::pair &pair) { + return right_object[pair.first] == pair.second; + }); + } + } + UNREACHABLE; +} diff --git a/src/util/json.h b/src/util/json.h index 2f9db3b888a..c10f61c12bb 100644 --- a/src/util/json.h +++ b/src/util/json.h @@ -361,6 +361,11 @@ class json_objectt:public jsont return object.find(key); } + std::size_t size() const + { + return object.size(); + } + iterator begin() { return object.begin(); @@ -458,4 +463,6 @@ inline const json_stringt &to_json_string(const jsont &json) return static_cast(json); } +bool operator==(const jsont &left, const jsont &right); + #endif // CPROVER_UTIL_JSON_H diff --git a/unit/json/json_parser.cpp b/unit/json/json_parser.cpp index 5a78390d326..0c4b1659eb9 100644 --- a/unit/json/json_parser.cpp +++ b/unit/json/json_parser.cpp @@ -133,3 +133,110 @@ SCENARIO("Loading JSON files") } } } + +TEST_CASE("json equality", "[core][util][json]") +{ + SECTION("null") + { + REQUIRE(jsont::null_json_object == jsont::null_json_object); + } + + SECTION("boolean") + { + REQUIRE(jsont::json_boolean(false) == jsont::json_boolean(false)); + REQUIRE(jsont::json_boolean(true) == jsont::json_boolean(true)); + REQUIRE_FALSE(jsont::json_boolean(true) == jsont::json_boolean(false)); + REQUIRE_FALSE(jsont::json_boolean(false) == jsont::null_json_object); + } + + SECTION("number") + { + REQUIRE(json_numbert("0") == json_numbert("0")); + REQUIRE(json_numbert("1") == json_numbert("1")); + REQUIRE(json_numbert("-1") == json_numbert("-1")); + REQUIRE(json_numbert("1.578") == json_numbert("1.578")); + REQUIRE_FALSE(json_numbert("0") == json_numbert("1")); + REQUIRE_FALSE(json_numbert("1") == json_numbert("-1")); + REQUIRE_FALSE(json_numbert("-1") == json_numbert("1")); + REQUIRE_FALSE(json_numbert("1.578") == json_numbert("1.5789")); + REQUIRE_FALSE(json_numbert("0") == jsont::json_boolean(false)); + REQUIRE_FALSE(jsont::json_boolean(false) == json_numbert("0")); + REQUIRE_FALSE(json_numbert("0") == jsont::null_json_object); + REQUIRE_FALSE(jsont::null_json_object == json_numbert("0")); + } + + SECTION("string") + { + REQUIRE(json_stringt("") == json_stringt("")); + REQUIRE(json_stringt("foo") == json_stringt("foo")); + REQUIRE(json_stringt("bar") == json_stringt("bar")); + REQUIRE_FALSE(json_stringt("foo") == json_stringt("bar")); + REQUIRE_FALSE(json_stringt("bar") == json_stringt("baz")); + REQUIRE_FALSE(json_stringt("foo") == json_stringt("food")); + REQUIRE_FALSE(json_stringt("1") == json_numbert("1")); + REQUIRE_FALSE(json_stringt("true") == jsont::json_boolean("true")); + REQUIRE_FALSE(json_stringt("") == jsont::json_boolean("false")); + REQUIRE_FALSE(json_stringt("") == jsont::null_json_object); + } + + SECTION("array") + { + REQUIRE(json_arrayt{} == json_arrayt{}); + REQUIRE( + json_arrayt{jsont::null_json_object} == + json_arrayt{jsont::null_json_object}); + REQUIRE( + json_arrayt{json_numbert{"9"}, json_numbert{"6"}} == + json_arrayt{json_numbert{"9"}, json_numbert{"6"}}); + REQUIRE( + json_arrayt{ + json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}} == + json_arrayt{ + json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}}); + + // different lengths + REQUIRE_FALSE( + json_arrayt{json_stringt{"foo"}, json_stringt{"bar"}} == + json_arrayt{ + json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}}); + // different elements + REQUIRE_FALSE( + json_arrayt{ + json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"foo"}} == + json_arrayt{ + json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}}); + // different kind + REQUIRE_FALSE(json_arrayt{} == jsont::json_boolean(false)); + REQUIRE_FALSE(json_arrayt{} == jsont::null_json_object); + } + + SECTION("object") + { + REQUIRE(json_objectt{} == json_objectt{}); + REQUIRE( + json_objectt{{"key", json_stringt{"value"}}} == + json_objectt{{"key", json_stringt{"value"}}}); + REQUIRE( + json_objectt{{"key1", json_stringt{"value1"}}, + {"key2", json_stringt{"value2"}}} == + json_objectt{{"key1", json_stringt{"value1"}}, + {"key2", json_stringt{"value2"}}}); + + // Extra property + REQUIRE_FALSE( + json_objectt{{"key1", json_stringt{"value1"}}, + {"key2", json_stringt{"value2"}}, + {"key3", json_stringt{"value3"}}} == + json_objectt{{"key1", json_stringt{"value1"}}, + {"key2", json_stringt{"value2"}}}); + // different field values + REQUIRE_FALSE( + json_objectt{{"key1", json_stringt{"foo"}}, + {"key2", json_stringt{"bar"}}} == + json_objectt{{"key1", json_stringt{"foo"}}, + {"key2", json_stringt{"baz"}}}); + // different kind + REQUIRE_FALSE(json_objectt{} == json_arrayt{}); + REQUIRE_FALSE(json_objectt{} == jsont::null_json_object); + } +}