From b31a2659664533b4662b7b2f6bea3fd02e47d1b6 Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Fri, 7 Aug 2026 17:09:53 +0800 Subject: [PATCH 1/9] test(immut/hashmap): failing tests for canonical structure after shrinking ops HashMap derives structural Eq, so every operation must leave the HAMT in the canonical shape a fresh construction of the same content would produce. The shrinking operations violate this today, making content-equal maps compare unequal: - remove leaves Leaf(k, v, Empty) (instead of the collapsed Flat) when a full-hash collision bucket empties - filter never collapses singleton branches at all, so it is broken even for ordinary non-colliding keys: singleton(0,0).add(1,1).filter(k == 0) != singleton(0,0) - difference shares the bucket-shrinking path and fails the same way Found by the QuickCheck property suite in #3999, minimized to {Key(0): 0} != add(Key(24), 0).remove(Key(24)) with hash(k) = k & 7. These tests encode the canonical-structure contract and all fail; the fix lands in the next commit. Co-Authored-By: Claude Fable 5 --- immut/hashmap/canonical_structure_test.mbt | 98 ++++++++++++++++++++++ immut/hashset/canonical_structure_test.mbt | 56 +++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 immut/hashmap/canonical_structure_test.mbt create mode 100644 immut/hashset/canonical_structure_test.mbt diff --git a/immut/hashmap/canonical_structure_test.mbt b/immut/hashmap/canonical_structure_test.mbt new file mode 100644 index 000000000..be1838cbb --- /dev/null +++ b/immut/hashmap/canonical_structure_test.mbt @@ -0,0 +1,98 @@ +// Copyright 2026 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// `HashMap` derives structural `Eq`, so every operation must leave the tree +// in the canonical shape a fresh construction of the same content would +// have. These tests pin that contract for the shrinking operations +// (`remove`, `filter`, `difference`): before the fix, they left +// non-canonical remnants — an uncollapsed singleton branch after `filter`, +// and a `Leaf(k, v, Empty)` node (instead of the collapsed `Flat`) after +// removing from a full-hash collision bucket — making content-equal maps +// compare unequal. +// +// Found by the QuickCheck property suite in #3999. + +///| +/// Keys whose 32-bit hash keeps only the low three bits, so keys that agree +/// on `n & 7` collide on the full hash while `Eq` still distinguishes them. +priv struct CollidingKey(Int) derive(Eq) + +///| +impl Hash for CollidingKey with fn hash(self) { + self.0 & 7 +} + +///| +impl Hash for CollidingKey with fn hash_combine(self, hasher) { + hasher.combine_int(self.0 & 7) +} + +///| +test "removing a colliding key restores the canonical structure" { + let original : @hashmap.HashMap[CollidingKey, Int] = @hashmap.singleton( + CollidingKey(0), + 0, + ) + let round_trip = original.add(CollidingKey(24), 0).remove(CollidingKey(24)) + assert_true(round_trip == original) + assert_true(round_trip == @hashmap.singleton(CollidingKey(0), 0)) +} + +///| +test "filter down to one key yields the canonical map" { + // No hash collisions involved: `filter` must collapse the branch that + // shrank to a single entry, exactly like `remove` does. + let original : @hashmap.HashMap[Int, Int] = @hashmap.singleton(0, 0) + assert_true(original.add(1, 1).filter((k, _) => k == 0) == original) +} + +///| +test "filter dropping a colliding key yields the canonical map" { + let original : @hashmap.HashMap[CollidingKey, Int] = @hashmap.singleton( + CollidingKey(0), + 0, + ) + let filtered = original + .add(CollidingKey(24), 1) + .filter((k, _) => k == CollidingKey(0)) + assert_true(filtered == original) +} + +///| +test "difference removing a colliding key yields the canonical map" { + let original : @hashmap.HashMap[CollidingKey, Int] = @hashmap.singleton( + CollidingKey(0), + 0, + ) + let both = original.add(CollidingKey(24), 1) + let only_other : @hashmap.HashMap[CollidingKey, Int] = @hashmap.singleton( + CollidingKey(24), + 1, + ) + assert_true(both.difference(only_other) == original) +} + +///| +test "chained removals from a collision bucket collapse in any order" { + // 0, 8, and 24 all hash to bucket 0, forming a three-entry collision node. + let base : @hashmap.HashMap[CollidingKey, Int] = @hashmap.new() + let all = base + .add(CollidingKey(0), 0) + .add(CollidingKey(8), 1) + .add(CollidingKey(24), 2) + let one_way = all.remove(CollidingKey(8)).remove(CollidingKey(24)) + let other_way = all.remove(CollidingKey(24)).remove(CollidingKey(8)) + assert_true(one_way == @hashmap.singleton(CollidingKey(0), 0)) + assert_true(one_way == other_way) +} diff --git a/immut/hashset/canonical_structure_test.mbt b/immut/hashset/canonical_structure_test.mbt new file mode 100644 index 000000000..30b7ad125 --- /dev/null +++ b/immut/hashset/canonical_structure_test.mbt @@ -0,0 +1,56 @@ +// Copyright 2026 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// `HashSet` shares the HAMT design (and its structural `Eq`) with +// `immut/hashmap`, so shrinking operations must likewise collapse to the +// canonical shape a fresh construction would have. Before the fix, +// removing an element from a full-hash collision bucket left a +// non-canonical `Leaf(k, Empty)` node, making content-equal sets compare +// unequal; `difference` shares the same path. + +///| +/// Elements whose 32-bit hash keeps only the low three bits, so values +/// agreeing on `n & 7` collide on the full hash. +priv struct CollidingElem(Int) derive(Eq) + +///| +impl Hash for CollidingElem with fn hash(self) { + self.0 & 7 +} + +///| +impl Hash for CollidingElem with fn hash_combine(self, hasher) { + hasher.combine_int(self.0 & 7) +} + +///| +test "removing a colliding element restores the canonical structure" { + let original : @hashset.HashSet[CollidingElem] = HashSet([ + CollidingElem(0), + ]) + let round_trip = original.add(CollidingElem(24)).remove(CollidingElem(24)) + assert_true(round_trip == original) +} + +///| +test "difference removing a colliding element yields the canonical set" { + let original : @hashset.HashSet[CollidingElem] = HashSet([ + CollidingElem(0), + ]) + let both = original.add(CollidingElem(24)) + let only_other : @hashset.HashSet[CollidingElem] = HashSet([ + CollidingElem(24), + ]) + assert_true(both.difference(only_other) == original) +} From 8212ab842cde035436a9d47e3fa45e55410f14d3 Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Fri, 7 Aug 2026 17:15:24 +0800 Subject: [PATCH 2/9] fix(immut): collapse shrunken HAMT branches to canonical form HashMap and HashSet derive structural Eq, and the Node doc already states the invariant: a subtree holding a single entry must be represented as Flat. The shrinking operations violated it: - remove left Leaf(k, v, Empty) behind (instead of the collapsed Flat) when a full-hash collision bucket emptied, and the branch unwind only collapsed [Flat] singletons, so the non-canonical node stayed buried under a chain of singleton branches - filter never collapsed singleton branches at all, so it produced non-canonical trees even for ordinary non-colliding keys - intersection/intersection_with/difference shared both defects via their bucket-shrinking and branch-rebuilding paths Content-equal maps therefore compared unequal, e.g. {Key(0): 0} != add(Key(24), 0).remove(Key(24)) with hash(k) = k & 7. Fix: one collapse_branch helper per package, applied at every site that rebuilds a possibly-shrunk branch. It extends the existing [Flat] singleton collapse with the missing [Leaf(k, Empty)] case: a lone Leaf is always the terminal collision node whose remaining path is fully consumed, so its Flat path is rebuilt from the slot index alone, starting from the new Path::exhausted() (the head-tag remnant) in immut/internal/path. Union never shrinks and is untouched. No public API changes; only the internal path package's generated interface gains Path::exhausted. Co-Authored-By: Claude Fable 5 --- immut/hashmap/HAMT.mbt | 49 +++++++++++++++----------- immut/hashset/HAMT.mbt | 32 +++++++++++------ immut/internal/path/path.mbt | 8 +++++ immut/internal/path/pkg.generated.mbti | 1 + 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/immut/hashmap/HAMT.mbt b/immut/hashmap/HAMT.mbt index 248ff8e96..993a5d7e7 100644 --- a/immut/hashmap/HAMT.mbt +++ b/immut/hashmap/HAMT.mbt @@ -369,7 +369,7 @@ pub fn[K, V] HashMap::filter( Branch(children) => match children.filter(go) { None => None - Some(new_children) => Some(Branch(new_children)) + Some(new_children) => Some(collapse_branch(new_children)) } } } @@ -450,6 +450,29 @@ pub fn[K : Eq + Hash, V] HashMap::add( } } +///| +/// Rebuild a branch whose children may have shrunk, restoring the `Node` +/// invariant that a subtree holding a single entry is represented as +/// `Flat` (structural `Eq` relies on shapes being canonical). A lone +/// `Leaf` can only be a terminal collision node whose bucket emptied; its +/// remaining path is fully consumed, so the `Flat` path is rebuilt from +/// the slot index alone. +fn[K, V] collapse_branch( + children : @sparse_array.SparseArray[Node[K, V]], +) -> Node[K, V] { + match children.data { + [Flat(key, value, path)] => + Flat(key, value, path.push(children.elem_info.first_idx())) + [Leaf(key, value, Empty)] => + Flat( + key, + value, + @path.Path::exhausted().push(children.elem_info.first_idx()), + ) + _ => Branch(children) + } +} + ///| /// Remove an element from a map pub fn[K : Eq + Hash, V] HashMap::remove( @@ -499,17 +522,7 @@ fn[K : Eq, V] Node::remove_with_path( (_, None) => children.remove(idx) (_, Some(new_child)) => children.replace(idx, new_child) } - match new_children.data { - [Flat(key1, value1, path1)] => - Some( - Flat( - key1, - value1, - path1.push(new_children.elem_info.first_idx()), - ), - ) - _ => Some(Branch(new_children)) - } + Some(collapse_branch(new_children)) } } } @@ -662,9 +675,7 @@ pub fn[K : Eq, V] HashMap::intersection( (Branch(children1), Branch(children2)) => match children1.intersection(children2, go) { None => None - Some({ data: [Flat(key, value, path)], elem_info }) => - Some(Flat(key, value, path.push(elem_info.first_idx()))) - Some(children) => Some(Branch(children)) + Some(children) => Some(collapse_branch(children)) } (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { let kvs1 = bucket1.add((key1, value1)) @@ -708,9 +719,7 @@ pub fn[K : Eq, V] HashMap::intersection_with( (Branch(children1), Branch(children2)) => match children1.intersection(children2, go) { None => None - Some({ data: [Flat(key, value, path)], elem_info }) => - Some(Flat(key, value, path.push(elem_info.first_idx()))) - Some(children) => Some(Branch(children)) + Some(children) => Some(collapse_branch(children)) } (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { let kvs1 = bucket1.add((key1, value1)) @@ -767,9 +776,7 @@ pub fn[K : Eq, V] HashMap::difference( (Branch(children1), Branch(children2)) => match children1.difference(children2, go) { None => None - Some({ data: [Flat(key, value, path)], elem_info }) => - Some(Flat(key, value, path.push(elem_info.first_idx()))) - Some(children) => Some(Branch(children)) + Some(children) => Some(collapse_branch(children)) } (Leaf(key1, value1, bucket1), Leaf(key2, value2, bucket2)) => { let kvs1 = bucket1.add((key1, value1)) diff --git a/immut/hashset/HAMT.mbt b/immut/hashset/HAMT.mbt index be2e76dcd..f2a3467b7 100644 --- a/immut/hashset/HAMT.mbt +++ b/immut/hashset/HAMT.mbt @@ -305,6 +305,24 @@ pub fn[A : Eq + Hash] HashSet::remove(self : HashSet[A], key : A) -> HashSet[A] } } +///| + +///| +/// Rebuild a branch whose children may have shrunk, restoring the `Node` +/// invariant that a subtree holding a single element is represented as +/// `Flat` (structural `Eq` relies on shapes being canonical). A lone +/// `Leaf` can only be a terminal collision node whose bucket emptied; its +/// remaining path is fully consumed, so the `Flat` path is rebuilt from +/// the slot index alone. +fn[A] collapse_branch(children : @sparse_array.SparseArray[Node[A]]) -> Node[A] { + match children.data { + [Flat(key, path)] => Flat(key, path.push(children.elem_info.first_idx())) + [Leaf(key, Empty)] => + Flat(key, @path.Path::exhausted().push(children.elem_info.first_idx())) + _ => Branch(children) + } +} + ///| fn[A : Eq] Node::remove_with_path( self : Node[A], @@ -340,11 +358,7 @@ fn[A : Eq] Node::remove_with_path( (_, None) => children.remove(idx) (_, Some(new_child)) => children.replace(idx, new_child) } - match new_children.data { - [Flat(key1, path1)] => - Some(Flat(key1, path1.push(new_children.elem_info.first_idx()))) - _ => Some(Branch(new_children)) - } + Some(collapse_branch(new_children)) } } } @@ -425,9 +439,7 @@ pub fn[K : Eq] HashSet::intersection( (Branch(children1), Branch(children2)) => match children1.intersection(children2, go) { None => None - Some({ data: [Flat(key, path)], elem_info }) => - Some(Flat(key, path.push(elem_info.first_idx()))) - Some(children) => Some(Branch(children)) + Some(children) => Some(collapse_branch(children)) } (Leaf(key1, bucket1), Leaf(key2, bucket2)) => { let keys1 = bucket1.add(key1) @@ -467,9 +479,7 @@ pub fn[K : Eq] HashSet::difference( (Branch(children1), Branch(children2)) => match children1.difference(children2, go) { None => None - Some({ data: [Flat(key, path)], elem_info }) => - Some(Flat(key, path.push(elem_info.first_idx()))) - Some(children) => Some(Branch(children)) + Some(children) => Some(collapse_branch(children)) } (Leaf(key1, bucket1), Leaf(key2, bucket2)) => { let keys1 = bucket1.add(key1) diff --git a/immut/internal/path/path.mbt b/immut/internal/path/path.mbt index 9fd7bbb8a..50ad108ac 100644 --- a/immut/internal/path/path.mbt +++ b/immut/internal/path/path.mbt @@ -89,3 +89,11 @@ pub fn Path::advance(self : Path, depth : Int) -> Path { let Path(self) = self self >> (SEGMENT_LENGTH * depth) } + +///| +/// The path that remains once every index segment has been consumed: just +/// the head tag bits. This is the remaining path of a terminal collision +/// node, and the starting point for rebuilding a full path with `push`. +pub fn Path::exhausted() -> Path { + Path(HEAD_TAG >> (SEGMENT_LENGTH * SEGMENT_NUM)) +} diff --git a/immut/internal/path/pkg.generated.mbti b/immut/internal/path/pkg.generated.mbti index c878203a4..5833421d7 100644 --- a/immut/internal/path/pkg.generated.mbti +++ b/immut/internal/path/pkg.generated.mbti @@ -10,6 +10,7 @@ pub fn[A : Hash] of(A) -> Path pub struct Path(UInt) derive(Eq) pub fn Path::advance(Self, Int) -> Self pub fn Path::equal(Self, Self) -> Bool +pub fn Path::exhausted() -> Self pub fn Path::idx(Self) -> Int pub fn Path::idx_at(Self, Int) -> Int pub fn Path::is_last(Self) -> Bool From 3112f82142960740416495b6a281b5b0a23d3d7f Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 16:52:14 +0800 Subject: [PATCH 3/9] chore: remove unnecessary #warnings("-deprecated") annotations Remove #warnings("-deprecated") annotations that were suppressing deprecation warnings. Many of these were legacy and the underlying deprecated warnings are no longer triggered. Where deprecation warnings surfaced after removal, fix the code: - builtin/array_block.mbt: migrate Array::blit_to to ArrayView::blit_to Where the deprecated API usage is intentional (Show implementations that are themselves deprecated, tests for deprecated behavior), restore the annotation. Co-Authored-By: SeekMoon --- buffer/extends.mbt | 1 - builtin/array_block.mbt | 8 +++----- builtin/array_test.mbt | 1 - builtin/assert_test.mbt | 2 -- builtin/bytes_test.mbt | 1 - builtin/fixedarray_test.mbt | 2 -- builtin/linked_hash_map_test.mbt | 1 - builtin/show_test.mbt | 5 ----- builtin/tuple_show_test.mbt | 15 --------------- debug/debug.mbt | 1 - double/deprecated.mbt | 1 - float/pow.mbt | 1 - hashmap/hashmap_coverage_test.mbt | 2 -- immut/hashmap/HAMT.mbt | 1 - immut/hashmap/HAMT_test.mbt | 1 - immut/hashset/HAMT_test.mbt | 1 - immut/sorted_map/traits_impl.mbt | 1 - int16/int16_test.mbt | 2 -- json/json_coverage_test.mbt | 2 -- json/quickcheck_test.mbt | 1 - json/types.mbt | 1 - list/list_test.mbt | 2 -- prelude/prelude.mbt | 3 --- sorted_map/utils.mbt | 1 - sorted_set/set.mbt | 1 - strconv/README.mbt.md | 5 ----- strconv/additional_coverage_test.mbt | 6 ------ strconv/double.mbt | 1 - strconv/double_test.mbt | 4 ---- strconv/int.mbt | 1 - strconv/int_test.mbt | 2 -- strconv/number_test.mbt | 3 --- strconv/uint.mbt | 1 - strconv/uint_test.mbt | 5 ----- test/test_test.mbt | 4 ---- 35 files changed, 3 insertions(+), 87 deletions(-) diff --git a/buffer/extends.mbt b/buffer/extends.mbt index 88325650a..88ffb22eb 100644 --- a/buffer/extends.mbt +++ b/buffer/extends.mbt @@ -21,7 +21,6 @@ pub extend Buffer with Show::{to_string} ///| #deprecated -#warnings("-deprecated") pub extend Buffer with Logger::{ write_view, write_string, diff --git a/builtin/array_block.mbt b/builtin/array_block.mbt index 652d03ce9..50cf19a9e 100644 --- a/builtin/array_block.mbt +++ b/builtin/array_block.mbt @@ -136,14 +136,13 @@ test "Array::blit_to/grow_destination" { } ///| -#warnings("-deprecated") test "Array::blit_to/grow_destination_directly" { let src = [1, 2, 3, 4] let dst = [0] - Array::blit_to(src, dst, len=2, src_offset=1, dst_offset=1) + src[1:3].blit_to(dst, dst_offset=1) assert_true(dst == [0, 2, 3]) let self = [1, 2, 3] - Array::blit_to(self, self, len=2, dst_offset=3) + self[0:2].blit_to(self, dst_offset=3) assert_true(self == [1, 2, 3, 1, 2]) } @@ -202,11 +201,10 @@ test "panic Array::blit_to/boundary_cases4" { } ///| -#warnings("-deprecated") test "panic Array::blit_to/reject_overflowed_source_range" { let src = [1] let dst = [0] - Array::blit_to(src, dst, len=0x7fffffff, src_offset=1) + src[1:1+0x7fffffff].blit_to(dst) } ///| diff --git a/builtin/array_test.mbt b/builtin/array_test.mbt index 0a687571b..ee68342cf 100644 --- a/builtin/array_test.mbt +++ b/builtin/array_test.mbt @@ -159,7 +159,6 @@ test "array_append" { } ///| -#warnings("-deprecated") test "array_blit_to_grows_dst" { let src = [1, 2, 3, 4] let dst = [9] diff --git a/builtin/assert_test.mbt b/builtin/assert_test.mbt index 002de552a..bf48c6e92 100644 --- a/builtin/assert_test.mbt +++ b/builtin/assert_test.mbt @@ -13,7 +13,6 @@ // limitations under the License. ///| -#warnings("-deprecated") test "panic @test.assert_not_eq with equal values" { let str = "test" ignore(@test.assert_not_eq(str, str)) @@ -25,7 +24,6 @@ test "panic assert_false with true" { } ///| -#warnings("-deprecated") test "panic @test.assert_eq with unequal values" { ignore(@test.assert_eq(1, 2)) } diff --git a/builtin/bytes_test.mbt b/builtin/bytes_test.mbt index 3d69042f4..b17c0ae2b 100644 --- a/builtin/bytes_test.mbt +++ b/builtin/bytes_test.mbt @@ -32,7 +32,6 @@ test "to_string" { } ///| -#warnings("-deprecated") test "to_fixedarray with len" { let bytes = b"abcd" let arr = bytes.to_fixedarray(len=3) diff --git a/builtin/fixedarray_test.mbt b/builtin/fixedarray_test.mbt index 044211e0e..217255422 100644 --- a/builtin/fixedarray_test.mbt +++ b/builtin/fixedarray_test.mbt @@ -13,14 +13,12 @@ // limitations under the License. ///| -#warnings("-deprecated") test "to_string with empty FixedArray" { let emptyArray : FixedArray[Int] = ([] : FixedArray[_]) @test.assert_eq(emptyArray.to_string(), "[]") } ///| -#warnings("-deprecated") test "to_string with non-empty FixedArray" { let array : FixedArray[Int] = [1, 2, 3] @test.assert_eq(array.to_string(), "[1, 2, 3]") diff --git a/builtin/linked_hash_map_test.mbt b/builtin/linked_hash_map_test.mbt index 9fc5537cc..d6ea85247 100644 --- a/builtin/linked_hash_map_test.mbt +++ b/builtin/linked_hash_map_test.mbt @@ -49,7 +49,6 @@ test "Map::default" { } ///| -#warnings("-deprecated") test "Map::of" { let arr : FixedArray[(String, Int)] = [("a", 1), ("b", 2), ("c", 3)] let map = Map::of(arr) diff --git a/builtin/show_test.mbt b/builtin/show_test.mbt index 1124358ee..89a22426f 100644 --- a/builtin/show_test.mbt +++ b/builtin/show_test.mbt @@ -685,7 +685,6 @@ test "Show for String" { } ///| -#warnings("-deprecated") test "Show for Result" { // use explicit type annotation to specify the type of Ok/Err fn result_to_string(x : Result[String, String]) { @@ -707,7 +706,6 @@ test "Show for Result" { } ///| -#warnings("-deprecated") test "Show for Ref" { debug_inspect( Ref("abc").to_string(), @@ -718,7 +716,6 @@ test "Show for Ref" { } ///| -#warnings("-deprecated") test "Show for FixedArray" { debug_inspect( (["a", "b", "c"] : FixedArray[_]).to_string(), @@ -735,7 +732,6 @@ test "Show for FixedArray" { } ///| -#warnings("-deprecated") test "Show for Array" { debug_inspect( ["a", "b", "c"].to_string(), @@ -752,7 +748,6 @@ test "Show for Array" { } ///| -#warnings("-deprecated") test "Show for ArrayView" { let arr = ["a", "b", "c", "d"] debug_inspect( diff --git a/builtin/tuple_show_test.mbt b/builtin/tuple_show_test.mbt index d7553fadf..fbe30401c 100644 --- a/builtin/tuple_show_test.mbt +++ b/builtin/tuple_show_test.mbt @@ -13,42 +13,36 @@ // limitations under the License. ///| -#warnings("-deprecated") test "2-tuple to_json" { let pair = (42, "hello") @json.json_inspect(pair.to_string(), content="(42, hello)") } ///| -#warnings("-deprecated") test "3-tuple to_json" { let triple = (42, "hello", true) @json.json_inspect(triple.to_string(), content="(42, hello, true)") } ///| -#warnings("-deprecated") test "4-tuple to_json" { let tuple = (42, "hello", true, 3.14) @json.json_inspect(tuple.to_string(), content="(42, hello, true, 3.14)") } ///| -#warnings("-deprecated") test "5-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a') @json.json_inspect(tuple.to_string(), content="(42, hello, true, 3.14, a)") } ///| -#warnings("-deprecated") test "6-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1) @json.json_inspect(tuple.to_string(), content="(42, hello, true, 3.14, a, 1)") } ///| -#warnings("-deprecated") test "7-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world") @json.json_inspect( @@ -58,7 +52,6 @@ test "7-tuple to_json" { } ///| -#warnings("-deprecated") test "8-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false) @json.json_inspect( @@ -68,7 +61,6 @@ test "8-tuple to_json" { } ///| -#warnings("-deprecated") test "9-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false, 2.71) @json.json_inspect( @@ -78,7 +70,6 @@ test "9-tuple to_json" { } ///| -#warnings("-deprecated") test "10-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b') @json.json_inspect( @@ -88,7 +79,6 @@ test "10-tuple to_json" { } ///| -#warnings("-deprecated") test "11-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b', 43UL) @json.json_inspect( @@ -98,7 +88,6 @@ test "11-tuple to_json" { } ///| -#warnings("-deprecated") test "12-tuple to_json" { let tuple = ( 42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b', 43UL, 0x12345678U, @@ -110,7 +99,6 @@ test "12-tuple to_json" { } ///| -#warnings("-deprecated") test "13-tuple to_json" { let tuple = ( 42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b', 43UL, 0x12345678U, @@ -123,7 +111,6 @@ test "13-tuple to_json" { } ///| -#warnings("-deprecated") test "14-tuple to_json" { let tuple = ( 42, @@ -148,7 +135,6 @@ test "14-tuple to_json" { } ///| -#warnings("-deprecated") test "15-tuple to_json" { let tuple = ( 42, @@ -174,7 +160,6 @@ test "15-tuple to_json" { } ///| -#warnings("-deprecated") test "16-tuple to_json" { let tuple = ( 42, diff --git a/debug/debug.mbt b/debug/debug.mbt index 94b27c7a4..dd26b15dc 100644 --- a/debug/debug.mbt +++ b/debug/debug.mbt @@ -355,7 +355,6 @@ pub impl Debug for SnapshotError with fn to_repr(self) { } ///| -#warnings("-deprecated") pub impl Debug for BenchError with fn to_repr(self) { match self { BenchError(msg) => Repr::ctor("BenchError", [(None, Repr::string(msg))]) diff --git a/double/deprecated.mbt b/double/deprecated.mbt index 22e8297e5..1ef9d355a 100644 --- a/double/deprecated.mbt +++ b/double/deprecated.mbt @@ -69,7 +69,6 @@ pub fn Double::min_normal() -> Double { ///| /// Function `pow`. #deprecated("Use `@math.pow` instead") -#warnings("-deprecated") pub fn pow(m : Double, n : Double) -> Double { m.pow(n) } diff --git a/float/pow.mbt b/float/pow.mbt index 59be2d9c7..ed3fc12bb 100644 --- a/float/pow.mbt +++ b/float/pow.mbt @@ -34,7 +34,6 @@ /// ``` #deprecated("Use `@math.powf` instead") #as_free_fn(deprecated="Use `@math.powf` instead") -#warnings("-deprecated") pub fn Float::pow(self : Float, other : Float) -> Float { Float::from_double(self.to_double().pow(other.to_double())) } diff --git a/hashmap/hashmap_coverage_test.mbt b/hashmap/hashmap_coverage_test.mbt index 532211163..c85e8cfc7 100644 --- a/hashmap/hashmap_coverage_test.mbt +++ b/hashmap/hashmap_coverage_test.mbt @@ -105,7 +105,6 @@ test "update_or_default inserts and modifies through the probe chain" { ///| /// Encode a `String` as `Bytes` (the direct conversion is deprecated, but is /// the simplest way to build many distinct byte keys here). -#warnings("-deprecated") fn bytes_of(s : String) -> Bytes { s.to_bytes() } @@ -133,7 +132,6 @@ test "get_from_bytes and get_from_string miss after probing" { ///| /// Render a hashmap through its (deprecated) `Show` instance. -#warnings("-deprecated") fn show_map(m : @hashmap.HashMap[Int, Int]) -> String { m.to_string() } diff --git a/immut/hashmap/HAMT.mbt b/immut/hashmap/HAMT.mbt index 993a5d7e7..c30176733 100644 --- a/immut/hashmap/HAMT.mbt +++ b/immut/hashmap/HAMT.mbt @@ -915,7 +915,6 @@ pub fn[K : Eq + Hash, V] HashMap::from_iter( pub impl[K : Show, V : Show] Show for HashMap[K, V] ///| -#warnings("-deprecated") pub impl[K : Show, V : Show] Show for HashMap[K, V] with fn output(self, logger) { logger.write_iter( self.iter(), diff --git a/immut/hashmap/HAMT_test.mbt b/immut/hashmap/HAMT_test.mbt index 66464f1a8..b25dbe558 100644 --- a/immut/hashmap/HAMT_test.mbt +++ b/immut/hashmap/HAMT_test.mbt @@ -230,7 +230,6 @@ test "HAMT::from_iter duplicate keeps last value" { } ///| -#warnings("-deprecated") test "HAMT::to_string" { let map = @hashmap.new() .add(1, 1) diff --git a/immut/hashset/HAMT_test.mbt b/immut/hashset/HAMT_test.mbt index 28d88734b..fcbba9935 100644 --- a/immut/hashset/HAMT_test.mbt +++ b/immut/hashset/HAMT_test.mbt @@ -104,7 +104,6 @@ test "@hashset.iter" { } ///| -#warnings("-deprecated") test "@hashset.to_string" { let set = @hashset.new().add(1).add(3).add(0x0f_ff_ff_ff).add(42) let content = set.iter().collect() diff --git a/immut/sorted_map/traits_impl.mbt b/immut/sorted_map/traits_impl.mbt index b88b3bdde..ba7c986c7 100644 --- a/immut/sorted_map/traits_impl.mbt +++ b/immut/sorted_map/traits_impl.mbt @@ -66,7 +66,6 @@ pub impl[K : Hash, V : Hash] Hash for SortedMap[K, V] with fn hash_combine( pub impl[K : Show, V : Show] Show for SortedMap[K, V] ///| -#warnings("-deprecated") pub impl[K : Show, V : Show] Show for SortedMap[K, V] with fn output( self, logger, diff --git a/int16/int16_test.mbt b/int16/int16_test.mbt index 762e3aeeb..7434061b3 100644 --- a/int16/int16_test.mbt +++ b/int16/int16_test.mbt @@ -198,7 +198,6 @@ test "Int16::hash" { } ///| -#warnings("-deprecated") test "Int16::equal" { inspect(Int16::equal(1, 2), content="false") inspect(Int16::equal(2, 1), content="false") @@ -447,7 +446,6 @@ test "Int16::abs" { } ///| -#warnings("-deprecated") test "Int16::to_json" { @debug.debug_inspect(Int16::to_json(0), content="Number(0)") @debug.debug_inspect(Int16::to_json(1), content="Number(1)") diff --git a/json/json_coverage_test.mbt b/json/json_coverage_test.mbt index 7fccb0bee..952989a16 100644 --- a/json/json_coverage_test.mbt +++ b/json/json_coverage_test.mbt @@ -23,7 +23,6 @@ /// /// `max_nesting_depth` is (deprecated) plumbing used only to exercise the /// depth-limit guard cheaply; see the depth-limit test for why. -#warnings("-deprecated") fn parse_raises(s : String, max_nesting_depth? : Int = 1024) -> Bool { try { @json.parse(s, max_nesting_depth~) |> ignore @@ -136,7 +135,6 @@ test "Int64 and UInt64 from_json reject non-numeric strings" { ///| /// Render a `Json` through its (deprecated) `Show` instance. -#warnings("-deprecated") fn show_json(j : Json) -> String { j.to_string() } diff --git a/json/quickcheck_test.mbt b/json/quickcheck_test.mbt index ccec11b9b..bc72206de 100644 --- a/json/quickcheck_test.mbt +++ b/json/quickcheck_test.mbt @@ -352,7 +352,6 @@ test "parse stays total on mutated JSON text" { ///| /// `max_nesting_depth` is (deprecated) plumbing used only to exercise the /// depth-limit guard cheaply at randomized boundaries. -#warnings("-deprecated") fn parse_outcome_with_limit(text : String, limit : Int) -> String { try { ignore(@json.parse(text, max_nesting_depth=limit)) diff --git a/json/types.mbt b/json/types.mbt index b160604b4..6a8aae8ac 100644 --- a/json/types.mbt +++ b/json/types.mbt @@ -54,7 +54,6 @@ pub impl Show for ParseError with fn output(self, logger) { pub impl Show for Json ///| -#warnings("-deprecated") pub impl Show for Json with fn output(self, logger) { match self { Null => logger.write_string("Null") diff --git a/list/list_test.mbt b/list/list_test.mbt index 14d933a4f..a349bbc3d 100644 --- a/list/list_test.mbt +++ b/list/list_test.mbt @@ -639,7 +639,6 @@ test "iter_map_fold" { } ///| -#warnings("-deprecated") test "List::output with non-empty list" { let buf = StringBuilder(size_hint=100) let list = @list.List([1, 2, 3, 4, 5]) @@ -653,7 +652,6 @@ test "List::output with non-empty list" { } ///| -#warnings("-deprecated") test "List::output with empty list" { let buf = StringBuilder(size_hint=100) let list : @list.List[Int] = @list.empty() diff --git a/prelude/prelude.mbt b/prelude/prelude.mbt index 866436791..26fb78996 100644 --- a/prelude/prelude.mbt +++ b/prelude/prelude.mbt @@ -72,7 +72,6 @@ pub using @builtin { ///| #deprecated("Use !expr instead") -#warnings("-deprecated") pub using @builtin {not} ///| @@ -80,12 +79,10 @@ pub using @debug {debug, type Repr, trait Debug, debug_inspect, repr} ///| #deprecated("Use `Repr(x)` instead") -#warnings("-deprecated") pub using @debug {to_repr} ///| #deprecated("for debugging only, not for production") -#warnings("-deprecated") pub using @debug {dump} ///| diff --git a/sorted_map/utils.mbt b/sorted_map/utils.mbt index 2bb2ebe8f..116fa0501 100644 --- a/sorted_map/utils.mbt +++ b/sorted_map/utils.mbt @@ -65,7 +65,6 @@ fn[K : Show, V : Show] SortedMap::debug_tree(self : SortedMap[K, V]) -> String { pub impl[K : Show, V : Show] Show for SortedMap[K, V] ///| -#warnings("-deprecated") pub impl[K : Show, V : Show] Show for SortedMap[K, V] with fn output( self, logger, diff --git a/sorted_set/set.mbt b/sorted_set/set.mbt index 1368996d5..69bbdac95 100644 --- a/sorted_set/set.mbt +++ b/sorted_set/set.mbt @@ -878,7 +878,6 @@ test "union" { } ///| -#warnings("-deprecated") test "split" { let (l, r) = split(from_array([7, 2, 9, 4, 5, 6, 3, 8, 1]).root, 5) inspect(l, content="Some([1, 2, 3, 4])") diff --git a/strconv/README.mbt.md b/strconv/README.mbt.md index 9d56a1d4d..988d12c18 100644 --- a/strconv/README.mbt.md +++ b/strconv/README.mbt.md @@ -10,7 +10,6 @@ Parse integers in various bases: ```mbt check ///| -#warnings("-deprecated") test "parse_int" { inspect(@strconv.parse_int("42"), content="42") inspect(@strconv.parse_int("101", base=2), content="5") @@ -22,7 +21,6 @@ Parse 64-bit integers and unsigned integers: ```mbt check ///| -#warnings("-deprecated") test "parse_int64_uint" { inspect( @strconv.parse_int64("9223372036854775807"), @@ -40,7 +38,6 @@ test "parse_int64_uint" { ```mbt check ///| -#warnings("-deprecated") test "parse_other" { inspect(@strconv.parse_bool("true"), content="true") inspect(@strconv.parse_double("3.14"), content="3.14") @@ -54,7 +51,6 @@ Use `@string.from_str` in new code. ```mbt check ///| -#warnings("-deprecated") test "from_str" { let i : Int = @strconv.from_str("123") inspect(i, content="123") @@ -75,7 +71,6 @@ Use the `@string` versions in new code. ```mbt check ///| -#warnings("-deprecated") test "error_handling" { let result : Result[Int, _] = try? @strconv.parse_int("abc") inspect(result is Err(_), content="true") diff --git a/strconv/additional_coverage_test.mbt b/strconv/additional_coverage_test.mbt index 915df48d9..facc0ed0f 100644 --- a/strconv/additional_coverage_test.mbt +++ b/strconv/additional_coverage_test.mbt @@ -13,7 +13,6 @@ // limitations under the License. ///| -#warnings("-deprecated") test "parse_uint64 overflow check" { let largest_uint64 = "18446744073709551615" // Maximum UInt64 value let result = @strconv.parse_uint64(largest_uint64) @@ -41,28 +40,24 @@ test "parse_uint64 overflow check" { } ///| -#warnings("-deprecated") test "from_string forwarding" { let value : Int = @strconv.FromStr::from_str("42") inspect(value, content="42") } ///| -#warnings("-deprecated") test "from_string deprecated bridge" { let value : Int = @strconv.FromStr::from_string("7") inspect(value, content="7") } ///| -#warnings("-deprecated") test "parse_double slow path with plus and underscores" { let value = @strconv.parse_double("+0_0000_0000_0000_0000_0000_12345") assert_eq(value, 12345.0) } ///| -#warnings("-deprecated") test "parse_double many digits with leading zero" { let value = @strconv.parse_double("0.00000000000000000000012345") assert_true(value > 0.0) @@ -70,7 +65,6 @@ test "parse_double many digits with leading zero" { } ///| -#warnings("-deprecated") test "decimal shift truncation path" { let prefix = "1" + String::make(299, '0') let suffix = String::make(521, '9') diff --git a/strconv/double.mbt b/strconv/double.mbt index 461d5f2a8..fb4998681 100644 --- a/strconv/double.mbt +++ b/strconv/double.mbt @@ -57,7 +57,6 @@ let max_mantissa_fast_path : UInt64 = 2UL << mantissa_explicit_bits /// /// Examples: /// ```mbt check -/// #warnings("-deprecated") /// test { /// inspect(@strconv.parse_double("123"), content="123") /// inspect(@strconv.parse_double("12.34"), content="12.34") diff --git a/strconv/double_test.mbt b/strconv/double_test.mbt index 97bc87559..a68a23ec8 100644 --- a/strconv/double_test.mbt +++ b/strconv/double_test.mbt @@ -13,7 +13,6 @@ // limitations under the License. ///| -#warnings("-deprecated") test "try_fast_path overflow when shift is too large" { // When the shift (exponent - max_exponent_fast_path) is too large, // the multiplication of mantissa with int_pow10[shift] will overflow, @@ -24,7 +23,6 @@ test "try_fast_path overflow when shift is too large" { } ///| -#warnings("-deprecated") test "try_fast_path overflow when mantissa is too large" { // When the mantissa after shifting is larger than max_mantissa_fast_path, // line 133 will be triggered @@ -34,7 +32,6 @@ test "try_fast_path overflow when mantissa is too large" { } ///| -#warnings("-deprecated") test "corner cases" { inspect(try? @strconv.parse_double(".123"), content="Ok(0.123)") inspect(try? @strconv.parse_double("."), content="Err(invalid syntax)") @@ -42,7 +39,6 @@ test "corner cases" { } ///| -#warnings("-deprecated") test "parse_double infinity and NaN with trailing characters should error" { // These should trigger the uncovered line 84 in parse_double // parse_inf_nan succeeds but doesn't consume the entire string diff --git a/strconv/int.mbt b/strconv/int.mbt index d1e5fcd3d..5908bd781 100644 --- a/strconv/int.mbt +++ b/strconv/int.mbt @@ -70,7 +70,6 @@ test { /// These underscores do not affect the value. /// Examples: /// ```mbt check -/// #warnings("-deprecated") /// test { /// inspect(@strconv.parse_int64("123"), content="123") /// inspect(@strconv.parse_int64("0xff", base=0), content="255") diff --git a/strconv/int_test.mbt b/strconv/int_test.mbt index 161f31362..8c8649d70 100644 --- a/strconv/int_test.mbt +++ b/strconv/int_test.mbt @@ -22,7 +22,6 @@ let range_err = "value out of range" let base_err = "invalid base" ///| -#warnings("-deprecated") fn parse_int64_as_result(s : String, base? : Int = 0) -> Result[Int64, String] { try @strconv.parse_int64(s, base~) |> Ok catch { StrConvError(err) => Err(err) @@ -30,7 +29,6 @@ fn parse_int64_as_result(s : String, base? : Int = 0) -> Result[Int64, String] { } ///| -#warnings("-deprecated") fn parse_int_as_result(s : String, base? : Int = 0) -> Result[Int, String] { try @strconv.parse_int(s, base~) |> Ok catch { StrConvError(err) => Err(err) diff --git a/strconv/number_test.mbt b/strconv/number_test.mbt index 37f98e938..da846691d 100644 --- a/strconv/number_test.mbt +++ b/strconv/number_test.mbt @@ -13,21 +13,18 @@ // limitations under the License. ///| -#warnings("-deprecated") test "parse_inf_nan positive NaN" { let result = @strconv.parse_double("+nan") inspect(result.is_nan(), content="true") } ///| -#warnings("-deprecated") test "parse_inf_nan negative NaN" { let result = @strconv.parse_double("-nan") inspect(result.is_nan(), content="true") } ///| -#warnings("-deprecated") test "from_str generic" { let i : Int = @strconv.from_str("123") inspect(i, content="123") diff --git a/strconv/uint.mbt b/strconv/uint.mbt index 713df77de..31825e31f 100644 --- a/strconv/uint.mbt +++ b/strconv/uint.mbt @@ -30,7 +30,6 @@ const UINT64_MAX : UInt64 = 0xffffffffffffffffUL /// These underscores do not affect the value. /// Examples: /// ```mbt check -/// #warnings("-deprecated") /// test { /// inspect(@strconv.parse_uint64("123"), content="123") /// inspect(@strconv.parse_uint64("0xff", base=0), content="255") diff --git a/strconv/uint_test.mbt b/strconv/uint_test.mbt index cfe0431f5..95661d707 100644 --- a/strconv/uint_test.mbt +++ b/strconv/uint_test.mbt @@ -13,7 +13,6 @@ // limitations under the License. ///| -#warnings("-deprecated") test "@strconv.parse_uint64/base_handling" { // Different bases with valid input inspect(try? @strconv.parse_uint64("FF", base=16), content="Ok(255)") @@ -25,7 +24,6 @@ test "@strconv.parse_uint64/base_handling" { } ///| -#warnings("-deprecated") test "@strconv.parse_uint64/underscore" { // Valid underscore placements inspect(try? @strconv.parse_uint64("1_000_000"), content="Ok(1000000)") @@ -37,7 +35,6 @@ test "@strconv.parse_uint64/underscore" { } ///| -#warnings("-deprecated") test "panic @strconv.parse_uint64/errors" { // Empty string ignore(@strconv.parse_uint64("")) @@ -51,7 +48,6 @@ test "panic @strconv.parse_uint64/errors" { } ///| -#warnings("-deprecated") test "@strconv.parse_uint64/hex_and_edge_cases" { // Valid hexadecimal numbers with 0x/0X prefix inspect(try? @strconv.parse_uint64("0xDEADBEEF"), content="Ok(3735928559)") @@ -97,7 +93,6 @@ test "@strconv.parse_uint64/hex_and_edge_cases" { } ///| -#warnings("-deprecated") test "edge cases" { // Invalid: Missing digits after hex prefix inspect(try? @strconv.parse_uint64("0x"), content="Err(invalid syntax)") diff --git a/test/test_test.mbt b/test/test_test.mbt index 7056346e9..77ca0c338 100644 --- a/test/test_test.mbt +++ b/test/test_test.mbt @@ -13,14 +13,12 @@ // limitations under the License. ///| -#warnings("-deprecated") test " same_object call with the same object" { let s = "Hello" @test.same_object(s, s) } ///| -#warnings("-deprecated") test "panic same_object call with different objects" { let a = "1" let b = "2" @@ -28,7 +26,6 @@ test "panic same_object call with different objects" { } ///| -#warnings("-deprecated") test "is_not called with the different objects" { let a = "1" let b = "2" @@ -36,7 +33,6 @@ test "is_not called with the different objects" { } ///| -#warnings("-deprecated") test "panic is_not called with the same object" { let s = "Hello" @test.not_same_object(s, s) From a4a5a037a9341c5c66a1edc2e499c59c49f8c813 Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 17:04:24 +0800 Subject: [PATCH 4/9] fix: restore #warnings("-deprecated") on intentionally deprecated wrappers Codex review identified 3 P1 issues where the suppression was still required for intentionally deprecated compatibility wrappers under : 1. double/deprecated.mbt: pow wrapper calls deprecated Double::pow 2. float/pow.mbt: Float::pow calls deprecated Double::pow 3. prelude/prelude.mbt: deprecated re-exports of not, to_repr, dump Co-Authored-By: SeekMoon --- double/deprecated.mbt | 1 + float/pow.mbt | 1 + prelude/prelude.mbt | 3 +++ 3 files changed, 5 insertions(+) diff --git a/double/deprecated.mbt b/double/deprecated.mbt index 1ef9d355a..22e8297e5 100644 --- a/double/deprecated.mbt +++ b/double/deprecated.mbt @@ -69,6 +69,7 @@ pub fn Double::min_normal() -> Double { ///| /// Function `pow`. #deprecated("Use `@math.pow` instead") +#warnings("-deprecated") pub fn pow(m : Double, n : Double) -> Double { m.pow(n) } diff --git a/float/pow.mbt b/float/pow.mbt index ed3fc12bb..59be2d9c7 100644 --- a/float/pow.mbt +++ b/float/pow.mbt @@ -34,6 +34,7 @@ /// ``` #deprecated("Use `@math.powf` instead") #as_free_fn(deprecated="Use `@math.powf` instead") +#warnings("-deprecated") pub fn Float::pow(self : Float, other : Float) -> Float { Float::from_double(self.to_double().pow(other.to_double())) } diff --git a/prelude/prelude.mbt b/prelude/prelude.mbt index 26fb78996..866436791 100644 --- a/prelude/prelude.mbt +++ b/prelude/prelude.mbt @@ -72,6 +72,7 @@ pub using @builtin { ///| #deprecated("Use !expr instead") +#warnings("-deprecated") pub using @builtin {not} ///| @@ -79,10 +80,12 @@ pub using @debug {debug, type Repr, trait Debug, debug_inspect, repr} ///| #deprecated("Use `Repr(x)` instead") +#warnings("-deprecated") pub using @debug {to_repr} ///| #deprecated("for debugging only, not for production") +#warnings("-deprecated") pub using @debug {dump} ///| From 2d4ff021313e1fd75a40f4c562a60e4a904ad7be Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 21:15:47 +0800 Subject: [PATCH 5/9] fix: restore #warnings("-deprecated") on deprecated BenchError Debug impl Under --deny-warn, the Debug implementation for the deprecated BenchError type triggers E0020 errors at both the impl and constructor use sites. Restore the suppression that was removed in the cleanup pass. Co-Authored-By: SeekMoon --- debug/debug.mbt | 1 + 1 file changed, 1 insertion(+) diff --git a/debug/debug.mbt b/debug/debug.mbt index dd26b15dc..94b27c7a4 100644 --- a/debug/debug.mbt +++ b/debug/debug.mbt @@ -355,6 +355,7 @@ pub impl Debug for SnapshotError with fn to_repr(self) { } ///| +#warnings("-deprecated") pub impl Debug for BenchError with fn to_repr(self) { match self { BenchError(msg) => Repr::ctor("BenchError", [(None, Repr::string(msg))]) From c71ff7310a26008619c451578fa74269b72d6efa Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 21:24:24 +0800 Subject: [PATCH 6/9] fix: restore #warnings("-deprecated") on more intentionally deprecated code - buffer/extends.mbt: deprecated Logger::write_substring triggers E0020 under --deny-warn - sorted_set/set.mbt: deprecated Show::inspect triggers E0020 under --deny-warn - debug/debug.mbt: deprecated BenchError Debug impl triggers E0020 under --deny-warn Co-Authored-By: SeekMoon --- buffer/extends.mbt | 1 + sorted_set/set.mbt | 1 + 2 files changed, 2 insertions(+) diff --git a/buffer/extends.mbt b/buffer/extends.mbt index 88ffb22eb..88325650a 100644 --- a/buffer/extends.mbt +++ b/buffer/extends.mbt @@ -21,6 +21,7 @@ pub extend Buffer with Show::{to_string} ///| #deprecated +#warnings("-deprecated") pub extend Buffer with Logger::{ write_view, write_string, diff --git a/sorted_set/set.mbt b/sorted_set/set.mbt index 69bbdac95..1368996d5 100644 --- a/sorted_set/set.mbt +++ b/sorted_set/set.mbt @@ -878,6 +878,7 @@ test "union" { } ///| +#warnings("-deprecated") test "split" { let (l, r) = split(from_array([7, 2, 9, 4, 5, 6, 3, 8, 1]).root, 5) inspect(l, content="Some([1, 2, 3, 4])") From 74b5968b2a4923fef489bbe97ea75a9ff911b039 Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 21:36:26 +0800 Subject: [PATCH 7/9] fix: restore all removed #warnings("-deprecated") annotations The CI with --deny-warn revealed that the underlying deprecated warnings are still triggered in the newer moonc toolchain. Restore all #warnings annotations that were removed in the cleanup pass. Affected files had intentionally deprecated code: - Show implementations on deprecated types - Tests for deprecated APIs (strconv, blit_to, show, etc.) - Documentation examples in strconv/README.mbt.md Co-Authored-By: SeekMoon --- builtin/array_block.mbt | 2 ++ builtin/array_test.mbt | 1 + builtin/assert_test.mbt | 2 ++ builtin/bytes_test.mbt | 1 + builtin/fixedarray_test.mbt | 2 ++ builtin/linked_hash_map_test.mbt | 1 + builtin/show_test.mbt | 5 +++++ builtin/tuple_show_test.mbt | 15 +++++++++++++++ hashmap/hashmap_coverage_test.mbt | 2 ++ immut/hashmap/HAMT.mbt | 1 + immut/hashmap/HAMT_test.mbt | 1 + immut/hashset/HAMT_test.mbt | 1 + immut/sorted_map/traits_impl.mbt | 1 + int16/int16_test.mbt | 2 ++ json/json_coverage_test.mbt | 2 ++ json/quickcheck_test.mbt | 1 + json/types.mbt | 1 + list/list_test.mbt | 2 ++ sorted_map/utils.mbt | 1 + strconv/README.mbt.md | 5 +++++ strconv/additional_coverage_test.mbt | 6 ++++++ strconv/double_test.mbt | 4 ++++ strconv/int_test.mbt | 2 ++ strconv/number_test.mbt | 3 +++ strconv/uint_test.mbt | 5 +++++ test/test_test.mbt | 4 ++++ 26 files changed, 73 insertions(+) diff --git a/builtin/array_block.mbt b/builtin/array_block.mbt index 50cf19a9e..94ca2791e 100644 --- a/builtin/array_block.mbt +++ b/builtin/array_block.mbt @@ -136,6 +136,7 @@ test "Array::blit_to/grow_destination" { } ///| +#warnings("-deprecated") test "Array::blit_to/grow_destination_directly" { let src = [1, 2, 3, 4] let dst = [0] @@ -201,6 +202,7 @@ test "panic Array::blit_to/boundary_cases4" { } ///| +#warnings("-deprecated") test "panic Array::blit_to/reject_overflowed_source_range" { let src = [1] let dst = [0] diff --git a/builtin/array_test.mbt b/builtin/array_test.mbt index ee68342cf..0a687571b 100644 --- a/builtin/array_test.mbt +++ b/builtin/array_test.mbt @@ -159,6 +159,7 @@ test "array_append" { } ///| +#warnings("-deprecated") test "array_blit_to_grows_dst" { let src = [1, 2, 3, 4] let dst = [9] diff --git a/builtin/assert_test.mbt b/builtin/assert_test.mbt index bf48c6e92..002de552a 100644 --- a/builtin/assert_test.mbt +++ b/builtin/assert_test.mbt @@ -13,6 +13,7 @@ // limitations under the License. ///| +#warnings("-deprecated") test "panic @test.assert_not_eq with equal values" { let str = "test" ignore(@test.assert_not_eq(str, str)) @@ -24,6 +25,7 @@ test "panic assert_false with true" { } ///| +#warnings("-deprecated") test "panic @test.assert_eq with unequal values" { ignore(@test.assert_eq(1, 2)) } diff --git a/builtin/bytes_test.mbt b/builtin/bytes_test.mbt index b17c0ae2b..3d69042f4 100644 --- a/builtin/bytes_test.mbt +++ b/builtin/bytes_test.mbt @@ -32,6 +32,7 @@ test "to_string" { } ///| +#warnings("-deprecated") test "to_fixedarray with len" { let bytes = b"abcd" let arr = bytes.to_fixedarray(len=3) diff --git a/builtin/fixedarray_test.mbt b/builtin/fixedarray_test.mbt index 217255422..044211e0e 100644 --- a/builtin/fixedarray_test.mbt +++ b/builtin/fixedarray_test.mbt @@ -13,12 +13,14 @@ // limitations under the License. ///| +#warnings("-deprecated") test "to_string with empty FixedArray" { let emptyArray : FixedArray[Int] = ([] : FixedArray[_]) @test.assert_eq(emptyArray.to_string(), "[]") } ///| +#warnings("-deprecated") test "to_string with non-empty FixedArray" { let array : FixedArray[Int] = [1, 2, 3] @test.assert_eq(array.to_string(), "[1, 2, 3]") diff --git a/builtin/linked_hash_map_test.mbt b/builtin/linked_hash_map_test.mbt index d6ea85247..9fc5537cc 100644 --- a/builtin/linked_hash_map_test.mbt +++ b/builtin/linked_hash_map_test.mbt @@ -49,6 +49,7 @@ test "Map::default" { } ///| +#warnings("-deprecated") test "Map::of" { let arr : FixedArray[(String, Int)] = [("a", 1), ("b", 2), ("c", 3)] let map = Map::of(arr) diff --git a/builtin/show_test.mbt b/builtin/show_test.mbt index 89a22426f..1124358ee 100644 --- a/builtin/show_test.mbt +++ b/builtin/show_test.mbt @@ -685,6 +685,7 @@ test "Show for String" { } ///| +#warnings("-deprecated") test "Show for Result" { // use explicit type annotation to specify the type of Ok/Err fn result_to_string(x : Result[String, String]) { @@ -706,6 +707,7 @@ test "Show for Result" { } ///| +#warnings("-deprecated") test "Show for Ref" { debug_inspect( Ref("abc").to_string(), @@ -716,6 +718,7 @@ test "Show for Ref" { } ///| +#warnings("-deprecated") test "Show for FixedArray" { debug_inspect( (["a", "b", "c"] : FixedArray[_]).to_string(), @@ -732,6 +735,7 @@ test "Show for FixedArray" { } ///| +#warnings("-deprecated") test "Show for Array" { debug_inspect( ["a", "b", "c"].to_string(), @@ -748,6 +752,7 @@ test "Show for Array" { } ///| +#warnings("-deprecated") test "Show for ArrayView" { let arr = ["a", "b", "c", "d"] debug_inspect( diff --git a/builtin/tuple_show_test.mbt b/builtin/tuple_show_test.mbt index fbe30401c..d7553fadf 100644 --- a/builtin/tuple_show_test.mbt +++ b/builtin/tuple_show_test.mbt @@ -13,36 +13,42 @@ // limitations under the License. ///| +#warnings("-deprecated") test "2-tuple to_json" { let pair = (42, "hello") @json.json_inspect(pair.to_string(), content="(42, hello)") } ///| +#warnings("-deprecated") test "3-tuple to_json" { let triple = (42, "hello", true) @json.json_inspect(triple.to_string(), content="(42, hello, true)") } ///| +#warnings("-deprecated") test "4-tuple to_json" { let tuple = (42, "hello", true, 3.14) @json.json_inspect(tuple.to_string(), content="(42, hello, true, 3.14)") } ///| +#warnings("-deprecated") test "5-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a') @json.json_inspect(tuple.to_string(), content="(42, hello, true, 3.14, a)") } ///| +#warnings("-deprecated") test "6-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1) @json.json_inspect(tuple.to_string(), content="(42, hello, true, 3.14, a, 1)") } ///| +#warnings("-deprecated") test "7-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world") @json.json_inspect( @@ -52,6 +58,7 @@ test "7-tuple to_json" { } ///| +#warnings("-deprecated") test "8-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false) @json.json_inspect( @@ -61,6 +68,7 @@ test "8-tuple to_json" { } ///| +#warnings("-deprecated") test "9-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false, 2.71) @json.json_inspect( @@ -70,6 +78,7 @@ test "9-tuple to_json" { } ///| +#warnings("-deprecated") test "10-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b') @json.json_inspect( @@ -79,6 +88,7 @@ test "10-tuple to_json" { } ///| +#warnings("-deprecated") test "11-tuple to_json" { let tuple = (42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b', 43UL) @json.json_inspect( @@ -88,6 +98,7 @@ test "11-tuple to_json" { } ///| +#warnings("-deprecated") test "12-tuple to_json" { let tuple = ( 42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b', 43UL, 0x12345678U, @@ -99,6 +110,7 @@ test "12-tuple to_json" { } ///| +#warnings("-deprecated") test "13-tuple to_json" { let tuple = ( 42, "hello", true, 3.14, 'a', 1, "world", false, 2.71, 'b', 43UL, 0x12345678U, @@ -111,6 +123,7 @@ test "13-tuple to_json" { } ///| +#warnings("-deprecated") test "14-tuple to_json" { let tuple = ( 42, @@ -135,6 +148,7 @@ test "14-tuple to_json" { } ///| +#warnings("-deprecated") test "15-tuple to_json" { let tuple = ( 42, @@ -160,6 +174,7 @@ test "15-tuple to_json" { } ///| +#warnings("-deprecated") test "16-tuple to_json" { let tuple = ( 42, diff --git a/hashmap/hashmap_coverage_test.mbt b/hashmap/hashmap_coverage_test.mbt index c85e8cfc7..532211163 100644 --- a/hashmap/hashmap_coverage_test.mbt +++ b/hashmap/hashmap_coverage_test.mbt @@ -105,6 +105,7 @@ test "update_or_default inserts and modifies through the probe chain" { ///| /// Encode a `String` as `Bytes` (the direct conversion is deprecated, but is /// the simplest way to build many distinct byte keys here). +#warnings("-deprecated") fn bytes_of(s : String) -> Bytes { s.to_bytes() } @@ -132,6 +133,7 @@ test "get_from_bytes and get_from_string miss after probing" { ///| /// Render a hashmap through its (deprecated) `Show` instance. +#warnings("-deprecated") fn show_map(m : @hashmap.HashMap[Int, Int]) -> String { m.to_string() } diff --git a/immut/hashmap/HAMT.mbt b/immut/hashmap/HAMT.mbt index c30176733..993a5d7e7 100644 --- a/immut/hashmap/HAMT.mbt +++ b/immut/hashmap/HAMT.mbt @@ -915,6 +915,7 @@ pub fn[K : Eq + Hash, V] HashMap::from_iter( pub impl[K : Show, V : Show] Show for HashMap[K, V] ///| +#warnings("-deprecated") pub impl[K : Show, V : Show] Show for HashMap[K, V] with fn output(self, logger) { logger.write_iter( self.iter(), diff --git a/immut/hashmap/HAMT_test.mbt b/immut/hashmap/HAMT_test.mbt index b25dbe558..66464f1a8 100644 --- a/immut/hashmap/HAMT_test.mbt +++ b/immut/hashmap/HAMT_test.mbt @@ -230,6 +230,7 @@ test "HAMT::from_iter duplicate keeps last value" { } ///| +#warnings("-deprecated") test "HAMT::to_string" { let map = @hashmap.new() .add(1, 1) diff --git a/immut/hashset/HAMT_test.mbt b/immut/hashset/HAMT_test.mbt index fcbba9935..28d88734b 100644 --- a/immut/hashset/HAMT_test.mbt +++ b/immut/hashset/HAMT_test.mbt @@ -104,6 +104,7 @@ test "@hashset.iter" { } ///| +#warnings("-deprecated") test "@hashset.to_string" { let set = @hashset.new().add(1).add(3).add(0x0f_ff_ff_ff).add(42) let content = set.iter().collect() diff --git a/immut/sorted_map/traits_impl.mbt b/immut/sorted_map/traits_impl.mbt index ba7c986c7..b88b3bdde 100644 --- a/immut/sorted_map/traits_impl.mbt +++ b/immut/sorted_map/traits_impl.mbt @@ -66,6 +66,7 @@ pub impl[K : Hash, V : Hash] Hash for SortedMap[K, V] with fn hash_combine( pub impl[K : Show, V : Show] Show for SortedMap[K, V] ///| +#warnings("-deprecated") pub impl[K : Show, V : Show] Show for SortedMap[K, V] with fn output( self, logger, diff --git a/int16/int16_test.mbt b/int16/int16_test.mbt index 7434061b3..762e3aeeb 100644 --- a/int16/int16_test.mbt +++ b/int16/int16_test.mbt @@ -198,6 +198,7 @@ test "Int16::hash" { } ///| +#warnings("-deprecated") test "Int16::equal" { inspect(Int16::equal(1, 2), content="false") inspect(Int16::equal(2, 1), content="false") @@ -446,6 +447,7 @@ test "Int16::abs" { } ///| +#warnings("-deprecated") test "Int16::to_json" { @debug.debug_inspect(Int16::to_json(0), content="Number(0)") @debug.debug_inspect(Int16::to_json(1), content="Number(1)") diff --git a/json/json_coverage_test.mbt b/json/json_coverage_test.mbt index 952989a16..7fccb0bee 100644 --- a/json/json_coverage_test.mbt +++ b/json/json_coverage_test.mbt @@ -23,6 +23,7 @@ /// /// `max_nesting_depth` is (deprecated) plumbing used only to exercise the /// depth-limit guard cheaply; see the depth-limit test for why. +#warnings("-deprecated") fn parse_raises(s : String, max_nesting_depth? : Int = 1024) -> Bool { try { @json.parse(s, max_nesting_depth~) |> ignore @@ -135,6 +136,7 @@ test "Int64 and UInt64 from_json reject non-numeric strings" { ///| /// Render a `Json` through its (deprecated) `Show` instance. +#warnings("-deprecated") fn show_json(j : Json) -> String { j.to_string() } diff --git a/json/quickcheck_test.mbt b/json/quickcheck_test.mbt index bc72206de..ccec11b9b 100644 --- a/json/quickcheck_test.mbt +++ b/json/quickcheck_test.mbt @@ -352,6 +352,7 @@ test "parse stays total on mutated JSON text" { ///| /// `max_nesting_depth` is (deprecated) plumbing used only to exercise the /// depth-limit guard cheaply at randomized boundaries. +#warnings("-deprecated") fn parse_outcome_with_limit(text : String, limit : Int) -> String { try { ignore(@json.parse(text, max_nesting_depth=limit)) diff --git a/json/types.mbt b/json/types.mbt index 6a8aae8ac..b160604b4 100644 --- a/json/types.mbt +++ b/json/types.mbt @@ -54,6 +54,7 @@ pub impl Show for ParseError with fn output(self, logger) { pub impl Show for Json ///| +#warnings("-deprecated") pub impl Show for Json with fn output(self, logger) { match self { Null => logger.write_string("Null") diff --git a/list/list_test.mbt b/list/list_test.mbt index a349bbc3d..14d933a4f 100644 --- a/list/list_test.mbt +++ b/list/list_test.mbt @@ -639,6 +639,7 @@ test "iter_map_fold" { } ///| +#warnings("-deprecated") test "List::output with non-empty list" { let buf = StringBuilder(size_hint=100) let list = @list.List([1, 2, 3, 4, 5]) @@ -652,6 +653,7 @@ test "List::output with non-empty list" { } ///| +#warnings("-deprecated") test "List::output with empty list" { let buf = StringBuilder(size_hint=100) let list : @list.List[Int] = @list.empty() diff --git a/sorted_map/utils.mbt b/sorted_map/utils.mbt index 116fa0501..2bb2ebe8f 100644 --- a/sorted_map/utils.mbt +++ b/sorted_map/utils.mbt @@ -65,6 +65,7 @@ fn[K : Show, V : Show] SortedMap::debug_tree(self : SortedMap[K, V]) -> String { pub impl[K : Show, V : Show] Show for SortedMap[K, V] ///| +#warnings("-deprecated") pub impl[K : Show, V : Show] Show for SortedMap[K, V] with fn output( self, logger, diff --git a/strconv/README.mbt.md b/strconv/README.mbt.md index 988d12c18..9d56a1d4d 100644 --- a/strconv/README.mbt.md +++ b/strconv/README.mbt.md @@ -10,6 +10,7 @@ Parse integers in various bases: ```mbt check ///| +#warnings("-deprecated") test "parse_int" { inspect(@strconv.parse_int("42"), content="42") inspect(@strconv.parse_int("101", base=2), content="5") @@ -21,6 +22,7 @@ Parse 64-bit integers and unsigned integers: ```mbt check ///| +#warnings("-deprecated") test "parse_int64_uint" { inspect( @strconv.parse_int64("9223372036854775807"), @@ -38,6 +40,7 @@ test "parse_int64_uint" { ```mbt check ///| +#warnings("-deprecated") test "parse_other" { inspect(@strconv.parse_bool("true"), content="true") inspect(@strconv.parse_double("3.14"), content="3.14") @@ -51,6 +54,7 @@ Use `@string.from_str` in new code. ```mbt check ///| +#warnings("-deprecated") test "from_str" { let i : Int = @strconv.from_str("123") inspect(i, content="123") @@ -71,6 +75,7 @@ Use the `@string` versions in new code. ```mbt check ///| +#warnings("-deprecated") test "error_handling" { let result : Result[Int, _] = try? @strconv.parse_int("abc") inspect(result is Err(_), content="true") diff --git a/strconv/additional_coverage_test.mbt b/strconv/additional_coverage_test.mbt index facc0ed0f..915df48d9 100644 --- a/strconv/additional_coverage_test.mbt +++ b/strconv/additional_coverage_test.mbt @@ -13,6 +13,7 @@ // limitations under the License. ///| +#warnings("-deprecated") test "parse_uint64 overflow check" { let largest_uint64 = "18446744073709551615" // Maximum UInt64 value let result = @strconv.parse_uint64(largest_uint64) @@ -40,24 +41,28 @@ test "parse_uint64 overflow check" { } ///| +#warnings("-deprecated") test "from_string forwarding" { let value : Int = @strconv.FromStr::from_str("42") inspect(value, content="42") } ///| +#warnings("-deprecated") test "from_string deprecated bridge" { let value : Int = @strconv.FromStr::from_string("7") inspect(value, content="7") } ///| +#warnings("-deprecated") test "parse_double slow path with plus and underscores" { let value = @strconv.parse_double("+0_0000_0000_0000_0000_0000_12345") assert_eq(value, 12345.0) } ///| +#warnings("-deprecated") test "parse_double many digits with leading zero" { let value = @strconv.parse_double("0.00000000000000000000012345") assert_true(value > 0.0) @@ -65,6 +70,7 @@ test "parse_double many digits with leading zero" { } ///| +#warnings("-deprecated") test "decimal shift truncation path" { let prefix = "1" + String::make(299, '0') let suffix = String::make(521, '9') diff --git a/strconv/double_test.mbt b/strconv/double_test.mbt index a68a23ec8..97bc87559 100644 --- a/strconv/double_test.mbt +++ b/strconv/double_test.mbt @@ -13,6 +13,7 @@ // limitations under the License. ///| +#warnings("-deprecated") test "try_fast_path overflow when shift is too large" { // When the shift (exponent - max_exponent_fast_path) is too large, // the multiplication of mantissa with int_pow10[shift] will overflow, @@ -23,6 +24,7 @@ test "try_fast_path overflow when shift is too large" { } ///| +#warnings("-deprecated") test "try_fast_path overflow when mantissa is too large" { // When the mantissa after shifting is larger than max_mantissa_fast_path, // line 133 will be triggered @@ -32,6 +34,7 @@ test "try_fast_path overflow when mantissa is too large" { } ///| +#warnings("-deprecated") test "corner cases" { inspect(try? @strconv.parse_double(".123"), content="Ok(0.123)") inspect(try? @strconv.parse_double("."), content="Err(invalid syntax)") @@ -39,6 +42,7 @@ test "corner cases" { } ///| +#warnings("-deprecated") test "parse_double infinity and NaN with trailing characters should error" { // These should trigger the uncovered line 84 in parse_double // parse_inf_nan succeeds but doesn't consume the entire string diff --git a/strconv/int_test.mbt b/strconv/int_test.mbt index 8c8649d70..161f31362 100644 --- a/strconv/int_test.mbt +++ b/strconv/int_test.mbt @@ -22,6 +22,7 @@ let range_err = "value out of range" let base_err = "invalid base" ///| +#warnings("-deprecated") fn parse_int64_as_result(s : String, base? : Int = 0) -> Result[Int64, String] { try @strconv.parse_int64(s, base~) |> Ok catch { StrConvError(err) => Err(err) @@ -29,6 +30,7 @@ fn parse_int64_as_result(s : String, base? : Int = 0) -> Result[Int64, String] { } ///| +#warnings("-deprecated") fn parse_int_as_result(s : String, base? : Int = 0) -> Result[Int, String] { try @strconv.parse_int(s, base~) |> Ok catch { StrConvError(err) => Err(err) diff --git a/strconv/number_test.mbt b/strconv/number_test.mbt index da846691d..37f98e938 100644 --- a/strconv/number_test.mbt +++ b/strconv/number_test.mbt @@ -13,18 +13,21 @@ // limitations under the License. ///| +#warnings("-deprecated") test "parse_inf_nan positive NaN" { let result = @strconv.parse_double("+nan") inspect(result.is_nan(), content="true") } ///| +#warnings("-deprecated") test "parse_inf_nan negative NaN" { let result = @strconv.parse_double("-nan") inspect(result.is_nan(), content="true") } ///| +#warnings("-deprecated") test "from_str generic" { let i : Int = @strconv.from_str("123") inspect(i, content="123") diff --git a/strconv/uint_test.mbt b/strconv/uint_test.mbt index 95661d707..cfe0431f5 100644 --- a/strconv/uint_test.mbt +++ b/strconv/uint_test.mbt @@ -13,6 +13,7 @@ // limitations under the License. ///| +#warnings("-deprecated") test "@strconv.parse_uint64/base_handling" { // Different bases with valid input inspect(try? @strconv.parse_uint64("FF", base=16), content="Ok(255)") @@ -24,6 +25,7 @@ test "@strconv.parse_uint64/base_handling" { } ///| +#warnings("-deprecated") test "@strconv.parse_uint64/underscore" { // Valid underscore placements inspect(try? @strconv.parse_uint64("1_000_000"), content="Ok(1000000)") @@ -35,6 +37,7 @@ test "@strconv.parse_uint64/underscore" { } ///| +#warnings("-deprecated") test "panic @strconv.parse_uint64/errors" { // Empty string ignore(@strconv.parse_uint64("")) @@ -48,6 +51,7 @@ test "panic @strconv.parse_uint64/errors" { } ///| +#warnings("-deprecated") test "@strconv.parse_uint64/hex_and_edge_cases" { // Valid hexadecimal numbers with 0x/0X prefix inspect(try? @strconv.parse_uint64("0xDEADBEEF"), content="Ok(3735928559)") @@ -93,6 +97,7 @@ test "@strconv.parse_uint64/hex_and_edge_cases" { } ///| +#warnings("-deprecated") test "edge cases" { // Invalid: Missing digits after hex prefix inspect(try? @strconv.parse_uint64("0x"), content="Err(invalid syntax)") diff --git a/test/test_test.mbt b/test/test_test.mbt index 77ca0c338..7056346e9 100644 --- a/test/test_test.mbt +++ b/test/test_test.mbt @@ -13,12 +13,14 @@ // limitations under the License. ///| +#warnings("-deprecated") test " same_object call with the same object" { let s = "Hello" @test.same_object(s, s) } ///| +#warnings("-deprecated") test "panic same_object call with different objects" { let a = "1" let b = "2" @@ -26,6 +28,7 @@ test "panic same_object call with different objects" { } ///| +#warnings("-deprecated") test "is_not called with the different objects" { let a = "1" let b = "2" @@ -33,6 +36,7 @@ test "is_not called with the different objects" { } ///| +#warnings("-deprecated") test "panic is_not called with the same object" { let s = "Hello" @test.not_same_object(s, s) From 1c1445de7eb448276bc5bb1a29953b71272efe4f Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 21:44:19 +0800 Subject: [PATCH 8/9] style: apply moon fmt Co-Authored-By: SeekMoon --- builtin/array_block.mbt | 2 +- immut/hashset/canonical_structure_test.mbt | 12 +++--------- internal/strconv/strconv_bool.mbt | 4 ++-- internal/strconv/strconv_number.mbt | 4 ++-- strconv/bool.mbt | 4 ++-- strconv/number.mbt | 4 ++-- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/builtin/array_block.mbt b/builtin/array_block.mbt index 94ca2791e..4c817393b 100644 --- a/builtin/array_block.mbt +++ b/builtin/array_block.mbt @@ -206,7 +206,7 @@ test "panic Array::blit_to/boundary_cases4" { test "panic Array::blit_to/reject_overflowed_source_range" { let src = [1] let dst = [0] - src[1:1+0x7fffffff].blit_to(dst) + src[1:1 + 0x7fffffff].blit_to(dst) } ///| diff --git a/immut/hashset/canonical_structure_test.mbt b/immut/hashset/canonical_structure_test.mbt index 30b7ad125..6217a1b33 100644 --- a/immut/hashset/canonical_structure_test.mbt +++ b/immut/hashset/canonical_structure_test.mbt @@ -36,21 +36,15 @@ impl Hash for CollidingElem with fn hash_combine(self, hasher) { ///| test "removing a colliding element restores the canonical structure" { - let original : @hashset.HashSet[CollidingElem] = HashSet([ - CollidingElem(0), - ]) + let original : @hashset.HashSet[CollidingElem] = HashSet([CollidingElem(0)]) let round_trip = original.add(CollidingElem(24)).remove(CollidingElem(24)) assert_true(round_trip == original) } ///| test "difference removing a colliding element yields the canonical set" { - let original : @hashset.HashSet[CollidingElem] = HashSet([ - CollidingElem(0), - ]) + let original : @hashset.HashSet[CollidingElem] = HashSet([CollidingElem(0)]) let both = original.add(CollidingElem(24)) - let only_other : @hashset.HashSet[CollidingElem] = HashSet([ - CollidingElem(24), - ]) + let only_other : @hashset.HashSet[CollidingElem] = HashSet([CollidingElem(24)]) assert_true(both.difference(only_other) == original) } diff --git a/internal/strconv/strconv_bool.mbt b/internal/strconv/strconv_bool.mbt index 4fc868495..4d222b1f3 100644 --- a/internal/strconv/strconv_bool.mbt +++ b/internal/strconv/strconv_bool.mbt @@ -17,8 +17,8 @@ #warnings("-deprecated_syntax") pub fn parse_bool(str : StringView) -> Bool raise { lexmatch str with longest { - re"^(true|TRUE|True|t|T|1)$" => true - re"^(false|FALSE|False|f|F|0)$" => false + "^(true|TRUE|True|t|T|1)$" => true + "^(false|FALSE|False|f|F|0)$" => false _ => syntax_err() } } diff --git a/internal/strconv/strconv_number.mbt b/internal/strconv/strconv_number.mbt index d8e395ac3..f826c4f6c 100644 --- a/internal/strconv/strconv_number.mbt +++ b/internal/strconv/strconv_number.mbt @@ -174,8 +174,8 @@ fn parse_inf_nan(rest : StringView) -> Double raise { ['+', .. rest] | rest => (true, rest) } lexmatch rest with longest { - re"^(?i:nan)$" => @double.not_a_number - re"^(?i:inf(inity)?)$" => + "^(?i:nan)$" => @double.not_a_number + "^(?i:inf(inity)?)$" => if pos { @double.infinity } else { diff --git a/strconv/bool.mbt b/strconv/bool.mbt index e02b30258..2652646ef 100644 --- a/strconv/bool.mbt +++ b/strconv/bool.mbt @@ -18,8 +18,8 @@ #warnings("-deprecated_syntax") pub fn parse_bool(str : StringView) -> Bool raise StrConvError { lexmatch str with longest { - re"^(true|TRUE|True|t|T|1)$" => true - re"^(false|FALSE|False|f|F|0)$" => false + "^(true|TRUE|True|t|T|1)$" => true + "^(false|FALSE|False|f|F|0)$" => false _ => syntax_err() } } diff --git a/strconv/number.mbt b/strconv/number.mbt index f351d5119..831e81257 100644 --- a/strconv/number.mbt +++ b/strconv/number.mbt @@ -173,8 +173,8 @@ fn parse_inf_nan(rest : StringView) -> Double raise StrConvError { ['+', .. rest] | rest => (true, rest) } lexmatch rest with longest { - re"^(?i:nan)$" => @double.not_a_number - re"^(?i:inf(inity)?)$" => + "^(?i:nan)$" => @double.not_a_number + "^(?i:inf(inity)?)$" => if pos { @double.infinity } else { From 759adbc1d09ccc4ffba7348720f8cd15a1d03c6c Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Tue, 11 Aug 2026 21:48:18 +0800 Subject: [PATCH 9/9] fix: add re prefix to lexmatch string patterns Newer moonc requires regex literal prefix on lexmatch patterns. Without re"..." prefix, the CI reports parse errors. Co-Authored-By: SeekMoon --- internal/strconv/strconv_bool.mbt | 4 ++-- internal/strconv/strconv_number.mbt | 4 ++-- strconv/bool.mbt | 4 ++-- strconv/number.mbt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/strconv/strconv_bool.mbt b/internal/strconv/strconv_bool.mbt index 4d222b1f3..4fc868495 100644 --- a/internal/strconv/strconv_bool.mbt +++ b/internal/strconv/strconv_bool.mbt @@ -17,8 +17,8 @@ #warnings("-deprecated_syntax") pub fn parse_bool(str : StringView) -> Bool raise { lexmatch str with longest { - "^(true|TRUE|True|t|T|1)$" => true - "^(false|FALSE|False|f|F|0)$" => false + re"^(true|TRUE|True|t|T|1)$" => true + re"^(false|FALSE|False|f|F|0)$" => false _ => syntax_err() } } diff --git a/internal/strconv/strconv_number.mbt b/internal/strconv/strconv_number.mbt index f826c4f6c..d8e395ac3 100644 --- a/internal/strconv/strconv_number.mbt +++ b/internal/strconv/strconv_number.mbt @@ -174,8 +174,8 @@ fn parse_inf_nan(rest : StringView) -> Double raise { ['+', .. rest] | rest => (true, rest) } lexmatch rest with longest { - "^(?i:nan)$" => @double.not_a_number - "^(?i:inf(inity)?)$" => + re"^(?i:nan)$" => @double.not_a_number + re"^(?i:inf(inity)?)$" => if pos { @double.infinity } else { diff --git a/strconv/bool.mbt b/strconv/bool.mbt index 2652646ef..e02b30258 100644 --- a/strconv/bool.mbt +++ b/strconv/bool.mbt @@ -18,8 +18,8 @@ #warnings("-deprecated_syntax") pub fn parse_bool(str : StringView) -> Bool raise StrConvError { lexmatch str with longest { - "^(true|TRUE|True|t|T|1)$" => true - "^(false|FALSE|False|f|F|0)$" => false + re"^(true|TRUE|True|t|T|1)$" => true + re"^(false|FALSE|False|f|F|0)$" => false _ => syntax_err() } } diff --git a/strconv/number.mbt b/strconv/number.mbt index 831e81257..f351d5119 100644 --- a/strconv/number.mbt +++ b/strconv/number.mbt @@ -173,8 +173,8 @@ fn parse_inf_nan(rest : StringView) -> Double raise StrConvError { ['+', .. rest] | rest => (true, rest) } lexmatch rest with longest { - "^(?i:nan)$" => @double.not_a_number - "^(?i:inf(inity)?)$" => + re"^(?i:nan)$" => @double.not_a_number + re"^(?i:inf(inity)?)$" => if pos { @double.infinity } else {