Rework PooledArrayBuilder<T> for small arrays (#9269)

This change uses a similar approach to Roslyn's `TemporaryArray<T>` to
avoid retrieving an `ImmutableArray<T>.Builder` from the pool for arrays
of 4 or fewer elements. The first four items added to the builder are
stored on the stack.

As part of this change, I've added a new `Razor.Diagnostics.Analyzers`
project that should contain any analyzers that we write specifically for
the Razor repo. These shouldn't be deployed; they're for internal
consumption only. I've added a variant of the
`TemporaryArrayAsRefAnalyzer` from the `Roslyn.Diagnostics.Analyzers` to
enforce that `PooledArrayBuilder<T>.AsRef()` can only be called on local
builders in a using statement.
This commit is contained in:
Dustin Campbell 2023-09-20 13:11:46 -07:00 коммит произвёл GitHub
Родитель 284c4be688 312f657075
Коммит cf76071cbd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
50 изменённых файлов: 1532 добавлений и 132 удалений

25
BuildAnalyzers.sln Normal file
Просмотреть файл

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34117.57
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Razor.Diagnostics.Analyzers", "src\Analyzers\Razor.Diagnostics.Analyzers\Razor.Diagnostics.Analyzers.csproj", "{42438A8F-6284-443A-A518-9AAD5371A403}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{42438A8F-6284-443A-A518-9AAD5371A403}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42438A8F-6284-443A-A518-9AAD5371A403}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42438A8F-6284-443A-A518-9AAD5371A403}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42438A8F-6284-443A-A518-9AAD5371A403}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F62A910A-9F8F-44CD-B8D7-8D7A88FF902A}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -2,6 +2,8 @@
"solution": {
"path": "Razor.sln",
"projects": [
"src\\Analyzers\\Razor.Diagnostics.Analyzers\\Razor.Diagnostics.Analyzers.csproj",
"src\\Analyzers\\Razor.Diagnostics.Analyzers.Test\\Razor.Diagnostics.Analyzers.Test.csproj",
"src\\Compiler\\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\\src\\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj",
"src\\Compiler\\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\\test\\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test.csproj",
"src\\Compiler\\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X\\src\\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X.csproj",
@ -12,9 +14,9 @@
"src\\Compiler\\Microsoft.AspNetCore.Razor.Language\\test\\Microsoft.AspNetCore.Razor.Language.Test.csproj",
"src\\Compiler\\Microsoft.CodeAnalysis.Razor\\src\\Microsoft.CodeAnalysis.Razor.csproj",
"src\\Compiler\\Microsoft.CodeAnalysis.Razor\\test\\Microsoft.CodeAnalysis.Razor.Test.csproj",
"src\\Compiler\\Microsoft.Net.Compilers.Razor.Toolset\\Microsoft.Net.Compilers.Razor.Toolset.csproj",
"src\\Compiler\\Microsoft.NET.Sdk.Razor.SourceGenerators.Transport\\Microsoft.NET.Sdk.Razor.SourceGenerators.Transport.csproj",
"src\\Compiler\\Microsoft.NET.Sdk.Razor.SourceGenerators\\Microsoft.NET.Sdk.Razor.SourceGenerators.csproj",
"src\\Compiler\\Microsoft.Net.Compilers.Razor.Toolset\\Microsoft.Net.Compilers.Razor.Toolset.csproj",
"src\\Compiler\\test\\Microsoft.AspNetCore.Razor.Test.Common\\Microsoft.AspNetCore.Razor.Test.Common.Compiler.csproj",
"src\\Compiler\\test\\Microsoft.AspNetCore.Razor.Test.ComponentShim\\Microsoft.AspNetCore.Razor.Test.ComponentShim.Compiler.csproj",
"src\\Compiler\\test\\Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X\\Microsoft.AspNetCore.Razor.Test.MvcShim.Version1_X.Compiler.csproj",
@ -44,7 +46,8 @@
"src\\Razor\\test\\Microsoft.VisualStudio.LanguageServer.ContainedLanguage.Test\\Microsoft.VisualStudio.LanguageServer.ContainedLanguage.Test.csproj",
"src\\Razor\\test\\Microsoft.VisualStudio.LanguageServices.Razor.Test\\Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj",
"src\\Razor\\test\\Microsoft.VisualStudio.LiveShare.Razor.Test\\Microsoft.VisualStudio.LiveShare.Razor.Test.csproj",
"src\\Shared\\Microsoft.AspNetCore.Razor.Utilities.Shared.Test\\Microsoft.AspNetCore.Razor.Utilities.Shared.Test.csproj",
"src\\Shared\\Microsoft.AspNetCore.Razor.Utilities.Shared\\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj"
]
}
}
}

Просмотреть файл

