Skip to content

Commit

Permalink
Merge branch 'main' into update-xunit
Browse files Browse the repository at this point in the history
  • Loading branch information
FreeApophis authored Jan 14, 2025
2 parents c4c54ed + 56bc6b5 commit 0b0b9d0
Show file tree
Hide file tree
Showing 37 changed files with 296 additions and 61 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:

env:
DOTNET_NOLOGO: 1
# This is for some reason set to 'all' on CI which means
# we get warnings for transitive dependencies (e.g. from Messerli.CodeStyle -> StyleCop)
NuGetAuditMode: direct

jobs:
build:
Expand All @@ -23,6 +26,10 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
name: Install Current .NET SDK
- uses: actions/setup-dotnet@v4
name: 'Install .NET SDK 8.0'
with:
dotnet-version: '8.0.x'
- uses: actions/setup-dotnet@v4
name: 'Install .NET SDK 7.0'
with:
Expand Down
3 changes: 0 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
<ItemGroup Label="Deterministic Builds and Source Link">
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All"/>
</ItemGroup>
<PropertyGroup>
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
</PropertyGroup>
Expand Down
3 changes: 1 addition & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
<PackageVersion Include="System.Linq.Async" Version="[5.0.0, 7)" />
</ItemGroup>
<ItemGroup Label="Build Dependencies">
<PackageVersion Include="PolySharp" Version="1.14.0" />
<PackageVersion Include="PolySharp" Version="1.15.0" />
<PackageVersion Include="Messerli.CodeStyle" Version="2.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.3" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
</ItemGroup>
<ItemGroup Label="Test Dependencies">
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
Expand Down
3 changes: 3 additions & 0 deletions FrameworkFeatureConstants.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<DefineConstants>$(DefineConstants);RANDOM_SHUFFLE;UTF8_SPAN_PARSABLE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
<DefineConstants>$(DefineConstants);REFLECTION_ASSEMBLY_NAME_INFO;REFLECTION_TYPE_NAME</DefineConstants>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>

<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
Expand Down
70 changes: 70 additions & 0 deletions Funcky.Analyzers/Funcky.Analyzers.Test/NonDefaultableTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Microsoft.CodeAnalysis.Testing;
using Xunit;
using VerifyCS = Funcky.Analyzers.Test.CSharpAnalyzerVerifier<Funcky.Analyzers.NonDefaultableAnalyzer>;

namespace Funcky.Analyzers.Test;

