Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to support .NET 9 #2275

Merged
merged 9 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,22 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
dotnet: ['net48', 'net6.0', 'net7.0', 'net8.0']
dotnet: ['net48', 'net8.0', 'net9.0']

steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
-
name: Run tests
run: dotnet test -f ${{ matrix.dotnet }}
run: dotnet test -c Debug -f ${{ matrix.dotnet }}
-
name: Upload Test Results
if: always()
Expand All @@ -47,12 +54,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: ['net6.0', 'net7.0', 'net8.0']
dotnet: ['net8.0', 'net9.0']

steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
-
name: Run tests
run: dotnet test -f ${{ matrix.dotnet }}
Expand Down
91 changes: 44 additions & 47 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 6" Condition="$(TargetFramework) == 'net6.0'">
<MicrosoftTestHostVer>[6.0.28,7)</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 7" Condition="$(TargetFramework) == 'net7.0'">
<MicrosoftTestHostVer>7.0.17</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 8" Condition="$(TargetFramework) == 'net8.0'">
<MicrosoftTestHostVer>8.0.3</MicrosoftTestHostVer>
</PropertyGroup>
<ItemGroup Label="Runtime dependencies">
<PackageVersion Include="HttpMultipartParser" Version="8.4.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="CsvHelper" Version="33.0.1" />
<PackageVersion Include="PolySharp" Version="1.14.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
<PackageVersion Include="WireMock.Net" Version="1.5.60" />
<PackageVersion Include="WireMock.Net.FluentAssertions" Version="1.5.51" />
</ItemGroup>
<ItemGroup Label="Compile dependencies">
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="MinVer" Version="5.0.0" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.2.0" />
</ItemGroup>
<ItemGroup Label="Testing dependencies">
<PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="HttpTracer" Version="2.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="Polly" Version="8.4.1" />
<PackageVersion Include="rest-mock-core" Version="0.7.12" />
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
<PackageVersion Include="System.Net.Http.Json" Version="8.0.0" />
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="All" />
<PackageVersion Include="xunit" Version="2.9.0" />
</ItemGroup>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 8" Condition="$(TargetFramework) == 'net8.0'">
<MicrosoftTestHostVer>8.0.3</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 9" Condition="$(TargetFramework) == 'net9.0'">
<MicrosoftTestHostVer>9.0.0</MicrosoftTestHostVer>
</PropertyGroup>
<ItemGroup Label="Runtime dependencies">
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageVersion Include="CsvHelper" Version="33.0.1"/>
<PackageVersion Include="System.Text.Json" Version="9.0.0"/>
</ItemGroup>
<ItemGroup Label="Compile dependencies">
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0"/>
<PackageVersion Include="MinVer" Version="6.0.0"/>
<PackageVersion Include="Nullable" Version="1.3.1"/>
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3"/>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0"/>
</ItemGroup>
<ItemGroup Label="Testing dependencies">
<PackageVersion Include="HttpMultipartParser" Version="8.4.0"/>
<PackageVersion Include="PolySharp" Version="1.15.0"/>
<PackageVersion Include="AutoFixture" Version="4.18.1"/>
<PackageVersion Include="coverlet.collector" Version="6.0.2"/>
<PackageVersion Include="FluentAssertions" Version="7.0.0"/>
<PackageVersion Include="HttpTracer" Version="2.1.1"/>
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)"/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageVersion Include="Moq" Version="4.20.72"/>
<PackageVersion Include="Polly" Version="8.5.0"/>
<PackageVersion Include="rest-mock-core" Version="0.7.12"/>
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0"/>
<PackageVersion Include="System.Net.Http.Json" Version="9.0.0"/>
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="All"/>
<PackageVersion Include="xunit" Version="2.9.2"/>
<PackageVersion Include="WireMock.Net" Version="1.6.10"/>
<PackageVersion Include="WireMock.Net.FluentAssertions" Version="1.5.51"/>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
2 changes: 1 addition & 1 deletion gen/SourceGenerator/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static IEnumerable<ClassDeclarationSyntax> FindClasses(this Compilation c
.SelectMany(model => model.SyntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>())
.Where(predicate);

public static IEnumerable<ClassDeclarationSyntax> FindAnnotatedClass(this Compilation compilation, string attributeName, bool strict) {
public static IEnumerable<ClassDeclarationSyntax> FindAnnotatedClasses(this Compilation compilation, string attributeName, bool strict) {
return compilation.FindClasses(
syntax => syntax.AttributeLists.Any(list => list.Attributes.Any(CheckAttribute))
);
Expand Down
30 changes: 20 additions & 10 deletions gen/SourceGenerator/ImmutableGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,28 @@

namespace SourceGenerator;

[Generator]
public class ImmutableGenerator : ISourceGenerator {
public void Initialize(GeneratorInitializationContext context) { }

public void Execute(GeneratorExecutionContext context) {
var compilation = context.Compilation;
[Generator(LanguageNames.CSharp)]
public class ImmutableGenerator : IIncrementalGenerator {
public void Initialize(IncrementalGeneratorInitializationContext context) {
var c = context.CompilationProvider.SelectMany((x, _) => GetImmutableClasses(x));

context.RegisterSourceOutput(
c.Collect(),
static (ctx, sources) => {
foreach (var source in sources) {
ctx.AddSource(source.Item1, source.Item2);
}
}
);
return;

var mutableClasses = compilation.FindAnnotatedClass("GenerateImmutable", strict: true);
IEnumerable<(string, SourceText)> GetImmutableClasses(Compilation compilation) {
var mutableClasses = compilation.FindAnnotatedClasses("GenerateImmutable", strict: true);

foreach (var mutableClass in mutableClasses) {
var immutableClass = GenerateImmutableClass(mutableClass, compilation);
context.AddSource($"ReadOnly{mutableClass.Identifier.Text}.cs", SourceText.From(immutableClass, Encoding.UTF8));
foreach (var mutableClass in mutableClasses) {
var immutableClass = GenerateImmutableClass(mutableClass, compilation);
yield return ($"ReadOnly{mutableClass.Identifier.Text}.cs", SourceText.From(immutableClass, Encoding.UTF8));
}
}
}

Expand Down
65 changes: 36 additions & 29 deletions gen/SourceGenerator/InheritedCloneGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,44 @@

namespace SourceGenerator;

[Generator]
public class InheritedCloneGenerator : ISourceGenerator {
[Generator(LanguageNames.CSharp)]
public class InheritedCloneGenerator : IIncrementalGenerator {
const string AttributeName = "GenerateClone";

public void Initialize(GeneratorInitializationContext context) { }

public void Execute(GeneratorExecutionContext context) {
var compilation = context.Compilation;

var candidates = compilation.FindAnnotatedClass(AttributeName, false);

foreach (var candidate in candidates) {
var semanticModel = compilation.GetSemanticModel(candidate.SyntaxTree);
var genericClassSymbol = semanticModel.GetDeclaredSymbol(candidate);
if (genericClassSymbol == null) continue;

// Get the method name from the attribute Name argument
var attributeData = genericClassSymbol.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == $"{AttributeName}Attribute");
var methodName = (string)attributeData.NamedArguments.FirstOrDefault(arg => arg.Key == "Name").Value.Value;

// Get the generic argument type where properties need to be copied from
var attributeSyntax = candidate.AttributeLists
.SelectMany(l => l.Attributes)
.FirstOrDefault(a => a.Name.ToString().StartsWith(AttributeName));
if (attributeSyntax == null) continue; // This should never happen

var typeArgumentSyntax = ((GenericNameSyntax)attributeSyntax.Name).TypeArgumentList.Arguments[0];
var typeSymbol = (INamedTypeSymbol)semanticModel.GetSymbolInfo(typeArgumentSyntax).Symbol;

var code = GenerateMethod(candidate, genericClassSymbol, typeSymbol, methodName);
context.AddSource($"{genericClassSymbol.Name}.Clone.g.cs", SourceText.From(code, Encoding.UTF8));
public void Initialize(IncrementalGeneratorInitializationContext context) {
var c = context.CompilationProvider.SelectMany((x, _) => GetClones(x));

context.RegisterSourceOutput(
c.Collect(),
static (ctx, sources) => {
foreach (var source in sources) {
ctx.AddSource(source.Item1, source.Item2);
}
}
);
return;

IEnumerable<(string, SourceText)> GetClones(Compilation compilation) {
var candidates = compilation.FindAnnotatedClasses(AttributeName, false);

foreach (var candidate in candidates) {
var semanticModel = compilation.GetSemanticModel(candidate.SyntaxTree);
var genericClassSymbol = semanticModel.GetDeclaredSymbol(candidate);
if (genericClassSymbol == null) continue;

var attributeData = genericClassSymbol.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == $"{AttributeName}Attribute");
var methodName = (string)attributeData.NamedArguments.FirstOrDefault(arg => arg.Key == "Name").Value.Value;
var baseType = attributeData.NamedArguments.FirstOrDefault(arg => arg.Key == "BaseType").Value.Value;

// Get the generic argument type where properties need to be copied from
var attributeSyntax = candidate.AttributeLists
.SelectMany(l => l.Attributes)
.FirstOrDefault(a => a.Name.ToString().StartsWith(AttributeName));
if (attributeSyntax == null) continue; // This should never happen

var code = GenerateMethod(candidate, genericClassSymbol, (INamedTypeSymbol)baseType, methodName);
yield return ($"{genericClassSymbol.Name}.Clone.g.cs", SourceText.From(code, Encoding.UTF8));
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'RestSharp.sln'))\props\Common.props"/>
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net471;net48;net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net471;net48;net8.0;net9.0</TargetFrameworks>
<PackageIcon>restsharp.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://restsharp.dev</PackageProjectUrl>
Expand Down
6 changes: 0 additions & 6 deletions src/RestSharp/Authenticators/OAuth/OAuthTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,6 @@ public static string GetNonce() {
/// </summary>
/// <param name="value">The value to escape.</param>
/// <returns>The escaped value.</returns>
/// <remarks>
/// The <see cref="Uri.EscapeDataString" /> method is <i>supposed</i> to take on
/// RFC 3986 behavior if certain elements are present in a .config file. Even if this
/// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
/// host actually having this configuration element present.
/// </remarks>
[return: NotNullIfNotNull(nameof(value))]
public static string? UrlEncodeRelaxed(string? value) {
if (value == null) return null;
Expand Down
5 changes: 3 additions & 2 deletions src/RestSharp/Extensions/GenerateImmutableAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ namespace RestSharp.Extensions;
class GenerateImmutableAttribute : Attribute;

[AttributeUsage(AttributeTargets.Class)]
class GenerateCloneAttribute<T> : Attribute where T : class {
public string? Name { get; set; }
class GenerateCloneAttribute : Attribute {
public Type? BaseType { get; set; }
public string? Name { get; set; }
};

[AttributeUsage(AttributeTargets.Property)]
Expand Down
10 changes: 0 additions & 10 deletions src/RestSharp/Options/RestClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,6 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba
/// </summary>
public CookieContainer? CookieContainer { get; set; }

/// <summary>
/// Maximum request duration in milliseconds. When the request timeout is specified using <seealso cref="RestRequest.Timeout"/>,
/// the lowest value between the client timeout and request timeout will be used.
/// </summary>
[Obsolete("Use Timeout instead.")]
public int MaxTimeout {
get => (int) (Timeout?.TotalMilliseconds ?? 0);
set => Timeout = TimeSpan.FromMilliseconds(value);
}

/// <summary>
/// Request duration. Used when the request timeout is not specified using <seealso cref="RestRequest.Timeout"/>,
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/RestSharp/Response/RestResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace RestSharp;
/// Container for data sent back from API including deserialized data
/// </summary>
/// <typeparam name="T">Type of data to deserialize to</typeparam>
[GenerateClone<RestResponse>(Name = "FromResponse")]
[GenerateClone(BaseType = typeof(RestResponse), Name = "FromResponse")]
[DebuggerDisplay($"{{{nameof(DebuggerDisplay)}()}}")]
public partial class RestResponse<T>(RestRequest request) : RestResponse(request) {
/// <summary>
Expand Down
18 changes: 12 additions & 6 deletions src/RestSharp/RestSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
<ItemGroup Condition="$(AddPolyfills) == 'true'">
<PackageReference Include="Nullable" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup Condition="$(AddSystemTextJson) == 'true'">
<PackageReference Include="System.Text.Json"/>
</ItemGroup>
<ItemGroup>
<Compile Update="RestClient.Extensions.Params.cs">
<DependentUpon>RestClient.Extensions.cs</DependentUpon>
Expand Down Expand Up @@ -63,12 +60,21 @@
</Compile>
</ItemGroup>
<ItemGroup Label="Source generator">
<ProjectReference Include="$(RepoRoot)\gen\SourceGenerator\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="$(RepoRoot)\gen\SourceGenerator\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup Label="Package README">
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net"/>
<Using Include="System.Net" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net471'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion test/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<TargetFrameworks>net48;net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net48;net8.0;net9.0</TargetFrameworks>
<Nullable>disable</Nullable>
<NoWarn>xUnit1033</NoWarn>
<VSTestLogger>trx%3bLogFileName=$(MSBuildProjectName).trx</VSTestLogger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<IsTestProject>false</IsTestProject>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\RestSharp\RestSharp.csproj" />
Expand Down
Loading