@ -103,8 +103,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.Razor.Sou
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{3AE210D1-C435-4693-BF79-2EF13ED554B9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Utilities.Shared", "src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj", "{BEF172F0-D1C2-4043-BF45-8E520FF79321}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.AspNetCore.Mvc.Razor.Extensions", "Microsoft.AspNetCore.Mvc.Razor.Extensions", "{DE461FC6-663E-4362-9B33-C5A88C7B4DB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Test", "src\Compiler\Microsoft.AspNetCore.Mvc.Razor.Extensions\test\Microsoft.AspNetCore.Mvc.Razor.Extensions.Test.csproj", "{7BDA653C-6164-4ACB-913B-C7BB91E2390A}"
@ -171,6 +169,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.ProjectEngineHost.Test", "src\Razor\test\Microsoft.AspNetCore.Razor.ProjectEngineHost.Test\Microsoft.AspNetCore.Razor.ProjectEngineHost.Test.csproj", "{4126E0A6-1CA9-44B1-AD22-66EDB9FEE7AD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Utilities.Shared.Test", "src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.Test\Microsoft.AspNetCore.Razor.Utilities.Shared.Test.csproj", "{7275F376-1B63-49D3-8078-0F7CA15CC637}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Utilities.Shared", "src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj", "{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{4AA319E0-C81E-47CC-841A-6EFCB6784A1F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Razor.Diagnostics.Analyzers", "src\Analyzers\Razor.Diagnostics.Analyzers\Razor.Diagnostics.Analyzers.csproj", "{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Razor.Diagnostics.Analyzers.Test", "src\Analyzers\Razor.Diagnostics.Analyzers.Test\Razor.Diagnostics.Analyzers.Test.csproj", "{167F1426-D9AE-49DF-B214-F00536DBC305}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -499,14 +507,6 @@ Global
{A2DED0B8-1A7E-4E3D-A820-691BB4500610}.Release|Any CPU.Build.0 = Release|Any CPU
{A2DED0B8-1A7E-4E3D-A820-691BB4500610}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{A2DED0B8-1A7E-4E3D-A820-691BB4500610}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.Release|Any CPU.Build.0 = Release|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{BEF172F0-D1C2-4043-BF45-8E520FF79321}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{7BDA653C-6164-4ACB-913B-C7BB91E2390A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BDA653C-6164-4ACB-913B-C7BB91E2390A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BDA653C-6164-4ACB-913B-C7BB91E2390A}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
@ -707,6 +707,38 @@ Global
{4126E0A6-1CA9-44B1-AD22-66EDB9FEE7AD}.Release|Any CPU.Build.0 = Release|Any CPU
{4126E0A6-1CA9-44B1-AD22-66EDB9FEE7AD}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{4126E0A6-1CA9-44B1-AD22-66EDB9FEE7AD}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.Release|Any CPU.Build.0 = Release|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{7275F376-1B63-49D3-8078-0F7CA15CC637}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.Release|Any CPU.Build.0 = Release|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.Release|Any CPU.Build.0 = Release|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.Debug|Any CPU.Build.0 = Debug|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.Release|Any CPU.ActiveCfg = Release|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.Release|Any CPU.Build.0 = Release|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -752,7 +784,6 @@ Global
{EF6275D9-682E-4ED9-85BB-92AFF18169CE} = {9B419123-4D1D-4ADB-ABB0-C324ED5A62E3}
{DE2A3AB5-3CE4-4CB3-BF46-1D54E537D130} = {F8C0166A-2714-44AA-87B5-3365F1FB1AE2}
{A2DED0B8-1A7E-4E3D-A820-691BB4500610} = {AA4EE974-E765-4B97-AF35-F734BF9830F6}
{BEF172F0-D1C2-4043-BF45-8E520FF79321} = {3AE210D1-C435-4693-BF79-2EF13ED554B9}
{DE461FC6-663E-4362-9B33-C5A88C7B4DB8} = {5B60F564-4AD7-4B70-A887-7D91496799A2}
{7BDA653C-6164-4ACB-913B-C7BB91E2390A} = {DE461FC6-663E-4362-9B33-C5A88C7B4DB8}
{9B419123-4D1D-4ADB-ABB0-C324ED5A62E3} = {5B60F564-4AD7-4B70-A887-7D91496799A2}
@ -785,6 +816,10 @@ Global
{53977089-1A87-4521-8368-0D50DFDA1279} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{C0C2AD17-5F5B-4B11-956D-203D91C377FB} = {92463391-81BE-462B-AC3C-78C6C760741F}
{4126E0A6-1CA9-44B1-AD22-66EDB9FEE7AD} = {92463391-81BE-462B-AC3C-78C6C760741F}
{7275F376-1B63-49D3-8078-0F7CA15CC637} = {3AE210D1-C435-4693-BF79-2EF13ED554B9}
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050} = {3AE210D1-C435-4693-BF79-2EF13ED554B9}
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5} = {4AA319E0-C81E-47CC-841A-6EFCB6784A1F}
{167F1426-D9AE-49DF-B214-F00536DBC305} = {4AA319E0-C81E-47CC-841A-6EFCB6784A1F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}

Просмотреть файл

@ -2,10 +2,13 @@
<Target Name="_PublishLanguageServerRids" AfterTargets="Pack" Condition="'$(DotNetBuildFromSource)' != 'true'">
<PropertyGroup>
<BuildAnalyzersSolutionPath>$(MSBuildThisFileDirectory)..\BuildAnalyzers.sln</BuildAnalyzersSolutionPath>
<LanguageServerProject>$(MSBuildThisFileDirectory)..\src\Razor\src\rzls\rzls.csproj</LanguageServerProject>
<RazorSolutionPath>$(MSBuildThisFileDirectory)..\Razor.sln</RazorSolutionPath>
</PropertyGroup>
<MSBuild Projects="$(BuildAnalyzersSolutionPath)"
Targets="Restore" />
<MSBuild Projects="$(RazorSolutionPath)"
Targets="Restore" />
<MSBuild Projects="$(LanguageServerProject)"

Просмотреть файл

@ -6,6 +6,13 @@
</PropertyGroup>
<ItemGroup>
<!-- Note: It is important BuildAnalyzers.sln builds first and does not allow building in parallel.
Because projects in Razor.sln (or Razor.Slim.slnf) have a project reference to Razor.Diagnostics.Analyzers
that is treated as an analyzer, there can be file locking issues if Razor.Diagnostics.Analyzers isn't
built before Razor.sln. Setting "BuildInParallel" to "false" ensures that this solution will be fully
built first. -->
<ProjectToBuild Include="$(RepoRoot)BuildAnalyzers.sln" BuildInParallel="false" />
<ProjectToBuild Condition="'$(OS)'=='WINDOWS_NT' and '$(SdkTaskProjects)'==''" Include="$(MSBuildThisFileDirectory)..\Razor.sln" />
<!-- Exclude VSIX projects on non-Windows -->

Просмотреть файл

@ -110,7 +110,9 @@
<MicrosoftNETSdkRazorPackageVersion>6.0.0-alpha.1.21072.5</MicrosoftNETSdkRazorPackageVersion>
<!-- Packages from dotnet/roslyn -->
<MicrosoftNetCompilersToolsetVersion>$(MicrosoftNetCompilersToolsetPackageVersion)</MicrosoftNetCompilersToolsetVersion>
<MicrosoftCodeAnalysisAnalyzerTestingPackageVersion>1.1.2-beta1.23323.1</MicrosoftCodeAnalysisAnalyzerTestingPackageVersion>
<MicrosoftCodeAnalysisAnalyzerTestingPackageVersion>1.1.2-beta1.23431.1</MicrosoftCodeAnalysisAnalyzerTestingPackageVersion>
<MicrosoftCodeAnalysisCSharpAnalyzerTestingXUnitPackageVersion>1.1.2-beta1.23431.1</MicrosoftCodeAnalysisCSharpAnalyzerTestingXUnitPackageVersion>
<MicrosoftCodeAnalysisTestingVerifiersXUnitPackageVersion>1.1.2-beta1.23431.1</MicrosoftCodeAnalysisTestingVerifiersXUnitPackageVersion>
<MicrosoftVisualStudioEditorPackageVersion>$(MicrosoftVisualStudioPackagesVersion)</MicrosoftVisualStudioEditorPackageVersion>
<MicrosoftVisualStudioExtensibilityTestingXunitVersion>0.1.169-beta</MicrosoftVisualStudioExtensibilityTestingXunitVersion>
<MicrosoftVisualStudioExtensibilityTestingSourceGeneratorVersion>$(MicrosoftVisualStudioExtensibilityTestingXunitVersion)</MicrosoftVisualStudioExtensibilityTestingSourceGeneratorVersion>

Просмотреть файл

@ -0,0 +1,23 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., Directory.Build.props))\Directory.Build.props" />
<PropertyGroup>
<PackageTags>aspnetcore;cshtml;razor</PackageTags>
<IsPackable>true</IsPackable>
<IsShipping>true</IsShipping>
<IncludeSymbols>true</IncludeSymbols>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<!-- In theory we want to have this property set, but our pipeline doesn't set the access tokens yet -->
<PublishWindowsPdb Condition="'$(DotNetSymbolServerTokenMsdl)'!='' and '$(DotNetSymbolServerTokenSymWeb)'!=''">true</PublishWindowsPdb>
<RollForward Condition="'$(IsTestProject)' == 'true'">LatestMajor</RollForward>
</PropertyGroup>
<ItemGroup Condition="'$(DotNetBuildFromSource)' != 'true'">
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="$(Tooling_MicrosoftCodeAnalysisAnalyzersPackageVersion)" NoWarn="NU1608" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="$(Tooling_MicrosoftCodeAnalysisBannedApiAnalyzersPackageVersion)" />
<PackageReference Include="Roslyn.Diagnostics.Analyzers" Version="$(Tooling_RoslynDiagnosticsAnalyzersPackageVersion)" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,104 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Xunit;
namespace Razor.Diagnostics.Analyzers.Test;
using VerifyCS = CSharpAnalyzerVerifier<PooledArrayBuilderAsRefAnalyzer>;
public class PooledArrayBuilderAsRefAnalyzerTests
{
private const string PooledArrayBuilderSource = """
namespace Microsoft.AspNetCore.Razor.PooledObjects
{
internal struct PooledArrayBuilder<T> : System.IDisposable
{
public void Dispose() { }
}
internal static class PooledArrayBuilderExtensions
{
public static ref PooledArrayBuilder<T> AsRef<T>(this in PooledArrayBuilder<T> array) => throw null;
}
}
""";
[Fact]
public async Task TestUsingVariable_CSharpAsync()
{
var code = $$"""
using Microsoft.AspNetCore.Razor.PooledObjects;
class C
{
void Method()
{
using (var array = new PooledArrayBuilder<int>())
{
ref var arrayRef1 = ref array.AsRef();
ref var arrayRef2 = ref PooledArrayBuilderExtensions.AsRef(in array);
}
}
}
{{PooledArrayBuilderSource}}
""";
await new VerifyCS.Test
{
TestCode = code,
}.RunAsync();
}
[Fact]
public async Task TestUsingDeclarationVariable_CSharpAsync()
{
var code = $$"""
using Microsoft.AspNetCore.Razor.PooledObjects;
class C
{
void Method()
{
using var array = new PooledArrayBuilder<int>();
ref var arrayRef1 = ref array.AsRef();
ref var arrayRef2 = ref PooledArrayBuilderExtensions.AsRef(in array);
}
}
{{PooledArrayBuilderSource}}
""";
await new VerifyCS.Test
{
TestCode = code,
}.RunAsync();
}
[Fact]
public async Task TestNonUsingVariable_CSharpAsync()
{
var code = $$"""
using Microsoft.AspNetCore.Razor.PooledObjects;
class C
{
void Method()
{
var array = new PooledArrayBuilder<int>();
ref var arrayRef1 = ref [|array.AsRef()|];
ref var arrayRef2 = ref [|PooledArrayBuilderExtensions.AsRef(in array)|];
}
}
{{PooledArrayBuilderSource}}
""";
await new VerifyCS.Test
{
TestCode = code,
}.RunAsync();
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFrameworks)</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);$(DefaultNetFxTargetFramework)</TargetFrameworks>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(MicrosoftCodeAnalysisCSharpAnalyzerTestingXUnitPackageVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.Testing.Verifiers.XUnit" Version="$(MicrosoftCodeAnalysisTestingVerifiersXUnitPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Razor.Diagnostics.Analyzers\Razor.Diagnostics.Analyzers.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing.Verifiers;
namespace Razor.Diagnostics.Analyzers.Test;
public static partial class CSharpAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
{
public class Test : CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
{
public Test()
{
SolutionTransforms.Add((solution, projectId) =>
{
var compilationOptions = solution.GetProject(projectId)!.CompilationOptions;
compilationOptions = compilationOptions!.WithSpecificDiagnosticOptions(
compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));
solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
return solution;
});
}
}
}

Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
namespace Razor.Diagnostics.Analyzers.Test;
public static partial class CSharpAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
{
/// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic()"/>
public static DiagnosticResult Diagnostic()
=> CSharpAnalyzerVerifier<TAnalyzer, XUnitVerifier>.Diagnostic();
/// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic(string)"/>
public static DiagnosticResult Diagnostic(string diagnosticId)
=> CSharpAnalyzerVerifier<TAnalyzer, XUnitVerifier>.Diagnostic(diagnosticId);
/// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic(DiagnosticDescriptor)"/>
public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
=> CSharpAnalyzerVerifier<TAnalyzer, XUnitVerifier>.Diagnostic(descriptor);
/// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.VerifyAnalyzerAsync(string, DiagnosticResult[])"/>
public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
{
var test = new Test
{
TestCode = source,
};
test.ExpectedDiagnostics.AddRange(expected);
await test.RunAsync(CancellationToken.None);
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace Razor.Diagnostics.Analyzers.Test;
internal static class CSharpVerifierHelper
{
/// <summary>
/// By default, the compiler reports diagnostics for nullable reference types at
/// <see cref="DiagnosticSeverity.Warning"/>, and the analyzer test framework defaults to only validating
/// diagnostics at <see cref="DiagnosticSeverity.Error"/>. This map contains all compiler diagnostic IDs
/// related to nullability mapped to <see cref="ReportDiagnostic.Error"/>, which is then used to enable all
/// of these warnings for default validation during analyzer and code fix tests.
/// </summary>
internal static ImmutableDictionary<string, ReportDiagnostic> NullableWarnings { get; } = GetNullableWarningsFromCompiler();
private static ImmutableDictionary<string, ReportDiagnostic> GetNullableWarningsFromCompiler()
{
string[] args = { "/warnaserror:nullable" };
var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
// Workaround for https://github.com/dotnet/roslyn/issues/41610
nullableWarnings = nullableWarnings
.SetItem("CS8632", ReportDiagnostic.Error)
.SetItem("CS8669", ReportDiagnostic.Error);
return nullableWarnings;
}
}

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Razor.Diagnostics.Analyzers;
internal static class DiagnosticCategory
{
public const string Reliability = nameof(Reliability);
}

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Razor.Diagnostics.Analyzers;
internal static class DiagnosticIds
{
public const string PooledArrayBuilderAsRef = "RZD001";
}

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis;
namespace Razor.Diagnostics.Analyzers;
internal static class Extensions
{
public static Diagnostic CreateDiagnostic(this IOperation operation, DiagnosticDescriptor rule)
{
var location = operation.Syntax.GetLocation();
if (!location.IsInSource)
{
location = Location.None;
}
return Diagnostic.Create(rule, location);
}
}

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking", Justification = "<Pending>", Scope = "member", Target = "~F:Razor.Diagnostics.Analyzers.PooledArrayBuilderAsRefAnalyzer.Rule")]

Просмотреть файл

@ -0,0 +1,73 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using static Razor.Diagnostics.Analyzers.Resources;
namespace Razor.Diagnostics.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class PooledArrayBuilderAsRefAnalyzer : DiagnosticAnalyzer
{
internal static readonly DiagnosticDescriptor Rule = new(
DiagnosticIds.PooledArrayBuilderAsRef,
CreateLocalizableResourceString(nameof(PooledArrayBuilderAsRefTitle)),
CreateLocalizableResourceString(nameof(PooledArrayBuilderAsRefMessage)),
DiagnosticCategory.Reliability,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(PooledArrayBuilderAsRefDescription)));
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterCompilationStartAction(context =>
{
var pooledArrayBuilderExtensions = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.PooledArrayBuilderExtensions);
if (pooledArrayBuilderExtensions is null)
{
return;
}
var pooledArrayBuilderAsRef = (IMethodSymbol?)pooledArrayBuilderExtensions.GetMembers("AsRef").SingleOrDefault();
if (pooledArrayBuilderAsRef is null)
{
return;
}
context.RegisterOperationAction(context => AnalyzeInvocation(context, pooledArrayBuilderAsRef), OperationKind.Invocation);
});
}
private static void AnalyzeInvocation(OperationAnalysisContext context, IMethodSymbol pooledArrayBuilderAsRef)
{
var invocation = (IInvocationOperation)context.Operation;
var targetMethod = invocation.TargetMethod.ReducedFrom ?? invocation.TargetMethod;
if (!SymbolEqualityComparer.Default.Equals(targetMethod.OriginalDefinition, pooledArrayBuilderAsRef))
{
return;
}
var instance = invocation.Instance ?? invocation.Arguments.FirstOrDefault()?.Value;
if (instance is not ILocalReferenceOperation localReference)
{
context.ReportDiagnostic(invocation.CreateDiagnostic(Rule));
return;
}
var declaration = invocation.SemanticModel!.GetOperation(localReference.Local.DeclaringSyntaxReferences.Single().GetSyntax(context.CancellationToken), context.CancellationToken);
if (declaration is not { Parent: IVariableDeclarationOperation { Parent: IVariableDeclarationGroupOperation { Parent: IUsingOperation or IUsingDeclarationOperation } } })
{
context.ReportDiagnostic(invocation.CreateDiagnostic(Rule));
return;
}
}
}

Просмотреть файл

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<ExcludeFromSourceBuild>false</ExcludeFromSourceBuild>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpPackageVersion)" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis;
namespace Razor.Diagnostics.Analyzers;
internal partial class Resources
{
private static readonly Type s_resourcesType = typeof(Resources);
public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource)
=> new(nameOfLocalizableResource, ResourceManager, s_resourcesType);
public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments)
=> new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments);
}

