Skip to content

Commit 0511818

Browse files
committed
Fixed MakeFullyQualifiedArgumentName
1 parent 09d3e3e commit 0511818

4 files changed

Lines changed: 75 additions & 28 deletions

File tree

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Copyright>(c) $([System.DateTime]::Now.Year), Pawel Gerr. All rights reserved.</Copyright>
5-
<VersionPrefix>9.6.4</VersionPrefix>
5+
<VersionPrefix>9.6.5</VersionPrefix>
66
<Authors>Pawel Gerr</Authors>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageProjectUrl>https://github.com/PawelGerr/Thinktecture.Runtime.Extensions</PackageProjectUrl>

src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/ContainingTypesExtensions.cs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,44 @@ public static string MakeFullyQualifiedArgumentName(
1111
bool skipRootContainingType,
1212
StringBuilder sb)
1313
{
14-
var sbLength = sb.Length;
14+
var originalLen = sb.Length;
1515

1616
for (var i = skipRootContainingType ? 1 : 0; i < containingTypes.Count; i++)
17-
{
18-
var containingType = containingTypes[i];
19-
sb.Append(containingType.Name);
20-
}
17+
sb.Append(containingTypes[i].Name);
2118

2219
sb.Append(typeMemberName);
2320

24-
var firstChar = sb[sbLength];
21+
// Nothing appended -> return empty string.
22+
if (sb.Length == originalLen)
23+
return string.Empty;
24+
25+
var len = sb.Length;
26+
27+
for (var i = originalLen; i < len; i++)
28+
{
29+
var ch = sb[i];
2530

26-
sb[sbLength] = firstChar != '_' || (sb.Length > sbLength && Char.IsDigit(sb[sbLength + 1]))
27-
? Char.ToLowerInvariant(firstChar)
28-
: Char.ToLowerInvariant(sb[sbLength + 1]);
31+
if (!char.IsLetter(ch))
32+
continue;
33+
34+
if (char.IsUpper(ch))
35+
{
36+
sb[i] = char.ToLowerInvariant(ch);
37+
38+
// Lowercase the rest of the initial consecutive uppercase run.
39+
for (var j = i + 1; j < len && char.IsUpper(sb[j]); j++)
40+
{
41+
sb[j] = char.ToLowerInvariant(sb[j]);
42+
}
43+
}
44+
45+
// Either we handled the uppercase run or the first letter was already lowercase.
46+
break;
47+
}
2948

30-
var argName = sb.ToString(sbLength, sb.Length - sbLength);
31-
sb.Length = sbLength;
49+
var result = sb.ToString(originalLen, len - originalLen);
50+
sb.Length = originalLen;
3251

33-
return argName;
52+
return result;
3453
}
3554
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Text;
2+
using Thinktecture.CodeAnalysis;
3+
using System.Linq;
4+
5+
namespace Thinktecture.Runtime.Tests.ContainingTypesExtensionsTests;
6+
7+
public class MakeFullyQualifiedArgumentName
8+
{
9+
[Theory]
10+
[InlineData(new string[0], "Foo", false, "foo")]
11+
[InlineData(new[] { "Outer" }, "Foo", false, "outerFoo")]
12+
[InlineData(new[] { "Outer", "Inner" }, "Foo", false, "outerInnerFoo")]
13+
[InlineData(new[] { "Outer", "Inner" }, "Foo", true, "innerFoo")]
14+
[InlineData(new string[0], "_Name", false, "_name")]
15+
[InlineData(new string[0], "ABC", false, "abc")]
16+
[InlineData(new string[0], "_ABC", false, "_abc")]
17+
[InlineData(new string[0], "_9Value", false, "_9value")] // note: first visible char '9' stays; underscore kept
18+
[InlineData(new string[0], "", false, "")]
19+
public void MakeFullyQualifiedArgumentName_Works(string[] types, string member, bool skipRoot, string expected)
20+
{
21+
var list = types.Select(n => new ContainingTypeState(n, true, false, [])).ToList();
22+
var sb = new StringBuilder("prefix"); // ensure restoration works
23+
var result = ContainingTypesExtensions.MakeFullyQualifiedArgumentName(list, member, skipRoot, sb);
24+
25+
result.Should().Be(expected);
26+
sb.Length.Should().Be("prefix".Length); // sb is restored
27+
}
28+
}

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/RegularUnionSourceGeneratorTests.Should_handle_special_chars.verified.txt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ abstract partial class _1TestUnionWithSpecialChars :
2525
/// <summary>
2626
/// Executes an action depending on the current type.
2727
/// </summary>
28-
/// <param name="_1Test">The action to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
28+
/// <param name="_1test">The action to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
2929
[global::System.Diagnostics.DebuggerStepThroughAttribute]
3030
public void Switch(
31-
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Action<global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test> @_1Test)
31+
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Action<global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test> @_1test)
3232
{
3333
switch (this)
3434
{
3535
case global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test value:
36-
@_1Test(value);
36+
@_1test(value);
3737
return;
3838
default:
3939
throw new global::System.ArgumentOutOfRangeException($"Unexpected type '{this.GetType().FullName}'.");
@@ -46,19 +46,19 @@ abstract partial class _1TestUnionWithSpecialChars :
4646
/// Executes an action depending on the current type.
4747
/// </summary>
4848
/// <param name="state">State to be passed to the callbacks.</param>
49-
/// <param name="_1Test">The action to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
49+
/// <param name="_1test">The action to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
5050
[global::System.Diagnostics.DebuggerStepThroughAttribute]
5151
public void Switch<TState>(
5252
TState @state,
53-
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Action<TState, global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test> @_1Test)
53+
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Action<TState, global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test> @_1test)
5454
#if NET9_0_OR_GREATER
5555
where TState : allows ref struct
5656
#endif
5757
{
5858
switch (this)
5959
{
6060
case global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test value:
61-
@_1Test(@state, value);
61+
@_1test(@state, value);
6262
return;
6363
default:
6464
throw new global::System.ArgumentOutOfRangeException($"Unexpected type '{this.GetType().FullName}'.");
@@ -70,18 +70,18 @@ abstract partial class _1TestUnionWithSpecialChars :
7070
/// <summary>
7171
/// Executes a function depending on the current type.
7272
/// </summary>
73-
/// <param name="_1Test">The function to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
73+
/// <param name="_1test">The function to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
7474
[global::System.Diagnostics.DebuggerStepThroughAttribute]
7575
public TResult Switch<TResult>(
76-
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Func<global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test, TResult> @_1Test)
76+
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Func<global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test, TResult> @_1test)
7777
#if NET9_0_OR_GREATER
7878
where TResult : allows ref struct
7979
#endif
8080
{
8181
switch (this)
8282
{
8383
case global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test value:
84-
return @_1Test(value);
84+
return @_1test(value);
8585
default:
8686
throw new global::System.ArgumentOutOfRangeException($"Unexpected type '{this.GetType().FullName}'.");
8787
}
@@ -93,11 +93,11 @@ abstract partial class _1TestUnionWithSpecialChars :
9393
/// Executes a function depending on the current type.
9494
/// </summary>
9595
/// <param name="state">State to be passed to the callbacks.</param>
96-
/// <param name="_1Test">The function to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
96+
/// <param name="_1test">The function to execute if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
9797
[global::System.Diagnostics.DebuggerStepThroughAttribute]
9898
public TResult Switch<TState, TResult>(
9999
TState @state,
100-
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Func<TState, global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test, TResult> @_1Test)
100+
[global::JetBrains.Annotations.InstantHandleAttribute] global::System.Func<TState, global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test, TResult> @_1test)
101101
#if NET9_0_OR_GREATER
102102
where TResult : allows ref struct
103103
where TState : allows ref struct
@@ -106,7 +106,7 @@ abstract partial class _1TestUnionWithSpecialChars :
106106
switch (this)
107107
{
108108
case global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test value:
109-
return @_1Test(@state, value);
109+
return @_1test(@state, value);
110110
default:
111111
throw new global::System.ArgumentOutOfRangeException($"Unexpected type '{this.GetType().FullName}'.");
112112
}
@@ -117,18 +117,18 @@ abstract partial class _1TestUnionWithSpecialChars :
117117
/// <summary>
118118
/// Maps current instance to an instance of type <typeparamref name="TResult"/>.
119119
/// </summary>
120-
/// <param name="_1Test">The instance to return if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
120+
/// <param name="_1test">The instance to return if the current type is <see cref="global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test"/>.</param>
121121
[global::System.Diagnostics.DebuggerStepThroughAttribute]
122122
public TResult Map<TResult>(
123-
TResult @_1Test)
123+
TResult @_1test)
124124
#if NET9_0_OR_GREATER
125125
where TResult : allows ref struct
126126
#endif
127127
{
128128
switch (this)
129129
{
130130
case global::Thinktecture.Tests._1TestUnionWithSpecialChars._1Test value:
131-
return @_1Test;
131+
return @_1test;
132132
default:
133133
throw new global::System.ArgumentOutOfRangeException($"Unexpected type '{this.GetType().FullName}'.");
134134
}

0 commit comments

Comments
 (0)