Skip to content

Commit

Permalink
Merge branch 'main' into string-static
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonCropp committed Feb 19, 2024
2 parents 3f3438e + 2948582 commit 8455f7e
Show file tree
Hide file tree
Showing 145 changed files with 8,978 additions and 896 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ Ensure you have replicated the bug in a minimal solution with the fewest moving

#### Submit a PR that fixes the bug

Submit a [Pull Request (PR)](https://help.github.com/articles/about-pull-requests/) that fixes the bug. Include in this PR a test that verifies the fix. If you were not able to fix the bug, a PR that illustrates your partial progress will suffice.
Submit a [Pull Request (PR)](https://help.github.com/articles/about-pull-requests/) that fixes the bug. Include in this PR a test that verifies the fix. If you were not able to fix the bug, a PR that illustrates your partial progress will suffice.
2 changes: 1 addition & 1 deletion .github/workflows/merge-dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
if: github.actor == 'dependabot[bot]'
steps:
- name: Dependabot Auto Merge
uses: ahmadnassri/action-dependabot-auto-merge@v2.3.1
uses: ahmadnassri/action-dependabot-auto-merge@v2.6.6
with:
target: minor
github-token: ${{ secrets.dependabot }}
Expand Down
19 changes: 0 additions & 19 deletions .github/workflows/on-tag-do-release.yml

This file was deleted.

309 changes: 309 additions & 0 deletions api_list.include.md

Large diffs are not rendered by default.

360 changes: 360 additions & 0 deletions contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
# Contributing


## Solution Structure


### Polyfill project

The main project that produces the nuget.


### Tests project

A NUnit test project that verifies all the APIs.


### NoRefsTests project

Some features of Polyfill [require nuget references](/#references) to be enabled. The NoRefsTests project had none of those refecences and tests the subset of features that do not require references.


### PublicTests project

Polyfill supports [making all APIs public](#consuming-and-type-visibility). The PublicTests project tests that scenario.


### UnsafeTests project

Some feature of Polyfill leverage unsafe code for better performance. For example `Append(this StringBuilder, ReadOnlySpan<char>)`. The UnsafeTests project tests this scenario vie enabling `<AllowUnsafeBlocks>True</AllowUnsafeBlocks>`.


### Consume project

Polyfill supports back to `net461` and `netcoreapp2.0`. However NUnit only support back to `net462` and `netcoreapp3.1`. The Consume project targets all frameworks that Polyfill supports, and consumes all APIs to ensure that they all compile on those frameworks


### ConsumeClassicReferences Project

Test the scenario when references are added through `<Reference` instead of `<PackageReference`.


## Submitting a new polyfill API


### Valid APIs

An API is a valid candidate to be polyfilled if it exists in the current stable version of .net or is planned for a future version .net

APIs that require a reference to a bridging nuget (similar to [System.ValueTuple](https://www.nuget.org/packages/System.ValueTuple/) or [System.Memory](https://www.nuget.org/packages/System.Memory/)) will only be accepted if, in a future version of .net that nuget is not required.


### Raise Pull Request not an Issue

If a new API is valid, dont bother raising a GitHub issue to ask about it. Instead submit a Pull Request that adds that API. Any discussion can happen in the PR comments.


### Add the new API to the Polyfill project


#### Conditional Compilation

The code for the API should be wrapped in conditional compilation statements. For example:

```
#if NETFRAMEWORK || NETSTANDARD || NETCOREAPP2X
```

The following additional compilation constants are provided:

* `NETCOREAPPX`: indicates if netcore is being targeted.
* `NETCOREAPP2X`: indicates if any major or minor version of netcore 2 is being targeted.
* `NETCOREAPP3X`: indicates if any major or minor version of netcore 3 is being targeted.
* `NET46X`: indicates if any major or minor version of NET46 is being targeted.
* `NET47X`: indicates if any major or minor version of NET47 is being targeted.
* `NET48X`: indicates if any major or minor version of NET48 is being targeted.
* `MEMORYREFERENCED`: indicates if [System.Memory](https://www.nuget.org/packages/System.Memory/)) is referenced.
* `TASKSEXTENSIONSREFERENCED`: indicates if [System.Threading.Tasks.Extensions](https://www.nuget.org/packages/System.Threading.Tasks.Extensions/)) is referenced.
* `VALUETUPLEREFERENCED`: indicates if [System.ValueTuple](https://www.nuget.org/packages/System.ValueTuple/)) is referenced.


#### Warnings disabled

Warnings must be disabled with a pragma.

```
#pragma warning disable
```

This is required to prevent custom code formatting rule in consuming projects from giving false warnings


#### ReSharper / Rider

Any potential ReSharper or Rider code formatting issues should be disabled. For example:

```
// ReSharper disable RedundantUsingDirective
// ReSharper disable UnusedMember.Global
```


#### Assume Implicit usings is disabled

Having Implicit usings enabled is optional for the consuming project. So ensure all using statements are included.


#### Make public if

Polyfill supports [making all APIs public](#consuming-and-type-visibility). This is done by making types public if `PolyPublic`. For example:

```
#if PolyPublic
public
#endif
sealed class ...
```

#### XML API comment

The XML API comments should match the actual API.


#### If the API is attribute based

Add a new class containing the Attribute

Example:

<!-- snippet: ModuleInitializerAttribute.cs -->
<a id='snippet-ModuleInitializerAttribute.cs'></a>
```cs
// <auto-generated />
#if !NET5_0_OR_GREATER

namespace System.Runtime.CompilerServices;

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Link = System.ComponentModel.DescriptionAttribute;

/// <summary>
/// Used to indicate to the compiler that a method should be called
/// in its containing module's initializer.
/// </summary>
/// <remarks>
/// When one or more valid methods
/// with this attribute are found in a compilation, the compiler will
/// emit a module initializer which calls each of the attributed methods.
///
/// Certain requirements are imposed on any method targeted with this attribute:
/// - The method must be `static`.
/// - The method must be an ordinary member method, as opposed to a property accessor, constructor, local function, etc.
/// - The method must be parameterless.
/// - The method must return `void`.
/// - The method must not be generic or be contained in a generic type.
/// - The method's effective accessibility must be `internal` or `public`.
///
/// The specification for module initializers in the .NET runtime can be found here:
/// https://github.com/dotnet/runtime/blob/master/docs/design/specs/Ecma-335-Augments.md#module-initializer
/// </remarks>
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-7.0")]
[ExcludeFromCodeCoverage]
[DebuggerNonUserCode]
[AttributeUsage(
validOn: AttributeTargets.Method,
Inherited = false)]
#if PolyPublic
public
#endif
sealed class ModuleInitializerAttribute :
Attribute
{
}

#endif
```
<sup><a href='/src/Polyfill/ModuleInitializerAttribute.cs#L1-L45' title='Snippet source file'>snippet source</a> | <a href='#snippet-ModuleInitializerAttribute.cs' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


#### If the API is a missing instance method

Add an extension method to `Polyfill_TYPE.cs` where `TYPE` is the type the method extending. So, for example, APIs that target `StreamWriter` go in `Polyfill_StreamWriter.cs`.

Example:

<!-- snippet: Polyfill_TextWriter.cs -->
<a id='snippet-Polyfill_TextWriter.cs'></a>
```cs
// <auto-generated />
#pragma warning disable

#if MEMORYREFERENCED && (NETFRAMEWORK || NETSTANDARD2_0 || NETCOREAPP2_0)

using System;
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using Link = System.ComponentModel.DescriptionAttribute;
using System.Threading;
using System.Threading.Tasks;

static partial class Polyfill
{

#if TASKSEXTENSIONSREFERENCED

/// <summary>
/// Asynchronously writes a character memory region to the stream.
/// </summary>
/// <param name="buffer">The character memory region to write to the stream.</param>
/// <param name="cancellationToken">
/// The token to monitor for cancellation requests.
/// The default value is <see cref="CancellationToken.None"/>.
/// </param>
/// <returns>A task that represents the asynchronous write operation.</returns>
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.io.textwriter.writeasync#system-io-textwriter-writeasync(system-readonlymemory((system-char))-system-threading-cancellationtoken)")]
public static ValueTask WriteAsync(
this TextWriter target,
ReadOnlyMemory<char> buffer,
CancellationToken cancellationToken = default)
{
// StreamReader doesn't accept cancellation token (pre-netstd2.1)
cancellationToken.ThrowIfCancellationRequested();

if (!MemoryMarshal.TryGetArray(buffer, out var segment))
{
segment = new(buffer.ToArray());
}

return new(target.WriteAsync(segment.Array!, segment.Offset, segment.Count));
}

/// <summary>
/// Asynchronously writes the text representation of a character memory region to the stream, followed by a line terminator.
/// </summary>
/// <param name="buffer">The character memory region to write to the stream.</param>
/// <param name="cancellationToken">
/// The token to monitor for cancellation requests.
/// The default value is <see cref="CancellationToken.None"/>.
/// </param>
/// <returns>A task that represents the asynchronous write operation.</returns>
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.io.textwriter.writelineasync#system-io-textwriter-writelineasync(system-readonlymemory((system-char))-system-threading-cancellationtoken)")]
public static ValueTask WriteLineAsync(
this TextWriter target,
ReadOnlyMemory<char> buffer,
CancellationToken cancellationToken = default)
{
// StreamReader doesn't accept cancellation token (pre-netstd2.1)
cancellationToken.ThrowIfCancellationRequested();

if (!MemoryMarshal.TryGetArray(buffer, out var segment))
{
segment = new(buffer.ToArray());
}

return new(target.WriteLineAsync(segment.Array!, segment.Offset, segment.Count));
}

#endif

/// <summary>
/// Writes a character span to the text stream.
/// </summary>
/// <param name="buffer">The character span to write.</param>
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.io.textwriter.write#system-io-textwriter-write(system-readonlyspan((system-char)))")]
public static void Write(
this TextWriter target,
ReadOnlySpan<char> buffer)
{
var pool = ArrayPool<char>.Shared;
var array = pool.Rent(buffer.Length);

try
{
buffer.CopyTo(new(array));
target.Write(array, 0, buffer.Length);
}
finally
{
pool.Return(array);
}
}

/// <summary>
/// Writes the text representation of a character span to the text stream, followed by a line terminator.
/// </summary>
/// <param name="buffer">The char span value to write to the text stream.</param>
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.io.textwriter.writeline#system-io-textwriter-writeline(system-readonlyspan((system-char)))")]
public static void WriteLine(
this TextWriter target,
ReadOnlySpan<char> buffer)
{
var pool = ArrayPool<char>.Shared;
var array = pool.Rent(buffer.Length);

try
{
buffer.CopyTo(new(array));
target.WriteLine(array, 0, buffer.Length);
}
finally
{
pool.Return(array);
}
}
}
#endif
```
<sup><a href='/src/Polyfill/Polyfill_TextWriter.cs#L1-L120' title='Snippet source file'>snippet source</a> | <a href='#snippet-Polyfill_TextWriter.cs' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


### Add a test

Add a for the new API to the Tests project.

Extension method tests to `PolyfillTests_TYPE.cs` where `TYPE` is the type the method extending. So, for example, APIs that target `StreamWriter` go in `PolyfillTests_StreamWriter.cs`. For example:

<!-- snippet: PolyfillTests_StreamReader.cs -->
<a id='snippet-PolyfillTests_StreamReader.cs'></a>
```cs
partial class PolyfillTests
{
[Test]
public async Task StreamReaderReadAsync()
{
using var stream = new MemoryStream("value"u8.ToArray());
var result = new char[5];
var memory = new Memory<char>(result);
using var reader = new StreamReader(stream);
var read = await reader.ReadAsync(memory);
Assert.AreEqual(5, read);
Assert.IsTrue("value".SequenceEqual(result));
}

[Test]
public async Task StreamReaderReadToEndAsync()
{
using var stream = new MemoryStream("value"u8.ToArray());
using var reader = new StreamReader(stream);
var read = await reader.ReadToEndAsync(Cancel.None);
Assert.AreEqual("value", read);
}
}
```
<sup><a href='/src/Tests/PolyfillTests_StreamReader.cs#L1-L23' title='Snippet source file'>snippet source</a> | <a href='#snippet-PolyfillTests_StreamReader.cs' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


### Add documentation

Add documentation for the API to the `readme.md`.


### Add to the Consume project

Add a simple usage of the API to the Consume project.
1 change: 1 addition & 0 deletions notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apidiff https://github.com/dotnet/core/blob/main/release-notes/8.0/8.0.1/api-diff/README.md
Loading

0 comments on commit 8455f7e

Please sign in to comment.