Просмотреть файл

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="PooledArrayBuilderAsRefDescription" xml:space="preserve">
<value>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</value>
<comment>An optional longer localizable description of the diagnostic.</comment>
</data>
<data name="PooledArrayBuilderAsRefMessage" xml:space="preserve">
<value>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</value>
<comment>The format-able message the diagnostic displays.</comment>
</data>
<data name="PooledArrayBuilderAsRefTitle" xml:space="preserve">
<value>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</value>
<comment>The title of the diagnostic.</comment>
</data>
</root>

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Razor.Diagnostics.Analyzers;
internal static class WellKnownTypeNames
{
public const string PooledArrayBuilderExtensions = "Microsoft.AspNetCore.Razor.PooledObjects.PooledArrayBuilderExtensions";
}

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.cs.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="cs" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.de.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="de" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.es.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="es" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.fr.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="fr" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.it.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="it" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.ja.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ja" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.ko.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ko" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.pl.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="pl" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.pt-BR.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="pt-BR" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.ru.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ru" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.tr.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="tr" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.zh-Hans.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="zh-Hans" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

22
src/Analyzers/Razor.Diagnostics.Analyzers/xlf/Resources.zh-Hant.xlf сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="zh-Hant" original="../Resources.resx">
<body>
<trans-unit id="PooledArrayBuilderAsRefDescription">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable.</target>
<note>An optional longer localizable description of the diagnostic.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefMessage">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The format-able message the diagnostic displays.</note>
</trans-unit>
<trans-unit id="PooledArrayBuilderAsRefTitle">
<source>Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</source>
<target state="new">Instance of PooledArrayBuilder&lt;T&gt;.AsRef() must be a 'using' variable</target>
<note>The title of the diagnostic.</note>
</trans-unit>
</body>
</file>
</xliff>

