Skip to content

Commit a8a8a13

Browse files
authored
Emit interceptor info correctly when invocation expr is on separate line (#91107)
* Emit interceptor info correctly when invocation expr is on separate line * Add more tests and add helper to udpate baselines
1 parent 17bae6c commit a8a8a13

File tree

9 files changed

+144
-44
lines changed

9 files changed

+144
-44
lines changed

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,27 @@ internal sealed record InterceptorLocationInfo
1515
{
1616
public InterceptorLocationInfo(IInvocationOperation operation)
1717
{
18-
SyntaxNode operationSyntax = operation.Syntax;
19-
TextSpan operationSpan = operationSyntax.Span;
20-
SyntaxTree operationSyntaxTree = operationSyntax.SyntaxTree;
21-
22-
FilePath = GetInterceptorFilePath(operationSyntaxTree, operation.SemanticModel?.Compilation.Options.SourceReferenceResolver);
23-
24-
FileLinePositionSpan span = operationSyntaxTree.GetLineSpan(operationSpan);
25-
LineNumber = span.StartLinePosition.Line + 1;
26-
27-
// Calculate the character offset to the end of the binding invocation detected.
28-
int invocationLength = ((MemberAccessExpressionSyntax)((InvocationExpressionSyntax)operationSyntax).Expression).Expression.Span.Length;
29-
CharacterNumber = span.StartLinePosition.Character + invocationLength + 2;
18+
MemberAccessExpressionSyntax memberAccessExprSyntax = ((MemberAccessExpressionSyntax)((InvocationExpressionSyntax)operation.Syntax).Expression);
19+
SyntaxTree operationSyntaxTree = operation.Syntax.SyntaxTree;
20+
TextSpan memberNameSpan = memberAccessExprSyntax.Name.Span;
21+
FileLinePositionSpan linePosSpan = operationSyntaxTree.GetLineSpan(memberNameSpan);
22+
23+
LineNumber = linePosSpan.StartLinePosition.Line + 1;
24+
CharacterNumber = linePosSpan.StartLinePosition.Character + 1;
25+
FilePath = GetInterceptorFilePath();
26+
27+
// Use the same logic used by the interceptors API for resolving the source mapped value of a path.
28+
// https://github.com/dotnet/roslyn/blob/f290437fcc75dad50a38c09e0977cce13a64f5ba/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs#L1063-L1064
29+
string GetInterceptorFilePath()
30+
{
31+
SourceReferenceResolver? sourceReferenceResolver = operation.SemanticModel?.Compilation.Options.SourceReferenceResolver;
32+
return sourceReferenceResolver?.NormalizePath(operationSyntaxTree.FilePath, baseFilePath: null) ?? operationSyntaxTree.FilePath;
33+
}
3034
}
3135

3236
public string FilePath { get; }
3337
public int LineNumber { get; }
3438
public int CharacterNumber { get; }
35-
36-
// Utilize the same logic used by the interceptors API for resolving the source mapped value of a path.
37-
// https://github.com/dotnet/roslyn/blob/f290437fcc75dad50a38c09e0977cce13a64f5ba/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs#L1063-L1064
38-
private static string GetInterceptorFilePath(SyntaxTree tree, SourceReferenceResolver? resolver) =>
39-
resolver?.NormalizePath(tree.FilePath, baseFilePath: null) ?? tree.FilePath;
4039
}
4140

4241
internal sealed record ConfigurationBinderInterceptorInfo
@@ -52,7 +51,7 @@ public void RegisterOverloadInfo(MethodsToGen_ConfigurationBinder overload, Type
5251
}
5352

5453
public OverloadInterceptorInfo GetOverloadInfo(MethodsToGen_ConfigurationBinder overload) =>
55-
DetermineOverload(overload, initIfNull: false) ?? throw new ArgumentNullException(nameof(overload));
54+
DetermineOverload(overload, initIfNull: false) ?? throw new ArgumentOutOfRangeException(nameof(overload));
5655

5756
private OverloadInterceptorInfo? DetermineOverload(MethodsToGen_ConfigurationBinder overload, bool initIfNull)
5857
{

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.Helpers.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,17 @@ public class CustomICollectionWithoutAnAddMethod : ICollection<string>
160160
public int Count => _items.Count;
161161
public bool IsReadOnly => false;
162162
}
163+
164+
public interface IGeolocation
165+
{
166+
public double Latitude { get; set; }
167+
public double Longitude { get; set; }
168+
}
169+
170+
public sealed record GeolocationRecord : IGeolocation
171+
{
172+
public double Latitude { get; set; }
173+
public double Longitude { get; set; }
174+
}
163175
#endregion
164176
}

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -667,12 +667,6 @@ public struct StructWithParameterlessAndParameterizedCtor
667667
public int MyInt { get; }
668668
}
669669