public sealed class NonDefaultableTest
{
private const string AttributeSource =
"""
namespace Funcky.CodeAnalysis
{
[System.AttributeUsage(System.AttributeTargets.Struct)]
internal sealed class NonDefaultableAttribute : System.Attribute { }
}
""";

[Fact]
public async Task DefaultInstantiationsOfRegularStructsGetNoDiagnostic()
{
const string inputCode =
"""
class Test
{
private void Usage()
{
_ = default(Foo);
}
}
struct Foo { }
""";
await VerifyCS.VerifyAnalyzerAsync(inputCode + AttributeSource);
}

[Fact]
public async Task DefaultInstantiationsOfAnnotatedStructsGetError()
{
const string inputCode =
"""
using Funcky.CodeAnalysis;
class Test
{
private void Usage()
{
_ = default(Foo);
_ = default(Funcky.Generic<int>);
}
}
[NonDefaultable]
struct Foo { }
namespace Funcky
{
[NonDefaultable]
struct Generic<T> { }
}
""";

DiagnosticResult[] expectedDiagnostics =
[
VerifyCS.Diagnostic().WithSpan(7, 13, 7, 25).WithArguments("Foo"),
VerifyCS.Diagnostic().WithSpan(8, 13, 8, 41).WithArguments("Generic<int>"),
];

await VerifyCS.VerifyAnalyzerAsync(inputCode + AttributeSource, expectedDiagnostics);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
λ1009 | Funcky | Error | NonDefaultableAnalyzer
54 changes: 54 additions & 0 deletions Funcky.Analyzers/Funcky.Analyzers/NonDefaultableAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Funcky.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class NonDefaultableAnalyzer : DiagnosticAnalyzer
{
public static readonly DiagnosticDescriptor DoNotUseDefault = new DiagnosticDescriptor(
id: $"{DiagnosticName.Prefix}{DiagnosticName.Usage}09",
title: "Do not use default to instantiate this type",
messageFormat: "Do not use default(...) to instantiate '{0}'",
category: nameof(Funcky),
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Values instantiated with default are in an invalid state; any member may throw an exception.");

private const string AttributeFullName = "Funcky.CodeAnalysis.NonDefaultableAttribute";

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DoNotUseDefault);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(OnCompilationStart);
}

private static void OnCompilationStart(CompilationStartAnalysisContext context)
{
if (context.Compilation.GetTypeByMetadataName(AttributeFullName) is { } nonDefaultableAttribute)
{
context.RegisterOperationAction(AnalyzeDefaultValueOperation(nonDefaultableAttribute), OperationKind.DefaultValue);
}
}

private static Action<OperationAnalysisContext> AnalyzeDefaultValueOperation(INamedTypeSymbol nonDefaultableAttribute)
=> context =>
{
var operation = (IDefaultValueOperation)context.Operation;
if (operation.Type is { } type && type.GetAttributes().Any(IsAttribute(nonDefaultableAttribute)))
{
context.ReportDiagnostic(Diagnostic.Create(
DoNotUseDefault,
operation.Syntax.GetLocation(),
messageArgs: type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)));
}
};

private static Func<AttributeData, bool> IsAttribute(INamedTypeSymbol attributeClass)
=> attribute => SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeClass);
}
2 changes: 1 addition & 1 deletion Funcky.Async.Test/Funcky.Async.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
<TargetFrameworks>net9.0;net8.0;net7.0</TargetFrameworks>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand Down
2 changes: 1 addition & 1 deletion Funcky.Async/Extensions/AsyncEnumerableExtensions/Merge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static async IAsyncEnumerable<TSource> Merge<TSource>(this IEnumerable<IA

try
{
await foreach (var element in MergeEnumerators(enumerators.RemoveRange(await enumerators.ToAsyncEnumerable().WhereAwait(async f => await HasMoreElements(f).ConfigureAwait(false)).ToListAsync().ConfigureAwait(false)), GetMergeComparer(comparer)))
await foreach (var element in MergeEnumerators(enumerators.RemoveRange(await enumerators.ToAsyncEnumerable().WhereAwait(async f => await HasMoreElements(f).ConfigureAwait(false)).ToListAsync().ConfigureAwait(false)), GetMergeComparer(comparer)).ConfigureAwait(false))
{
yield return element;
}
Expand Down
13 changes: 7 additions & 6 deletions Funcky.Async/Extensions/AsyncEnumerableExtensions/PowerSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@ public static IAsyncEnumerable<IEnumerable<TSource>> PowerSet<TSource>(this IAsy

private static async IAsyncEnumerable<IEnumerable<TSource>> PowerSetInternal<TSource>(this IAsyncEnumerable<TSource> source, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var asyncEnumerator = source.GetAsyncEnumerator(cancellationToken);
await using var sourceEnumerator = asyncEnumerator.ConfigureAwait(false);
#pragma warning disable CA2007 // Configured via IAsyncEnumerable<T> extension
await using var asyncEnumerator = source.ConfigureAwait(false).WithCancellation(cancellationToken).GetAsyncEnumerator();
#pragma warning restore CA2007

await foreach (var set in PowerSetEnumerator(asyncEnumerator).WithCancellation(cancellationToken))
await foreach (var set in PowerSetEnumerator(asyncEnumerator).WithCancellation(cancellationToken).ConfigureAwait(false))
{
yield return set;
}
}

private static async IAsyncEnumerable<ImmutableStack<TSource>> PowerSetEnumerator<TSource>(this IAsyncEnumerator<TSource> source)
private static async IAsyncEnumerable<ImmutableStack<TSource>> PowerSetEnumerator<TSource>(this ConfiguredCancelableAsyncEnumerable<TSource>.Enumerator source)
{
if (await source.MoveNextAsync().ConfigureAwait(false))
if (await source.MoveNextAsync())
{
var temp = source.Current;
await foreach (var set in source.PowerSetEnumerator())
await foreach (var set in source.PowerSetEnumerator().ConfigureAwait(false))
{
yield return set;
yield return set.Push(temp);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
using System.Runtime.CompilerServices;

namespace Funcky.Extensions;

public static partial class AsyncEnumerableExtensions
{
/// <summary>Returns a sequence mapping each element together with its predecessor.</summary>
/// <exception cref="ArgumentNullException">Thrown when any value in <paramref name="source"/> is <see langword="null"/>.</exception>
[Pure]
public static async IAsyncEnumerable<ValueWithPrevious<TSource>> WithPrevious<TSource>(this IAsyncEnumerable<TSource> source)
public static IAsyncEnumerable<ValueWithPrevious<TSource>> WithPrevious<TSource>(this IAsyncEnumerable<TSource> source)
where TSource : notnull
=> source.WithPreviousInternal();

private static async IAsyncEnumerable<ValueWithPrevious<TSource>> WithPreviousInternal<TSource>(this IAsyncEnumerable<TSource> source, [EnumeratorCancellation] CancellationToken cancellationToken = default)
where TSource : notnull
{
var previous = Option<TSource>.None;

await foreach (var value in source)
await foreach (var value in source.ConfigureAwait(false).WithCancellation(cancellationToken))
{
yield return new ValueWithPrevious<TSource>(value, previous);
previous = value;
Expand Down
2 changes: 1 addition & 1 deletion Funcky.Async/Funcky.Async.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net5.0;netstandard2.1;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net9.0;net8.0;net5.0;netstandard2.1;netstandard2.0</TargetFrameworks>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<Description>Extends Funcky with support for IAsyncEnumerable and Tasks.</Description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<AssemblyName>Funcky.SourceGenerator.Test</AssemblyName>
<RootNamespace>Funcky.SourceGenerator.Test</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//HintName: .g.cs
// <auto-generated/>
#nullable enable

namespace Funcky.Extensions
{
public static partial class ParseExtensions
{
[global::System.Diagnostics.Contracts.Pure]
public static Funcky.Monads.Option<global::Funcky.Extensions.Target> ParseTargetOrNone(this string candidate, string? hasDefault = null) => global::Funcky.Extensions.Target.TryParse(candidate, out var result, hasDefault) ? result : default(Funcky.Monads.Option<global::Funcky.Extensions.Target>);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//HintName: OrNoneFromTryPatternAttribute.g.cs
namespace Funcky.Internal
{
[global::System.Diagnostics.Conditional("COMPILE_TIME_ONLY")]
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = true)]
internal class OrNoneFromTryPatternAttribute : global::System.Attribute
{
public OrNoneFromTryPatternAttribute(global::System.Type type, string method)
=> (Type, Method) = (type, method);

public global::System.Type Type { get; }

public string Method { get; }
}
}
30 changes: 30 additions & 0 deletions Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ public static bool TryParse(string candidate, out Target result)
return TestHelper.Verify(source + Environment.NewLine + OptionSource);
}

[Fact]
public Task GenerateMethodWithDefaultValuedArgument()
{
const string source =
"""
#nullable enable
using Funcky.Internal;
namespace Funcky.Extensions
{
[OrNoneFromTryPattern(typeof(Target), nameof(Target.TryParse))]
public static partial class ParseExtensions
{
}
public sealed class Target
{
public static bool TryParse(string candidate, out Target result, string? hasDefault = null)
{
result = default!;
return false;
}
}
}
""";

return TestHelper.Verify(source + Environment.NewLine + OptionSource);
}

[Fact]
public Task GeneratesMethodWhenTargetIsNotNullableAnnotated()
{
Expand Down

This file was deleted.

6 changes: 3 additions & 3 deletions Funcky.SourceGenerator/Funcky.SourceGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" VersionOverride="3.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" VersionOverride="4.12.0" />
<PackageReference Include="PolySharp" PrivateAssets="all" />
</ItemGroup>
</Project>
Loading

0 comments on commit 0b0b9d0

Please sign in to comment.