Просмотреть файл

@ -40,5 +40,10 @@
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" NoWarn="NU1608" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" PrivateAssets="All" />
<PackageReference Include="Roslyn.Diagnostics.Analyzers" PrivateAssets="All" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\Analyzers\Razor.Diagnostics.Analyzers\Razor.Diagnostics.Analyzers.csproj"
PrivateAssets="all"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer" />
</ItemGroup>
</Project>

Просмотреть файл

@ -495,16 +495,16 @@ internal abstract partial class SyntaxNode
// Walk backwards until we find a non-whitespace token. We accomplish this by looking up the stack and walking nodes backwards from where we
// were located.
if (tryWalkBackwards(stack, out foundToken))
if (tryWalkBackwards(ref stack.AsRef(), out foundToken))
{
return foundToken;
}
// Encountered a newline while backtracking, so we need to walk forward instead.
return walkForward(stack);
return walkForward(ref stack.AsRef());
}
bool tryWalkBackwards(PooledArrayBuilder<(SyntaxNode node, int nodeIndexInParent)> stack, [NotNullWhen(true)] out SyntaxToken? foundToken)
bool tryWalkBackwards(ref PooledArrayBuilder<(SyntaxNode node, int nodeIndexInParent)> stack, [NotNullWhen(true)] out SyntaxToken? foundToken)
{
// Can't just pop the stack, we may need to rewalk from the start to find the next node if this fails
for (var originalStackPosition = stack.Count - 1; originalStackPosition >= 0; originalStackPosition--)
@ -528,7 +528,7 @@ internal abstract partial class SyntaxNode
throw new ArgumentOutOfRangeException(nameof(position));
}
SyntaxToken walkForward(PooledArrayBuilder<(SyntaxNode node, int nodeIndexInParent)> stack)
SyntaxToken walkForward(ref PooledArrayBuilder<(SyntaxNode node, int nodeIndexInParent)> stack)
{
while (stack.TryPop(out var entry))
{

Просмотреть файл

@ -7,6 +7,11 @@
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="$(Tooling_MicrosoftCodeAnalysisAnalyzersPackageVersion)" NoWarn="NU1608" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="$(Tooling_MicrosoftCodeAnalysisBannedApiAnalyzersPackageVersion)" />
<PackageReference Include="Roslyn.Diagnostics.Analyzers" Version="$(Tooling_RoslynDiagnosticsAnalyzersPackageVersion)" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\Analyzers\Razor.Diagnostics.Analyzers\Razor.Diagnostics.Analyzers.csproj"
PrivateAssets="all"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer" />
</ItemGroup>
</Project>

Просмотреть файл

@ -299,6 +299,7 @@
<ItemGroup>
<BindingRedirectAssemblies Include="@(ProjectReference)" AssemblyName="%(Filename)" />
<BindingRedirectAssemblies Remove="@(ProjectReference)" Condition="%(ProjectReference.Name) == 'Microsoft.CodeAnalysis.Remote.Razor.CoreComponents'" />
<BindingRedirectAssemblies Remove="@(ProjectReference)" Condition="%(ProjectReference.ReferenceOutputAssembly) == 'false'" />
<BindingRedirectAssemblies Include="$(AssemblyName)" AssemblyName="$(AssemblyName)" />
</ItemGroup>
<PropertyGroup>

Просмотреть файл

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Razor.Serialization.Json;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Utilities;
using Xunit;

Просмотреть файл

@ -10,12 +10,19 @@
<!-- In theory we want to have this property set, but our pipeline doesn't set the access tokens yet -->
<PublishWindowsPdb Condition="'$(DotNetSymbolServerTokenMsdl)'!='' and '$(DotNetSymbolServerTokenSymWeb)'!=''">true</PublishWindowsPdb>
<RollForward Condition="'$(IsTestProject)' == 'true'">LatestMajor</RollForward>
</PropertyGroup>
<ItemGroup Condition="'$(DotNetBuildFromSource)' != 'true'">
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="$(Tooling_MicrosoftCodeAnalysisAnalyzersPackageVersion)" NoWarn="NU1608" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="$(Tooling_MicrosoftCodeAnalysisBannedApiAnalyzersPackageVersion)" />
<PackageReference Include="Roslyn.Diagnostics.Analyzers" Version="$(Tooling_RoslynDiagnosticsAnalyzersPackageVersion)" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\Analyzers\Razor.Diagnostics.Analyzers\Razor.Diagnostics.Analyzers.csproj"
PrivateAssets="all"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFrameworks)</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);$(DefaultNetFxTargetFramework)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xunit.Combinatorial" Version="$(XunitCombinatorialPackageVersion)" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,83 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Utilities.Shared.Test.PooledObjects;
public class PooledArrayBuilderTests
{
[Theory]
[CombinatorialData]
public void AddElements([CombinatorialRange(0, 8)] int count)
{
using var builder = new PooledArrayBuilder<int>();
for (var i = 0; i < count; i++)
{
builder.Add(i);
}
for (var i = 0; i < count; i++)
{
Assert.Equal(i, builder[i]);
}
var result = builder.DrainToImmutable();
for (var i = 0; i < count; i++)
{
Assert.Equal(i, result[i]);
}
}
public static IEnumerable<object[]> RemoveAtIndex_Data
{
get
{
for (var count = 0; count < 8; count++)
{
for (var removeIndex = 0; removeIndex < 8; removeIndex++)
{
if (removeIndex < count)
{
yield return new object[] { count, removeIndex };
}
}
}
}
}
[Theory]
[MemberData(nameof(RemoveAtIndex_Data))]
public void RemoveAtIndex(int count, int removeIndex)
{
using var builder = new PooledArrayBuilder<int>();
for (var i = 0; i < count; i++)
{
builder.Add(i);
}
var newCount = count;
var newValue = removeIndex;
// Now, remove each element at removeIndex.
for (var i = removeIndex; i < builder.Count; i++)
{
builder.RemoveAt(removeIndex);
newCount--;
newValue++;
Assert.Equal(newCount, builder.Count);
// Check the values starting at removeIndex.
for (var j = removeIndex; j < newCount; j++)
{
Assert.Equal(newValue + (j - removeIndex), builder[j]);
}
}
}
}

