/* class json::value. */
/* Dump this json::value tree to OUTF.
- No formatting is done. There are no guarantees about the order
- in which the key/value pairs of json::objects are printed. */
+
+ No formatting is done.
+
+ The key/value pairs of json::objects are printed in the order
+ in which the keys were originally inserted. */
void
value::dump (FILE *outf) const
}
/* class json::object, a subclass of json::value, representing
- an unordered collection of key/value pairs. */
+ an ordered collection of key/value pairs. */
/* json:object's dtor. */
void
object::print (pretty_printer *pp) const
{
- /* Note that the order is not guaranteed. */
pp_character (pp, '{');
- for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+
+ /* Iterate in the order that the keys were inserted. */
+ unsigned i;
+ const char *key;
+ FOR_EACH_VEC_ELT (m_keys, i, key)
{
- if (it != m_map.begin ())
+ if (i > 0)
pp_string (pp, ", ");
- const char *key = const_cast <char *>((*it).first);
- value *value = (*it).second;
+ map_t &mut_map = const_cast<map_t &> (m_map);
+ value *value = *mut_map.get (key);
pp_doublequote (pp);
pp_string (pp, key); // FIXME: escaping?
pp_doublequote (pp);
*ptr = v;
}
else
- /* If the key wasn't already present, take a copy of the key,
- and store the value. */
- m_map.put (xstrdup (key), v);
+ {
+ /* If the key wasn't already present, take a copy of the key,
+ and store the value. */
+ char *owned_key = xstrdup (key);
+ m_map.put (owned_key, v);
+ m_keys.safe_push (owned_key);
+ }
}
/* Get the json::value * for KEY.
ASSERT_EQ (obj.get ("not-present"), NULL);
}
-/* Verify that JSON objects are written correctly. We can't test more than
- one key/value pair, as we don't impose a guaranteed ordering. */
+/* Verify that JSON objects are written correctly. */
static void
test_writing_objects ()
{
object obj;
obj.set ("foo", new json::string ("bar"));
- assert_print_eq (obj, "{\"foo\": \"bar\"}");
+ obj.set ("baz", new json::string ("quux"));
+ /* This test relies on json::object writing out key/value pairs
+ in key-insertion order. */
+ assert_print_eq (obj, "{\"foo\": \"bar\", \"baz\": \"quux\"}");
}
/* Verify that JSON arrays are written correctly. */
void dump (FILE *) const;
};
-/* Subclass of value for objects: an unordered collection of
- key/value pairs. */
+/* Subclass of value for objects: a collection of key/value pairs
+ preserving the ordering in which keys were inserted.
+
+ Preserving the order eliminates non-determinism in the output,
+ making it easier for the user to compare repeated invocations. */
class object : public value
{
typedef hash_map <char *, value *,
simple_hashmap_traits<nofree_string_hash, value *> > map_t;
map_t m_map;
+
+ /* Keep track of order in which keys were inserted. */
+ auto_vec <const char *> m_keys;
};
/* Subclass of value for arrays. */
#error message
/* Use dg-regexp to consume the JSON output starting with
- the innermost values, and working outwards.
- We can't rely on any ordering of the keys. */
+ the innermost values, and working outwards. */
/* { dg-regexp "\"kind\": \"error\"" } */
/* { dg-regexp "\"column-origin\": 1" } */
#warning message
/* Use dg-regexp to consume the JSON output starting with
- the innermost values, and working outwards.
- We can't rely on any ordering of the keys. */
+ the innermost values, and working outwards. */
/* { dg-regexp "\"kind\": \"warning\"" } */
/* { dg-regexp "\"column-origin\": 1" } */
#warning message
/* Use dg-regexp to consume the JSON output starting with
- the innermost values, and working outwards.
- We can't rely on any ordering of the keys. */
+ the innermost values, and working outwards. */
/* { dg-regexp "\"kind\": \"error\"" } */
/* { dg-regexp "\"column-origin\": 1" } */
}
/* Use dg-regexp to consume the JSON output starting with
- the innermost values, and working outwards.
- We can't rely on any ordering of the keys. */
+ the innermost values, and working outwards. */
/* Verify nested diagnostics. */
return ptr->colour;
}
-/* Use dg-regexp to consume the JSON output starting with
- the innermost values, and working outwards.
- We can't rely on any ordering of the keys. */
+/* Verify fix-it hints.
-/* { dg-regexp "\"kind\": \"error\"" } */
-/* { dg-regexp "\"column-origin\": 1" } */
-/* { dg-regexp "\"escape-source\": false" } */
-/* { dg-regexp "\"message\": \".*\"" } */
+ Use dg-regexp to consume the JSON output from start to
+ finish, relying on the ordering of the keys.
+ The following uses indentation to visualize the structure
+ of the JSON (although the actual output is all on one line).
-/* Verify fix-it hints. */
-
-/* { dg-regexp "\"string\": \"color\"" } */
-
-/* { dg-regexp "\"start\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
-/* { dg-regexp "\"line\": 8" } */
-/* { dg-regexp "\"column\": 15" } */
-/* { dg-regexp "\"display-column\": 15" } */
-/* { dg-regexp "\"byte-column\": 15" } */
-
-/* { dg-regexp "\"next\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
-/* { dg-regexp "\"line\": 8" } */
-/* { dg-regexp "\"column\": 21" } */
-/* { dg-regexp "\"display-column\": 21" } */
-/* { dg-regexp "\"byte-column\": 21" } */
-
-/* { dg-regexp "\"fixits\": \[\[\{\}, \]*\]" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
-/* { dg-regexp "\"line\": 8" } */
-/* { dg-regexp "\"column\": 15" } */
-/* { dg-regexp "\"display-column\": 15" } */
-/* { dg-regexp "\"byte-column\": 15" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
-/* { dg-regexp "\"line\": 8" } */
-/* { dg-regexp "\"column\": 20" } */
-/* { dg-regexp "\"display-column\": 20" } */
-/* { dg-regexp "\"byte-column\": 20" } */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
-/* { dg-regexp "\[\[\{\}, \]*\]" } */
+ { dg-regexp {\[} }
+ { dg-regexp {\{} }
+ { dg-regexp {"kind": "error"} }
+ { dg-regexp {, "message": "'struct s' has no member named 'colour'; did you mean 'color'\?"} }
+ { dg-regexp {, "children": \[\]} }
+ { dg-regexp {, "column-origin": 1} }
+ { dg-regexp {, "locations": } }
+ { dg-regexp {\[} }
+ { dg-regexp {\{} }
+ { dg-regexp {"caret": } }
+ { dg-regexp {\{} }
+ { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
+ { dg-regexp {, "line": 8} }
+ { dg-regexp {, "display-column": 15} }
+ { dg-regexp {, "byte-column": 15} }
+ { dg-regexp {, "column": 15} }
+ { dg-regexp {\}} }
+ { dg-regexp {, "finish": } }
+ { dg-regexp {\{} }
+ { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
+ { dg-regexp {, "line": 8} }
+ { dg-regexp {, "display-column": 20} }
+ { dg-regexp {, "byte-column": 20} }
+ { dg-regexp {, "column": 20} }
+ { dg-regexp {\}} }
+ { dg-regexp {\}} }
+ { dg-regexp {\]} }
+ { dg-regexp {, "fixits": } }
+ { dg-regexp {\[} }
+ { dg-regexp {\{} }
+ { dg-regexp {"start": } }
+ { dg-regexp {\{} }
+ { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
+ { dg-regexp {, "line": 8} }
+ { dg-regexp {, "display-column": 15} }
+ { dg-regexp {, "byte-column": 15} }
+ { dg-regexp {, "column": 15} }
+ { dg-regexp {\}} }
+ { dg-regexp {, "next": } }
+ { dg-regexp {\{} }
+ { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
+ { dg-regexp {, "line": 8} }
+ { dg-regexp {, "display-column": 21} }
+ { dg-regexp {, "byte-column": 21} }
+ { dg-regexp {, "column": 21} }
+ { dg-regexp {\}} }
+ { dg-regexp {, "string": "color"} }
+ { dg-regexp {\}} }
+ { dg-regexp {\]} }
+ { dg-regexp {, "escape-source": false\}} }
+ { dg-regexp {\]} } */
#error message
/* Use dg-regexp to consume the JSON output starting with
- the innermost values, and working outwards.
- We can't rely on any ordering of the keys. */
+ the innermost values, and working outwards. */
/* { dg-regexp "\"kind\": \"error\"" } */
/* { dg-regexp "\"column-origin\": 1" } */