Skip to content

Commit d8560ab

Browse files
fix: Correct analyzer test spans and clarify allocation documentation
- Fix OPENTXT007 diagnostic span in tests (should include full expression) - Clarify that SplitToEnumerableNoAlloc allocates strings per segment - Document that Regex-based methods have unavoidable Match allocations - Note that JoinNoAlloc(IEnumerable) may box struct enumerators - Update README with accurate allocation expectations
1 parent ffe960e commit d8560ab

6 files changed

Lines changed: 23 additions & 12 deletions

File tree

Analyzers.Tests/SmartDetectionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ void TestMethod()
351351
string text = "a,b,c,d,e";
352352
353353
// Split with FirstOrDefault - double diagnostic
354-
string first = {|OPENTXT007:{|OPENTXT002:text.Split(',')|}|}.FirstOrDefault();
354+
string first = {|OPENTXT007:{|OPENTXT002:text.Split(',')|}.FirstOrDefault()|};
355355
356356
// Split in foreach
357357
foreach (var part in {|OPENTXT008:text.Split(',')|})

Analyzers.Tests/SplitAnalyzerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class TestClass
5454
void TestMethod()
5555
{
5656
string text = "a,b,c";
57-
string first = {|OPENTXT007:{|OPENTXT002:text.Split(',')|}|}.First();
57+
string first = {|OPENTXT007:{|OPENTXT002:text.Split(',')|}.First()|};
5858
}
5959
}
6060
""";

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ Comparing `string.Split()` (BCL) vs `SplitAsSegments` (IEnumerable) vs `SplitAsS
3636
| | SplitAsSegments(string) | 215.2 ns | 128 B |
3737
| | BCL Split(string) | 227.1 ns | 696 B |
3838

39-
> **Key Takeaway:** The `*NoAlloc` methods achieve **zero heap allocations** while maintaining competitive performance—ideal for high-throughput scenarios where GC pressure matters.
39+
> **Key Takeaway:** The `SplitAsSegmentsNoAlloc` methods achieve **zero heap allocations** when iterating `StringSegment` values—ideal for high-throughput scenarios where GC pressure matters.
40+
>
41+
> **Note:** Regex-based split methods have unavoidable `Match` object allocations. Methods returning `string` (like `SplitToEnumerableNoAlloc`) allocate per segment.
4042
4143
---
4244

Source/Extensions.Split.NoAlloc.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ public static partial class TextExtensions
77
/// <summary>
88
/// Enumerates a string by segments that are separated by the split character.
99
/// </summary>
10+
/// <remarks>
11+
/// Note: Each segment allocates a new string via ToString(). For true zero-allocation,
12+
/// use <see cref="SplitAsSegmentsNoAlloc(string, char, StringSplitOptions)"/> which returns StringSegments.
13+
/// </remarks>
1014
/// <param name="source">The source characters to look through.</param>
1115
/// <param name="splitCharacter">The character to find.</param>
1216
/// <param name="options">Can specify to omit empty entries.</param>
13-
/// <returns>A zero-allocation enumerable of the string segments.</returns>
17+
/// <returns>A ValueEnumerable of string segments (allocates per segment).</returns>
1418
[CLSCompliant(false)]
1519
public static ValueEnumerable<ZLinq.Linq.Select<StringSegmentSplitEnumerator, StringSegment, string>, string> SplitToEnumerableNoAlloc(
1620
this string source,

Source/Extensions.StringSegment.NoAlloc.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ public static ValueEnumerable<StringSegmentSplitEnumerator, StringSegment> Split
3030
: throw new ArgumentNullException(nameof(source), "Must be a StringSegment that has a value (is not null).");
3131

3232
/// <summary>
33-
/// Enumerates a string by segments that are separated by the regular expression matches (zero-allocation).
33+
/// Enumerates a string by segments that are separated by the regular expression matches.
3434
/// </summary>
35+
/// <remarks>
36+
/// Note: Regex matching internally allocates Match objects per match. The enumerator itself
37+
/// is stack-allocated, but regex operations have unavoidable allocations.
38+
/// </remarks>
3539
/// <param name="source">The source characters to look through.</param>
3640
/// <param name="pattern">The pattern to split by.</param>
3741
/// <param name="options">Can specify to omit empty entries.</param>
38-
/// <returns>A ValueEnumerable of the segments (zero-allocation when used with foreach or ZLinq).</returns>
42+
/// <returns>A ValueEnumerable of the segments.</returns>
3943
[CLSCompliant(false)]
4044
public static ValueEnumerable<RegexSplitSegmentEnumerator, StringSegment> SplitAsSegmentsNoAlloc(
4145
this string source,
@@ -81,11 +85,15 @@ public static ValueEnumerable<StringSegmentSequenceSplitEnumerator, StringSegmen
8185
}
8286

8387
/// <summary>
84-
/// Joins a sequence of segments with a separator sequence (zero-allocation).
88+
/// Joins a sequence of segments with a separator sequence.
8589
/// </summary>
90+
/// <remarks>
91+
/// Note: This overload accepts IEnumerable which may cause boxing if the source is a struct enumerator.
92+
/// For true zero-allocation joins, use the ValueEnumerable overloads returned by split operations.
93+
/// </remarks>
8694
/// <param name="source">The segments to join.</param>
8795
/// <param name="between">The segment to place between each segment.</param>
88-
/// <returns>A ValueEnumerable of the joined segments (zero-allocation when used with foreach or ZLinq).</returns>
96+
/// <returns>A ValueEnumerable of the joined segments.</returns>
8997
/// <exception cref="System.ArgumentNullException">The source is null.</exception>
9098
[CLSCompliant(false)]
9199
public static ValueEnumerable<StringSegmentJoinEnumerator, StringSegment> JoinNoAlloc(

Tests/Open.Text.Tests.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@
3535
<ItemGroup>
3636
<ProjectReference Include="..\Source\Open.Text.csproj" />
3737
<!-- Reference the analyzer to suppress false IDE0078 suggestions for StringComparable -->
38-
<ProjectReference Include="..\Analyzers\Open.Text.Analyzers.csproj"
39-
ReferenceOutputAssembly="false"
40-
OutputItemType="Analyzer"
41-
PrivateAssets="all" />
38+
<ProjectReference Include="..\Analyzers\Open.Text.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" PrivateAssets="all" />
4239
</ItemGroup>
4340

4441
</Project>

0 commit comments

Comments
 (0)