670-
public interface IGeolocation
671-
{
672-
public double Latitude { get; set; }
673-
public double Longitude { get; set; }
674-
}
675-
676670
[TypeConverter(typeof(GeolocationTypeConverter))]
677671
public struct Geolocation : IGeolocation
678672
{
@@ -704,12 +698,6 @@ public sealed class GeolocationClass : IGeolocation
704698
public double Longitude { get; set; }
705699
}
706700

707-
public sealed record GeolocationRecord : IGeolocation
708-
{
709-
public double Latitude { get; set; }
710-
public double Longitude { get; set; }
711-
}
712-
713701
public class GeolocationWrapper
714702
{
715703
public Geolocation Location { get; set; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Extensions.Configuration;
5+
using Xunit;
6+
7+
namespace Microsoft.Extensions.SourceGeneration.Configuration.Binder.Tests
8+
{
9+
public partial class ConfigurationBinderTests : ConfigurationBinderTestsBase
10+
{
11+
[Fact]
12+
public void GeneratorHandlesInvocationsOnNewline()
13+
{
14+
IConfiguration configuration = TestHelpers.GetConfigurationFromJsonString(@"{""Longitude"":1,""Latitude"":2}");
15+
16+
GeolocationRecord record = configuration.Get<
17+
GeolocationRecord
18+
>();
19+
Verify();
20+
21+
record = (GeolocationRecord)configuration
22+
.Get(typeof(GeolocationRecord), _ => { });
23+
Verify();
24+
25+
TestHelpers
26+
.GetConfigurationFromJsonString(@"{""Longitude"":3,""Latitude"":4}")
27+
.Bind(record);
28+
Verify(3, 4);
29+
30+
int lat = configuration
31+
.GetValue<int>("Latitude");
32+
Assert.Equal(2, lat);
33+
34+
record = configuration.Get
35+
<GeolocationRecord>();
36+
Verify();
37+
38+
record = (GeolocationRecord)configuration
39+
.Get(
40+
typeof(GeolocationRecord), _ =>
41+
{ });
42+
Verify();
43+
44+
TestHelpers
45+
.GetConfigurationFromJsonString(@"{""Longitude"":3,
46+
""Latitude"":4}
47+
")
48+
.Bind(
49+
record
50+
);
51+
Verify(3, 4);
52+
53+
long latLong = configuration
54+
.GetValue<
55+
#if DEBUG
56+
int
57+
#else
58+
long
59+
#endif
60+
>
61+
("Latitude")
62+
;
63+
Assert.Equal(2, lat);
64+
65+
record = (GeolocationRecord)configuration.
66+
Get(typeof(GeolocationRecord), _ => { });
67+
Verify();
68+
69+
record = (GeolocationRecord)
70+
configuration.
71+
Get(typeof(GeolocationRecord), _ => { });
72+
Verify();
73+
74+
record = (GeolocationRecord)
75+
configuration
76+
.Get(typeof(GeolocationRecord), _ => { });
77+
Verify();
78+
79+
void Verify(int longitude = 1, int latitude = 2)
80+
{
81+
Assert.Equal(longitude, record.Longitude);
82+
Assert.Equal(latitude, record.Latitude);
83+
}
84+
}
85+
}
86+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.Threading.Tasks;
55
using Xunit;
66

7-
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
7+
namespace Microsoft.Extensions.SourceGeneration.Configuration.Binder.Tests
88
{
99
public partial class ConfigurationBindingGeneratorTests
1010
{
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using Microsoft.CodeAnalysis.CSharp;
99
using Xunit;
1010

11-
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
11+
namespace Microsoft.Extensions.SourceGeneration.Configuration.Binder.Tests
1212
{
1313
public partial class ConfigurationBindingGeneratorTests
1414
{
@@ -649,7 +649,6 @@ public interface ICustomSet<T> : ISet<T>
649649

650650
await VerifyAgainstBaselineUsingFile("Collections.generated.txt", source, assessDiagnostics: (d) =>
651651
{
652-
Console.WriteLine((d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count(), d.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count()));
653652
Assert.Equal(3, d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count());
654653
Assert.Equal(6, d.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count());
655654
});
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
using System.Threading.Tasks;
1515
using Microsoft.CodeAnalysis;
1616
using Microsoft.CodeAnalysis.CSharp;
17+
using Microsoft.Extensions.Configuration;
18+
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
1719
using Microsoft.Extensions.DependencyInjection;
1820
using Microsoft.Extensions.Options;
19-
using Microsoft.Extensions.SourceGeneration.Configuration.Binder.Tests;
2021
using SourceGenerators.Tests;
2122
using Xunit;
2223

23-
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
24+
namespace Microsoft.Extensions.SourceGeneration.Configuration.Binder.Tests
2425
{
2526
[ActiveIssue("https://github.com/dotnet/runtime/issues/52062", TestPlatforms.Browser)]
2627
public partial class ConfigurationBindingGeneratorTests : ConfigurationBinderTestsBase
@@ -388,11 +389,24 @@ private static async Task VerifyAgainstBaselineUsingFile(
388389
var (d, r) = await RunGenerator(testSourceCode, languageVersion);
389390
bool success = RoslynTestUtils.CompareLines(expectedLines, r[0].SourceText, out string errorMessage);
390391

391-
#if !SKIP_BASELINES
392+
#if UPDATE_BASELINES
393+
if (!success)
394+
{
395+
string? repoRootDir = Environment.GetEnvironmentVariable("RepoRootDir");
396+
Assert.True(repoRootDir is not null, "To update baselines, specifiy the root runtime repo dir");
397+
398+
IEnumerable<string> lines = r[0].SourceText.Lines.Select(l => l.ToString());
399+
string source = string.Join(Environment.NewLine, lines).TrimEnd(Environment.NewLine.ToCharArray()) + Environment.NewLine;
400+
path = Path.Combine($"{repoRootDir}\\src\\libraries\\Microsoft.Extensions.Configuration.Binder\\tests\\SourceGenerationTests\\", path);
401+
402+
await File.WriteAllTextAsync(path, source).ConfigureAwait(false);
403+
success = true;
404+
}
405+
#endif
406+
392407
Assert.Single(r);
393408
(assessDiagnostics ?? ((d) => Assert.Empty(d))).Invoke(d);
394409
Assert.True(success, errorMessage);
395-
#endif
396410
}
397411

398412
private static async Task<(ImmutableArray<Diagnostic>, ImmutableArray<GeneratedSourceResult>)> RunGenerator(

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests.csproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<PropertyGroup>
1212
<DefineConstants>$(DefineConstants);BUILDING_SOURCE_GENERATOR_TESTS;ROSLYN4_0_OR_GREATER;ROSLYN4_4_OR_GREATER</DefineConstants>
1313
<DefineConstants Condition="'$(LaunchTestDebugger)' == 'true'">$(DefineConstants);LAUNCH_DEBUGGER</DefineConstants>
14-
<DefineConstants Condition="'$(SkipBaselines)' == 'true'">$(DefineConstants);SKIP_BASELINES</DefineConstants>
14+
<DefineConstants Condition="'$(UpdateBaselines)' == 'true'">$(DefineConstants);UPDATE_BASELINES</DefineConstants>
1515
</PropertyGroup>
1616

1717
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
@@ -28,6 +28,7 @@
2828
<Compile Include="..\Common\ConfigurationBinderTests.Helpers.cs" Link="Common\ConfigurationBinderTests.Helpers.cs" />
2929
<Compile Include="..\Common\ConfigurationBinderTests.TestClasses.cs" Link="Common\ConfigurationBinderTests.TestClasses.cs" />
3030
<Compile Include="..\Common\ConfigurationBinderTests.TestClasses.Collections.cs" Link="Common\ConfigurationBinderTests.TestClasses.Collections.cs" />
31+
<Compile Include="ConfigurationBinderTests.Generator.cs" />
3132
</ItemGroup>
3233

3334
<ItemGroup>
@@ -50,9 +51,9 @@
5051
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
5152
</Content>
5253

53-
<Compile Include="ConfigurationBindingGeneratorTests.cs" />
54-
<Compile Include="ConfigurationBindingGeneratorTests.Baselines.cs" />
55-
<Compile Include="ConfigurationBindingGeneratorTests.Baselines.Options.cs" />
54+
<Compile Include="GeneratorTests.cs" />
55+
<Compile Include="GeneratorTests.Baselines.cs" />
56+
<Compile Include="GeneratorTests.Baselines.Options.cs" />
5657
</ItemGroup>
5758

5859
<Target Name="FixIncrementalCoreCompileWithAnalyzers" BeforeTargets="CoreCompile">

src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/Common/OptionsBuilderConfigurationExtensionsTests.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public static void BindConfiguration_ThrowsForNullConfigurationSectionPath()
3131

3232
Assert.Throws<ArgumentNullException>("configSectionPath", () =>
3333
{
34-
optionsBuilder.BindConfiguration(configSectionPath);
34+
optionsBuilder
35+
.BindConfiguration(configSectionPath);
3536
});
3637
}
3738

@@ -170,8 +171,8 @@ public static void BindConfiguration_UpdatesOptionOnConfigurationUpdate()
170171
services.AddSingleton<IConfiguration>(new ConfigurationBuilder()
171172
.Add(configSource)
172173
.Build());
173-
OptionsBuilder<FakeOptions> optionsBuilder = services.AddOptions<FakeOptions>();
174-
_ = optionsBuilder.BindConfiguration(configSectionName);
174+
_ = services.AddOptions<FakeOptions>()
175+
.BindConfiguration(configSectionName);
175176
using ServiceProvider serviceProvider = services.BuildServiceProvider();
176177
var optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<FakeOptions>>();
177178
bool updateHasRun = false;

0 commit comments

Comments
 (0)