From 1bd2eb7943a85cc934da728eaed14eed52d16931 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 2 May 2024 17:38:40 +0800 Subject: [PATCH] Specialised substring equality for annotated strs (#54302) The least-bad idea I've had so far for fixing #53042. I figure this fixes the bug raised there, and we can always switch to a clearly-better solution if one appears. The fact that only a string without annotations is equal to a non-annotated string (in order to preserve the transitivity of equality), makes the generic fallback methods for string comparison insufficient. As such, ==(::AnnoatedString, ::AbstractString) is implemented in annotated.jl, but this issue re-appears when dealing with substrings. The obvious solution is to just implement a specialised method for substrings. This does seem potentially a bit whack-a-mole, but I'm worried that cleverer solutions might come with subtle issues of their own. For now, let's try the simple and obvious solution, and improve it later if we can work out a nicer way of handling this issue in general. --- base/strings/annotated.jl | 18 ++++++++++++++++++ test/strings/annotated.jl | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index 1ffab7a68f9e5..65fd9cf485738 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -203,6 +203,24 @@ cmp(a::AnnotatedString, b::AnnotatedString) = cmp(a.string, b.string) ==(a::AnnotatedString, b::AbstractString) = isempty(a.annotations) && a.string == b ==(a::AbstractString, b::AnnotatedString) = isempty(b.annotations) && a == b.string +# To prevent substring equality from hitting the generic fallback + +function ==(a::SubString{<:AnnotatedString}, b::SubString{<:AnnotatedString}) + SubString(a.string.string, a.offset, a.ncodeunits, Val(:noshift)) == + SubString(b.string.string, b.offset, b.ncodeunits, Val(:noshift)) && + annotations(a) == annotations(b) +end + +==(a::SubString{<:AnnotatedString}, b::AnnotatedString) = + annotations(a) == annotations(b) && SubString(a.string.string, a.offset, a.ncodeunits, Val(:noshift)) == b.string + +==(a::SubString{<:AnnotatedString}, b::AbstractString) = + isempty(annotations(a)) && SubString(a.string.string, a.offset, a.ncodeunits, Val(:noshift)) == b + +==(a::AbstractString, b::SubString{<:AnnotatedString}) = b == a + +==(a::AnnotatedString, b::SubString{<:AnnotatedString}) = b == a + """ annotatedstring(values...) diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index f16c2bec348ca..13d463b4c7d21 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -12,6 +12,8 @@ @test "a" * str == Base.AnnotatedString("asome string") @test str * "a" == Base.AnnotatedString("some stringa") @test str * str == Base.AnnotatedString("some stringsome string") + @test str[3:4] == SubString("me") + @test SubString("me") == str[3:4] Base.annotate!(str, 1:4, :thing => 0x01) Base.annotate!(str, 6:11, :other => 0x02) Base.annotate!(str, 1:11, :all => 0x03) @@ -21,6 +23,8 @@ # └───┰─────┘ # :all @test str[3:4] == SubString(str, 3, 4) + @test str[3:4] != SubString("me") + @test SubString("me") != str[3:4] @test Base.AnnotatedString(str[3:4]) == Base.AnnotatedString("me", [(1:2, :thing => 0x01), (1:2, :all => 0x03)]) @test Base.AnnotatedString(str[3:6]) ==