Просмотреть файл

@ -0,0 +1,5 @@
{
"methodDisplay": "method",
"shadowCopy": false,
"parallelizeTestCollections": false
}

Просмотреть файл

@ -29,4 +29,34 @@ internal static class EnumerableExtensions
return results.DrainToImmutable();
}
}
public static bool TryGetCount<T>(this IEnumerable<T> sequence, out int count)
{
#if NET6_0_OR_GREATER
return Linq.Enumerable.TryGetNonEnumeratedCount(sequence, out count);
#else
return TryGetCount<T>((IEnumerable)sequence, out count);
#endif
}
public static bool TryGetCount<T>(this IEnumerable sequence, out int count)
{
switch (sequence)
{
case ICollection collection:
count = collection.Count;
return true;
case ICollection<T> collection:
count = collection.Count;
return true;
case IReadOnlyCollection<T> collection:
count = collection.Count;
return true;
}
count = 0;
return false;
}
}

Просмотреть файл

@ -9,7 +9,7 @@ using Microsoft.Extensions.ObjectPool;
namespace Microsoft.AspNetCore.Razor.PooledObjects;
internal static partial class PooledObjectExtensions
internal static partial class Extensions
{
public static PooledObject<T> GetPooledObject<T>(this ObjectPool<T> pool)
where T : class

Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNetCore.Razor.PooledObjects;
internal static class PooledArrayBuilderExtensions
{
/// <summary>
/// Gets a mutable reference to a <see cref="PooledArrayBuilder{T}"/> stored in a <c>using</c> variable.
/// </summary>
/// <remarks>
/// <para>This supporting method allows <see cref="PooledArrayBuilder{T}"/>, a non-copyable <see langword="struct"/>
/// implementing <see cref="IDisposable"/>, to be used with <c>using</c> statements while still allowing them to
/// be passed by reference in calls. The following two calls are equivalent:</para>
///
/// <code>
/// using var array = PooledArrayBuilder&lt;T&gt;.Empty;
///
/// // Using the 'Unsafe.AsRef' method
/// Method(ref Unsafe.AsRef(in builder));
///
/// // Using this helper method
/// Method(ref builder.AsRef());
/// </code>
///
/// <para>⚠ Do not move or rename this method without updating the corresponding
/// Razor.Diagnostics.Analyzers\PooledArrayBuilderAsRefAnalyzer.cs.</para>
/// </remarks>
/// <typeparam name="T">The type of element stored in the pooled array builder.</typeparam>
/// <param name="builder">A read-only reference to a pooled array builder which is part of a <c>using</c> statement.</param>
/// <returns>A mutable reference to the pooled array builder.</returns>
public static ref PooledArrayBuilder<T> AsRef<T>(this in PooledArrayBuilder<T> builder)
=> ref Unsafe.AsRef(in builder);
}

Просмотреть файл

@ -1,51 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Utilities;
namespace Microsoft.AspNetCore.Razor.PooledObjects;
internal ref partial struct PooledArrayBuilder<T>
internal partial struct PooledArrayBuilder<T>
{
public struct Enumerator : IEnumerator<T>
[NonCopyable]
public struct Enumerator(in PooledArrayBuilder<T> builder)
{
private readonly ImmutableArray<T>.Builder? _builder;
private int _index;
private T? _current;
// Enumerate a copy of the original.
private readonly PooledArrayBuilder<T> _builder = new(builder);
private int _index = 0;
private T _current = default!;
public Enumerator(ImmutableArray<T>.Builder builder)
{
_builder = builder;
_index = 0;
_current = default;
}
public T Current => _current!;
object? IEnumerator.Current => Current;
public readonly void Dispose()
{
}
public readonly T Current => _current;
public bool MoveNext()
{
if (_builder is { } builder && _index < builder.Count)
if (_index >= _builder.Count)
{
_current = builder[_index];
_index++;
return true;
return false;
}
return false;
}
void IEnumerator.Reset()
{
_index = 0;
_current = default;
_current = _builder[_index];
_index++;
return true;
}
}
}

Просмотреть файл

@ -4,8 +4,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.ObjectPool;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Razor.Utilities;
namespace Microsoft.AspNetCore.Razor.PooledObjects;
@ -13,58 +15,198 @@ namespace Microsoft.AspNetCore.Razor.PooledObjects;
/// Wraps a pooled <see cref="ImmutableArray{T}.Builder"/> but doesn't allocate it until
/// it's needed. Note: Dispose this to ensure that the pooled array builder is returned
/// to the pool.
///
/// There is significant effort to avoid retrieving the <see cref="ImmutableArray{T}.Builder"/>.
/// For very small arrays of length 4 or less, the elements will be stored on the stack. If the array
/// grows larger than 4 elements, a builder will be employed. Afterward, the build will
/// continue to be used, even if the arrays shrinks and has fewer than 4 elements.
/// </summary>
internal ref partial struct PooledArrayBuilder<T>
[NonCopyable]
internal partial struct PooledArrayBuilder<T> : IDisposable
{
private readonly ObjectPool<ImmutableArray<T>.Builder> _pool;
private readonly int? _capacity;
public static PooledArrayBuilder<T> Empty => default;
/// <summary>
/// The number of items that can be stored inline.
/// </summary>
private const int InlineCapacity = 4;
/// <summary>
/// A builder to be used as storage after the first time that the number
/// of items exceeds <see cref="InlineCapacity"/>. Once the builder is used,
/// it is still used even if the number of items shrinks below <see cref="InlineCapacity"/>.
/// Essentially, if this field is non-null, it will be used as storage.
/// </summary>
private ImmutableArray<T>.Builder? _builder;
public PooledArrayBuilder()
: this(null, null)
/// <summary>
/// An optional initial capacity for the builder.
/// </summary>
private int? _capacity;
private T _element0;
private T _element1;
private T _element2;
private T _element3;
/// <summary>
/// The number of inline elements. Note that this value is only used when <see cref="_builder"/> is <see langword="null"/>.
/// </summary>
private int _inlineCount;
public PooledArrayBuilder(int? capacity = null)
{
_capacity = capacity is > InlineCapacity ? capacity : null;
_element0 = default!;
_element1 = default!;
_element2 = default!;
_element3 = default!;
_inlineCount = 0;
}
public PooledArrayBuilder(ObjectPool<ImmutableArray<T>.Builder>? pool = null, int? capacity = null)
private PooledArrayBuilder(in PooledArrayBuilder<T> builder)
{
_pool = pool ?? ArrayBuilderPool<T>.Default;
_capacity = capacity;
}
public readonly T this[int i]
{
get
{
if (_builder is null || Count <= i)
{
throw new IndexOutOfRangeException();
}
return _builder[i];
}
set
{
if (_builder is null || Count <= i)
{
throw new IndexOutOfRangeException();
}
_builder[i] = value;
}
// This is an intentional copy used to create an Enumerator.
#pragma warning disable RS0042
this = builder;
#pragma warning restore RS0042
}
public void Dispose()
{
ClearAndFree();
// Return _builder to the pool if necessary. Note that we don't need to clear the inline elements here
// because this type is intended to be allocated on the stack and the GC can reclaim objects from the
// stack after the last use of a reference to them.
if (_builder is { } builder)
{
ArrayBuilderPool<T>.Default.Return(builder);
_builder = null;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearInlineElement(int index)
{
Debug.Assert(_inlineCount <= InlineCapacity);
// Clearing out an item makes it potentially available for garbage collection.
// Note: On .NET Core, we can be a bit more judicious and only zero-out
// fields that contain references to heap-allocated objects.
#if NETCOREAPP
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
#endif
{
SetInlineElement(index, default!);
}
}
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly get
{
if (_builder is { } builder)
{
return builder[index];
}
if (index >= _inlineCount)
{
ThrowIndexOutOfRangeException();
}
return GetInlineElement(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (_builder is { } builder)
{
builder[index] = value;
return;
}
if (index >= _inlineCount)
{
ThrowIndexOutOfRangeException();
}
SetInlineElement(index, value);
}
}
public T this[Index index]
{
readonly get => this[index.GetOffset(Count)];
set => this[index.GetOffset(Count)] = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly T GetInlineElement(int index)
{
Debug.Assert(_inlineCount <= InlineCapacity);
return index switch
{
0 => _element0,
1 => _element1,
2 => _element2,
3 => _element3,
_ => Assumed.Unreachable<T>()
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetInlineElement(int index, T value)
{
Debug.Assert(_inlineCount <= InlineCapacity);
switch (index)
{
case 0:
_element0 = value;
break;
case 1:
_element1 = value;
break;
case 2:
_element2 = value;
break;
case 3:
_element3 = value;
break;
default:
Assumed.Unreachable();
break;
}
}
public readonly int Count
=> _builder?.Count ?? 0;
=> _builder?.Count ?? _inlineCount;
public void Add(T item)
{
_builder ??= GetBuilder();
_builder.Add(item);
if (_builder is { } builder)
{
builder.Add(item);
}
else if (_inlineCount < InlineCapacity)
{
SetInlineElement(_inlineCount, item);
_inlineCount++;
}
else
{
Debug.Assert(_inlineCount == InlineCapacity);
MoveInlineItemsToBuilder();
_builder.Add(item);
}
}
public void AddRange(ImmutableArray<T> items)
@ -74,62 +216,144 @@ internal ref partial struct PooledArrayBuilder<T>
return;
}
_builder ??= GetBuilder();
_builder.AddRange(items);
}
public void AddRange(IReadOnlyList<T> items)
{
if (items.Count == 0)
if (_builder is { } builder)
{
return;
builder.AddRange(items);
}
else if (_inlineCount + items.Length <= InlineCapacity)
{
foreach (var item in items)
{
SetInlineElement(_inlineCount, item);
_inlineCount++;
}
}
else
{
MoveInlineItemsToBuilder();
_builder.AddRange(items);
}
_builder ??= GetBuilder();
_builder.AddRange(items);
}
public void AddRange(IEnumerable<T> items)
{
_builder ??= GetBuilder();
_builder.AddRange(items);
if (_builder is { } builder)
{
builder.AddRange(items);
return;
}
if (!items.TryGetCount(out var count))
{
// We couldn't retrieve a count, so we have to enumerate the elements.
foreach (var item in items)
{
Add(item);
}
return;
}
if (count == 0)
{
// No items, so there's nothing to do.
return;
}
if (_inlineCount + count <= InlineCapacity)
{
// The items can fit into our inline elements.
foreach (var item in items)
{
SetInlineElement(_inlineCount, item);
_inlineCount++;
}
}
else
{
// The items can't fit into our inline elements, so we switch to a builder.
MoveInlineItemsToBuilder();
_builder.AddRange(items);
}
}
public readonly Enumerator GetEnumerator()
=> _builder is { } builder
? new Enumerator(builder)
: default;
public void ClearAndFree()
public void Clear()
{
if (_builder is { } builder)
{
_pool.Return(builder);
_builder = null;
// Keep using a real builder to avoid churn in the object pool.
builder.Clear();
}
}
public readonly void RemoveAt(int index)
{
if (_builder is null || Count <= index)
else
{
throw new IndexOutOfRangeException();
var oldCapacity = _capacity;
this = Empty;
_capacity = oldCapacity;
}
_builder.RemoveAt(index);
}
private readonly ImmutableArray<T>.Builder GetBuilder()
public readonly Enumerator GetEnumerator()
=> new(in this);
public void RemoveAt(int index)
{
var result = _pool.Get();
if (_capacity is int capacity)
if (_builder is { } builder)
{
result.SetCapacityIfLarger(capacity);
builder.RemoveAt(index);
return;
}
return result;
if (index < 0 || index >= _inlineCount)
{
ThrowIndexOutOfRangeException();
}
// Copy inline elements depending on the index to be removed.
switch (index)
{
case 0:
_element0 = _element1;
if (_inlineCount > 1)
{
_element1 = _element2;
}
if (_inlineCount > 2)
{
_element2 = _element3;
}
break;
case 1:
_element1 = _element2;
if (_inlineCount > 2)
{
_element2 = _element3;
}
break;
case 2:
_element2 = _element3;
break;
}
// Clear the last element if necessary.
if (_inlineCount > 3)
{
ClearInlineElement(3);
}
// Decrement the count.
_inlineCount--;
}
public void RemoveAt(Index index)
=> RemoveAt(index.GetOffset(Count));
/// <summary>
/// Returns the current contents as an <see cref="ImmutableArray{T}"/> and sets
/// the collection to a zero length array.
@ -141,18 +365,66 @@ internal ref partial struct PooledArrayBuilder<T>
/// will then be set to a zero-length array.
/// </remarks>
/// <returns>An immutable array.</returns>
public readonly ImmutableArray<T> DrainToImmutable()
=> _builder?.DrainToImmutable() ?? ImmutableArray<T>.Empty;
public ImmutableArray<T> DrainToImmutable()
{
if (_builder is { } builder)
{
return builder.DrainToImmutable();
}
var inlineArray = InlineItemsToImmutableArray();
var oldCapacity = _capacity;
this = Empty;
_capacity = oldCapacity;
return inlineArray;
}
public readonly ImmutableArray<T> ToImmutable()
=> _builder?.ToImmutable() ?? ImmutableArray<T>.Empty;
{
if (_builder is { } builder)
{
return builder.ToImmutable();
}
return InlineItemsToImmutableArray();
}
private readonly ImmutableArray<T> InlineItemsToImmutableArray()
{
Debug.Assert(_inlineCount <= InlineCapacity);
return _inlineCount switch
{
0 => ImmutableArray<T>.Empty,
1 => ImmutableArray.Create(_element0),
2 => ImmutableArray.Create(_element0, _element1),
3 => ImmutableArray.Create(_element0, _element1, _element2),
_ => ImmutableArray.Create(_element0, _element1, _element2, _element3)
};
}
public readonly T[] ToArray()
=> _builder?.ToArray() ?? Array.Empty<T>();
{
if (_builder is { } builder)
{
return builder.ToArray();
}
return _inlineCount switch
{
0 => Array.Empty<T>(),
1 => new[] { _element0 },
2 => new[] { _element0, _element1 },
3 => new[] { _element0, _element1, _element2 },
_ => new[] { _element0, _element1, _element2, _element3 }
};
}
public void Push(T item)
{
this.Add(item);
Add(item);
}
public readonly T Peek()
@ -160,14 +432,16 @@ internal ref partial struct PooledArrayBuilder<T>
return this[^1];
}
public readonly T Pop()
public T Pop()
{
var item = this[^1];
RemoveAt(Count - 1);
var index = ^1;
var item = this[index];
RemoveAt(index);
return item;
}
public readonly bool TryPop([MaybeNullWhen(false)] out T item)
public bool TryPop([MaybeNullWhen(false)] out T item)
{
if (Count == 0)
{
@ -178,4 +452,37 @@ internal ref partial struct PooledArrayBuilder<T>
item = Pop();
return true;
}
/// <summary>
/// This is present to help the JIT inline methods that need to throw
/// a <see cref="IndexOutOfRangeException"/>.
/// </summary>
[DoesNotReturn]
private static void ThrowIndexOutOfRangeException()
=> throw new IndexOutOfRangeException();
[MemberNotNull(nameof(_builder))]
private void MoveInlineItemsToBuilder()
{
Debug.Assert(_builder is null);
var builder = ArrayBuilderPool<T>.Default.Get();
if (_capacity is int capacity)
{
builder.SetCapacityIfLarger(capacity);
}
_builder = builder;
// Add the inline items and clear their field values.
for (var i = 0; i < _inlineCount; i++)
{
builder.Add(GetInlineElement(i));
ClearInlineElement(i);
}
// Since _inlineCount tracks the number of inline items used, we zero it out here.
_inlineCount = 0;
}
}

Просмотреть файл

@ -3,6 +3,9 @@
using System.Runtime.CompilerServices;
// Razor shared utilities test assemblies
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Utilities.Shared.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
// Razor compiler assemblies
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Language, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Utilities;
[AttributeUsage(AttributeTargets.Struct)]
internal sealed class NonCopyableAttribute : Attribute
{
}