/// @brief Remove the i-th element from the map.
///
+ /// If the index is out of bounds, nothing happens.
+ ///
/// @param i the position of the element you want to remove
void remove(int const i) override {
- auto it(m.begin());
- std::advance(it, i);
- m.erase(it);
+ if (i >= 0 && static_cast<size_t>(i) < m.size()) {
+ auto it(m.begin());
+ std::advance(it, i);
+ m.erase(it);
+ }
}
bool contains(const std::string& s) const override {
EXPECT_EQ("{ \"value\": None }", el->str());
}
+// Verifies that MapElement::remove(int) is a no-op for out-of-range indices
+// rather than invoking undefined behavior (Gitlab #4637).
+TEST(Element, mapElementRemoveOutOfRange) {
+ ElementPtr el = Element::fromJSON("{ \"a\": 1, \"b\": 2 }");
+ ASSERT_EQ(2, static_cast<int>(el->size()));
+
+ // Index past the end should be a no-op.
+ EXPECT_NO_THROW(el->remove(5));
+ EXPECT_EQ(2, static_cast<int>(el->size()));
+ EXPECT_EQ("{ \"a\": 1, \"b\": 2 }", el->str());
+
+ // Index equal to size should be a no-op.
+ EXPECT_NO_THROW(el->remove(2));
+ EXPECT_EQ(2, static_cast<int>(el->size()));
+ EXPECT_EQ("{ \"a\": 1, \"b\": 2 }", el->str());
+
+ // Negative index should be a no-op.
+ EXPECT_NO_THROW(el->remove(-1));
+ EXPECT_EQ(2, static_cast<int>(el->size()));
+ EXPECT_EQ("{ \"a\": 1, \"b\": 2 }", el->str());
+
+ // Empty map: any remove should be a no-op.
+ ElementPtr empty = Element::createMap();
+ EXPECT_NO_THROW(empty->remove(0));
+ EXPECT_TRUE(empty->empty());
+
+ // In-range remove still works (ordered map: index 0 is key "a").
+ EXPECT_NO_THROW(el->remove(0));
+ EXPECT_EQ(1, static_cast<int>(el->size()));
+ EXPECT_EQ(2, el->get("b")->intValue());
+ EXPECT_TRUE(isNull(el->get("a")));
+}
+
TEST(Element, toAndFromWire) {
// Wire format is now plain JSON.
EXPECT_EQ("1", Element::create(1)->toWire());