diff --git a/.markdownlint.json b/.markdownlint.json index c464661bec..183fb8f237 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1,6 +1,7 @@ { "default": true, "MD013": false, + "MD024": false, "MD026": false, "MD041": false, "MD046": { diff --git a/Directory.Build.props b/Directory.Build.props index 7a056e3aae..efc14905e8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,6 +12,11 @@ + + + false + + preview 16 diff --git a/README.md b/README.md index 44f4e31f11..c70f4a7360 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,20 @@ Microsoft created a set of analyzers called [Microsoft.CodeAnalysis.FxCopAnalyze ## Main analyzers +Recently the set of analyzer packages produced by this repository have been consolidated. The following table summarizes this information: + +| NuGet Package Name | Version | Summary | +|----------|:-------:|---------| +| Microsoft.CodeAnalysis.NetAnalyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.NetAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.NetAnalyzers) | ✔️ Primary analyzer package for this repo. Included default for .NET 5+. For earlier targets [read more](#microsoftcodeanalysisnetanalyzers). | +| Microsoft.CodeAnalysis.BannedApiAnalyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.BannedApiAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.BannedApiAnalyzers) | ✔️ Allows banning use of arbitrary code. [Read more](#microsoftcodeanalysisbannedapianalyzers). | +| Microsoft.CodeAnalysis.PublicApiAnalyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.PublicApiAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.PublicApiAnalyzers) | ✔️ Helps library authors monitor changes to their public APIs. [Read more](#microsoftcodeanalysispublicapianalyzers). | +| Microsoft.CodeAnalysis.Analyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.Analyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.Analyzers) | ⚠️ Intended projects providing analyzers and code fixes. [Read more](#microsoftcodeanalysisanalyzers). | +| Roslyn.Diagnostics.Analyzers | [![NuGet](https://img.shields.io/nuget/v/Roslyn.Diagnostics.Analyzers.svg)](https://www.nuget.org/packages/Roslyn.Diagnostics.Analyzers) | ⚠️ Rules specific to the Roslyn project, not intended for general consumption. [Read more](#roslyndiagnosticsanalyzers). | +| Microsoft.CodeAnalysis.FxCopAnalyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.FxCopAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.FxCopAnalyzers) | ⛔ Use `Microsoft.CodeAnalysis.NetAnalyzers` instead. [Read more](#microsoftcodeanalysisfxcopanalyzers). | +| Microsoft.CodeQuality.Analyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeQuality.Analyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeQuality.Analyzers) | ⛔ Use `Microsoft.CodeAnalysis.NetAnalyzers` instead. [Read more](#microsoftcodequalityanalyzers). | +| Microsoft.NetCore.Analyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.NetCore.Analyzers.svg)](https://www.nuget.org/packages/Microsoft.NetCore.Analyzers) | ⛔ Use `Microsoft.CodeAnalysis.NetAnalyzers` instead. [Read more](#microsoftnetcoreanalyzers). | +| Microsoft.NetFramework.Analyzers | [![NuGet](https://img.shields.io/nuget/v/Microsoft.NetFramework.Analyzers.svg)](https://www.nuget.org/packages/Microsoft.NetFramework.Analyzers) | ⛔ Use `Microsoft.CodeAnalysis.NetAnalyzers` instead. [Read more](#microsoftnetcoreanalyzers). | + ### Microsoft.CodeAnalysis.NetAnalyzers *Latest stable version:* [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.NetAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.NetAnalyzers) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 77f344f11d..e90ed77c0b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -91,7 +91,7 @@ jobs: _configuration: Release pool: name: NetCorePublic-Pool - queue: BuildPool.Ubuntu.1604.amd64.Open + queue: BuildPool.Ubuntu.1804.amd64.Open timeoutInMinutes: 40 steps: - checkout: self @@ -122,5 +122,5 @@ jobs: steps: - script: sudo npm install -g markdownlint-cli displayName: Install markdownlint-cli - - script: markdownlint '**/*.md' -i '**/AnalyzerReleases.*.md' -i 'docs/Analyzer reference page template.md' -i 'eng/common/**/*.md' + - script: markdownlint '**/*.md' -i 'docs/Analyzer reference page template.md' -i 'eng/common/**/*.md' displayName: Run markdownlint diff --git a/eng/GenerateAnalyzerNuspec.targets b/eng/GenerateAnalyzerNuspec.targets index e444794396..a79e9d33ff 100644 --- a/eng/GenerateAnalyzerNuspec.targets +++ b/eng/GenerateAnalyzerNuspec.targets @@ -144,7 +144,7 @@ <_NuspecMetadata Include="copyright=$(Copyright)" /> <_NuspecMetadata Include="license=$(PackageLicenseExpression)" /> <_NuspecMetadata Include="projectUrl=$(PackageProjectUrl)" /> - <_NuspecMetadata Include="iconUrl=$(PackageIconUrl)" /> + <_NuspecMetadata Include="icon=$(PackageIcon)" /> <_NuspecMetadata Include="releaseNotes=$(PackageReleaseNotes)" /> <_NuspecMetadata Include="tags=$(PackageTags)" /> <_NuspecMetadata Include="serviceable=$(Serviceable)" /> diff --git a/eng/Signing.props b/eng/Signing.props new file mode 100644 index 0000000000..586fe4875a --- /dev/null +++ b/eng/Signing.props @@ -0,0 +1,5 @@ + + + true + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7ac6d116ad..ac27761e27 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,9 +3,9 @@ - + https://github.com/dotnet/arcade - 1d951297eb7bdd29a31dff3149606152717ed6b4 + c5c6ef92686f208055e884bad45d32966f0b1f95 diff --git a/eng/Versions.props b/eng/Versions.props index d2adbd8cf1..1bb2b1edb1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -3,7 +3,7 @@ 3.3.3 beta1 6.0.0 - preview4 + preview5 $(VersionPrefix) $(NetAnalyzersVersionPrefix) diff --git a/nuget/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj b/nuget/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj index c290a3127e..741235007a 100644 --- a/nuget/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj +++ b/nuget/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj @@ -13,6 +13,7 @@ Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics PublicApi ApiAnalyzer $(RepoRoot)src\PublicApiAnalyzers $(RepoRoot)src\PublicApiAnalyzers + true diff --git a/nuget/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj b/nuget/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj index 0ad37d3e79..4525797672 100644 --- a/nuget/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj +++ b/nuget/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj @@ -10,6 +10,7 @@ Roslyn.Diagnostics Analyzers Roslyn.Diagnostics Analyzers Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics + true diff --git a/nuget/Text.Analyzers/Text.Analyzers.Package.csproj b/nuget/Text.Analyzers/Text.Analyzers.Package.csproj index df3ae1f1f3..f8dc0d7020 100644 --- a/nuget/Text.Analyzers/Text.Analyzers.Package.csproj +++ b/nuget/Text.Analyzers/Text.Analyzers.Package.csproj @@ -11,6 +11,7 @@ Text Analyzers Text Analyzers Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics + true true diff --git a/src/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md b/src/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md index 4356fad5fd..f19591db48 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md @@ -1,6 +1,7 @@ ## Release 2.9.8 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- -RS1014 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpImmutableObjectMethodAnalyzer \ No newline at end of file +RS1014 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpImmutableObjectMethodAnalyzer diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md index df4983403b..7ce0a429a3 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md @@ -1,6 +1,7 @@ ## Release 3.0.0 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS1024 | MicrosoftCodeAnalysisCorrectness | Warning | CompareSymbolsCorrectlyAnalyzer @@ -20,10 +21,10 @@ RS2006 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCr RS2007 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) RS2008 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) - ## Release 2.9.8 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS1001 | MicrosoftCodeAnalysisCorrectness | Warning | DiagnosticAnalyzerAttributeAnalyzer @@ -52,6 +53,7 @@ RS1023 | Library | Warning | UpgradeMSBuildWorkspaceAnalyzer ## Release 3.3.0 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS1031 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md index 72c1634f2a..c2e30e9f91 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,6 +1,7 @@ ; Please do not edit this file manually, it should only be updated through code fix application. ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS1034 | MicrosoftCodeAnalysisPerformance | Warning | PreferIsKindAnalyzer diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs b/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs index 216940bfe0..5ebe82460a 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs @@ -21,6 +21,7 @@ public class CompareSymbolsCorrectlyAnalyzer : DiagnosticAnalyzer private static readonly string s_symbolTypeFullName = typeof(ISymbol).FullName; private const string s_symbolEqualsName = nameof(ISymbol.Equals); + private const string s_HashCodeCombineName = "Combine"; public const string SymbolEqualityComparerName = "Microsoft.CodeAnalysis.SymbolEqualityComparer"; public static readonly DiagnosticDescriptor EqualityRule = new( @@ -78,9 +79,10 @@ public override void Initialize(AnalysisContext context) OperationKind.BinaryOperator); var equalityComparerMethods = GetEqualityComparerMethodsToCheck(compilation); + var systemHashCode = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemHashCode); context.RegisterOperationAction( - context => HandleInvocationOperation(in context, symbolType, symbolEqualityComparerType, equalityComparerMethods), + context => HandleInvocationOperation(in context, symbolType, symbolEqualityComparerType, equalityComparerMethods, systemHashCode), OperationKind.Invocation); if (symbolEqualityComparerType != null) @@ -140,8 +142,12 @@ private static void HandleBinaryOperator(in OperationAnalysisContext context, IN context.ReportDiagnostic(binary.Syntax.GetLocation().CreateDiagnostic(EqualityRule)); } - private static void HandleInvocationOperation(in OperationAnalysisContext context, INamedTypeSymbol symbolType, - INamedTypeSymbol? symbolEqualityComparerType, ImmutableDictionary> equalityComparerMethods) + private static void HandleInvocationOperation( + in OperationAnalysisContext context, + INamedTypeSymbol symbolType, + INamedTypeSymbol? symbolEqualityComparerType, + ImmutableDictionary> equalityComparerMethods, + INamedTypeSymbol? systemHashCodeType) { var invocationOperation = (IInvocationOperation)context.Operation; var method = invocationOperation.TargetMethod; @@ -149,14 +155,16 @@ private static void HandleInvocationOperation(in OperationAnalysisContext contex switch (method.Name) { case WellKnownMemberNames.ObjectGetHashCode: - if (IsNotInstanceInvocationOrNotOnSymbol(invocationOperation, symbolType)) + // This is a call for an instance of ISymbol.GetHashCode() + // without the correct arguments + if (IsSymbolType(invocationOperation.Instance, symbolType)) { context.ReportDiagnostic(invocationOperation.CreateDiagnostic(GetHashCodeRule)); } break; case s_symbolEqualsName: - if (symbolEqualityComparerType != null && IsNotInstanceInvocationOrNotOnSymbol(invocationOperation, symbolType)) + if (symbolEqualityComparerType is not null && IsNotInstanceInvocationOrNotOnSymbol(invocationOperation, symbolType)) { var parameters = invocationOperation.Arguments; if (parameters.All(p => IsSymbolType(p.Value, symbolType))) @@ -166,10 +174,21 @@ private static void HandleInvocationOperation(in OperationAnalysisContext contex } break; + case s_HashCodeCombineName: + // A call System.HashCode.Combine(ISymbol) will do the wrong thing and should be avoided + if (systemHashCodeType is not null && + invocationOperation.Instance is null && + systemHashCodeType.Equals(method.ContainingType, SymbolEqualityComparer.Default) && + invocationOperation.Arguments.Any(arg => IsSymbolType(arg.Value, symbolType))) + { + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(GetHashCodeRule)); + } + break; + default: if (equalityComparerMethods.TryGetValue(method.Name, out var possibleMethodTypes)) { - if (symbolEqualityComparerType != null && + if (symbolEqualityComparerType is not null && possibleMethodTypes.Contains(method.ContainingType.OriginalDefinition) && IsBehavingOnSymbolType(method, symbolType) && !invocationOperation.Arguments.Any(arg => IsSymbolType(arg.Value, symbolEqualityComparerType))) @@ -228,9 +247,9 @@ private static void HandleObjectCreation(in OperationAnalysisContext context, IN } } - private static bool IsSymbolType(IOperation operation, INamedTypeSymbol symbolType) + private static bool IsSymbolType(IOperation? operation, INamedTypeSymbol symbolType) { - if (operation.Type is object && IsSymbolType(operation.Type, symbolType)) + if (operation?.Type is object && IsSymbolType(operation.Type, symbolType)) { return true; } diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf index ab6b2bfd35..876746cf5c 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf @@ -259,7 +259,7 @@ Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. - Utilisez 'syntax.IsKind(kind)' au lieu de 'syntax.Kind() == kind' pour la vérification des genres de syntaxe. Dans la mesure où le code utilisant 'IsKind' est légèrement plus efficace au moment de l'exécution, l'utilisation systématique de cette forme permet d'améliorer les performances dans les scénarios d'analyse complexes. + Utilisez 'syntax.IsKind(kind)' au lieu de 'syntax.Kind() == kind' pour la vérification des genres de syntaxe. Dans la mesure où le code utilisant 'IsKind' est légèrement plus efficace au moment de l'exécution, l'utilisation cohérente de ce formulaire permet d'améliorer les performances dans les scénarios d'analyse complexes. diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf index c3edebc972..2d4594133c 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf @@ -304,7 +304,7 @@ Remove duplicate entries for diagnostic ID between analyzer releases - アナライザー リリース間で診断 ID の重複しているエントリを削除してください + アナライザー リリース間で診断 ID の重複しているエントリを削除する diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf index 735d8f20c2..86d75d1d02 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf @@ -557,7 +557,7 @@ A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. - 사용자가 단일 코드 수정 사항을 사용하여 기본 진단의 여러 인스턴스를 수정할 수 있도록 하려면 CodeFixProvider가 FixAll 지원을 제공해야 합니다. 자세한 내용은 https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md의 설명서를 참조하세요. + 사용자가 단일 코드 수정 사항을 사용하여 기본 진단의 여러 인스턴스를 수정할 수 있도록 하려면 CodeFixProvider가 FixAll 지원을 제공해야 합니다. 자세한 내용은 https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md에 있는 문서를 참조하세요. @@ -692,4 +692,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf index 01846ad182..2623672afe 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf @@ -557,7 +557,7 @@ A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. - CodeFixProvider должен предоставлять поддержку FixAll, чтобы пользователи могли исправить несколько экземпляров базовых диагностических данных с помощью одного исправления кода. Дополнительные сведения см. в документации на странице https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md. + CodeFixProvider должен предоставлять поддержку FixAll, чтобы пользователи могли исправить несколько экземпляров базовых диагностических данных с помощью одного исправления кода. Дополнительные сведения см. в документации по адресу https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md. @@ -692,4 +692,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf index 828e04e0b1..4a5aa923b7 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Add rule entry to unshipped release file diff --git a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf index e7499be9c0..6f0ffb4eca 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf +++ b/src/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Add rule entry to unshipped release file diff --git a/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs b/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs index d406598486..bf9c78077c 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs +++ b/src/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Immutable; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; using Test.Utilities; using Xunit; using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier< @@ -1141,5 +1143,118 @@ public void M2(IEnumerable source, IEnumerable }, }.RunAsync(); } + + [Fact, WorkItem(4956, "https://github.com/dotnet/roslyn-analyzers/issues/4956")] + public async Task RS1024_StringGetHashCode() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { +@" +using System; + +class C +{ + void M() + { + ReadOnlySpan testROS = default; + int hashCode = string.GetHashCode(testROS, StringComparison.OrdinalIgnoreCase); + } +}" + , SymbolEqualityComparerStubCSharp + }, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies() + } + }.RunAsync(); + } + + [Fact] + public async Task RS1024_GetHashCodeOnInt64() + { + var code = @" +using System; +using Microsoft.CodeAnalysis; + +static class HashCodeHelper +{ + public static Int32 GetHashCode(Int64 x) => 0; + public static Int32 GetHashCode(ISymbol symbol) => [|symbol.GetHashCode()|]; +} + +public class C +{ + public int GetHashCode(Int64 obj) + { + return HashCodeHelper.GetHashCode(obj); + } + + public int GetHashCode(ISymbol symbol) + { + return HashCodeHelper.GetHashCode(symbol); + } + + public int GetHashCode(object o) + { + if (o is ISymbol symbol) + { + return [|HashCode.Combine(symbol)|]; + } + + return HashCode.Combine(o); + } + + public int GetHashCode(object o1, object o2) + { + if (o1 is ISymbol symbol1 && o2 is ISymbol symbol2) + { + return [|HashCode.Combine(symbol1, symbol2)|]; + } + + if (o1 is ISymbol symbolFirst) + { + return [|HashCode.Combine(symbolFirst, o2)|]; + } + + if (o2 is ISymbol symbolSecond) + { + return [|HashCode.Combine(o1, symbolSecond)|]; + } + + return HashCode.Combine(o1, o2); + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = + { + code, SymbolEqualityComparerStubCSharp + }, + }, + FixedState = + { + Sources = + { + code, SymbolEqualityComparerStubCSharp + }, + MarkupHandling = Testing.MarkupMode.Allow + }, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies() + }.RunAsync(); + } + + private static ReferenceAssemblies CreateNetCoreReferenceAssemblies() + => ReferenceAssemblies.NetCore.NetCoreApp31.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis", "3.0.0"), + new PackageIdentity("System.Runtime.Serialization.Formatters", "4.3.0"), + new PackageIdentity("System.Configuration.ConfigurationManager", "4.7.0"), + new PackageIdentity("System.Security.Cryptography.Cng", "4.7.0"), + new PackageIdentity("System.Security.Permissions", "4.7.0"), + new PackageIdentity("Microsoft.VisualBasic", "10.3.0"))); } } diff --git a/src/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md index 5e309c0d20..4940349f52 100644 --- a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md +++ b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md @@ -1,8 +1,9 @@ ## Release 2.9.8 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0030 | ApiDesign | Warning | SymbolIsBannedAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md) RS0031 | ApiDesign | Warning | SymbolIsBannedAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md) -RS0035 | ApiDesign | Error | RestrictedInternalsVisibleToAnalyzer \ No newline at end of file +RS0035 | ApiDesign | Error | RestrictedInternalsVisibleToAnalyzer diff --git a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf index 69ff8bbcc1..14505d02e4 100644 --- a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf +++ b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + The list of banned symbols contains a duplicate. diff --git a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf index 033b45bc38..003bdfbe92 100644 --- a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf +++ b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + The list of banned symbols contains a duplicate. diff --git a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.Fixer.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.Fixer.cs new file mode 100644 index 0000000000..2a6898f627 --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.Fixer.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.NetCore.Analyzers.Runtime; + +namespace Microsoft.NetCore.CSharp.Analyzers.Runtime +{ + [ExportCodeFixProvider(LanguageNames.CSharp)] + public sealed class CSharpUseSpanBasedStringConcatFixer : UseSpanBasedStringConcatFixer + { + private protected override SyntaxNode ReplaceInvocationMethodName(SyntaxGenerator generator, SyntaxNode invocationSyntax, string newName) + { + var memberAccessSyntax = (MemberAccessExpressionSyntax)((InvocationExpressionSyntax)invocationSyntax).Expression; + var oldNameSyntax = memberAccessSyntax.Name; + var newNameSyntax = generator.IdentifierName(newName).WithTriviaFrom(oldNameSyntax); + return invocationSyntax.ReplaceNode(oldNameSyntax, newNameSyntax); + } + + private protected override bool IsSystemNamespaceImported(Project project, IReadOnlyList namespaceImports) + { + foreach (var import in namespaceImports) + { + if (import is UsingDirectiveSyntax { Name: IdentifierNameSyntax { Identifier: { ValueText: nameof(System) } } }) + return true; + } + return false; + } + + private protected override IOperation WalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand) + { + return UseSpanBasedStringConcat.CSharpWalkDownBuiltInImplicitConversionOnConcatOperand(operand); + } + + private protected override bool IsNamedArgument(IArgumentOperation argumentOperation) + { + return argumentOperation.Syntax is ArgumentSyntax argumentSyntax && argumentSyntax.NameColon is not null; + } + } +} diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.cs new file mode 100644 index 0000000000..db26f01c6c --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.NetCore.Analyzers.Runtime; + +namespace Microsoft.NetCore.CSharp.Analyzers.Runtime +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpUseSpanBasedStringConcat : UseSpanBasedStringConcat + { + private protected override bool TryGetTopMostConcatOperation(IBinaryOperation binaryOperation, [NotNullWhen(true)] out IBinaryOperation? rootBinaryOperation) + { + if (!IsConcatOperation(binaryOperation)) + { + rootBinaryOperation = default; + return false; + } + + var current = binaryOperation; + while (current.Parent is IBinaryOperation parentBinaryOperation && IsConcatOperation(parentBinaryOperation)) + current = parentBinaryOperation; + + rootBinaryOperation = current; + return true; + + static bool IsConcatOperation(IBinaryOperation operation) + { + return operation.OperatorKind == BinaryOperatorKind.Add && + operation.Type.SpecialType == SpecialType.System_String; + } + } + + private protected override IOperation WalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand) + { + return CSharpWalkDownBuiltInImplicitConversionOnConcatOperand(operand); + } + } +} diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 033aae59c4..a82b9f618e 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,5 +1,7 @@ ; Please do not edit this file manually, it should only be updated through code fix application. + ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- CA1418 | Interoperability | Warning | UseValidPlatformString, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1418) @@ -8,6 +10,8 @@ CA1840 | Performance | Info | UseEnvironmentMembers, [Documentation](https://doc CA1841 | Performance | Info | PreferDictionaryContainsMethods, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1841) CA1842 | Performance | Info | DoNotUseWhenAllOrWaitAllWithSingleArgument, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1842) CA1843 | Performance | Info | DoNotUseWhenAllOrWaitAllWithSingleArgument, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1843) +CA1844 | Performance | Info | ProvideStreamMemoryBasedAsyncOverrides, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1844) +CA1845 | Performance | Info | UseSpanBasedStringConcat, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1845) CA2250 | Usage | Info | UseCancellationTokenThrowIfCancellationRequested, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250) ### Removed Rules diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs index e8bb838964..c54845fe1c 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffix.cs @@ -176,14 +176,15 @@ private static bool TryGetTypeSuffix(IEnumerable typeSymbols, foreach (var type in typeSymbols) { // User specific mapping has higher priority than hardcoded one - if (userMap.TryGetValue(type.OriginalDefinition, out var suffix) && - !RoslynString.IsNullOrWhiteSpace(suffix)) + if (userMap.TryGetValue(type.OriginalDefinition, out var suffix)) { - suffixInfo = SuffixInfo.Create(suffix, canSuffixBeCollection: false); - return true; + if (!RoslynString.IsNullOrWhiteSpace(suffix)) + { + suffixInfo = SuffixInfo.Create(suffix, canSuffixBeCollection: false); + return true; + } } - - if (hardcodedMap.TryGetValue(type.OriginalDefinition, out suffixInfo)) + else if (hardcodedMap.TryGetValue(type.OriginalDefinition, out suffixInfo)) { return true; } diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameofInPlaceOfString.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameofInPlaceOfString.cs index 22b7d9f65b..e3869f8c4f 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameofInPlaceOfString.cs +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameofInPlaceOfString.cs @@ -60,16 +60,11 @@ private void AnalyzeArgument(OperationAnalysisContext context) return; } - if (argument.Parameter == null) - { - return; - } - var stringText = (string)argument.Value.ConstantValue.Value; - var matchingParameter = argument.Parameter; - - switch (matchingParameter.Name) + // argument.Parameter will not be null here. The only case for it to be null is an __arglist argument, for which + // the argument type will not be of SpecialType.System_String. + switch (argument.Parameter.Name) { case ParamName: var parametersInScope = GetParametersInScope(context); diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf index b256a4dd8f..9270e58c3c 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Přidat přetížení System.Uri {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf index 971e9f90ad..cfccf9b299 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + „System.Uri“-Überladungen hinzufügen {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf index 323ddd7445..5d0db9608e 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Agregar sobrecargas de 'System.Uri' {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf index 98d15a4879..2da232dc5a 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf @@ -244,7 +244,7 @@ Add operator overload named alternate - Ajouter une surcharge d'opérateur nommée alternative + Ajouter une surcharge d'opérateur nommée alternate @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Ajouter une surcharge « System.Uri » {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf index 164c9478f9..caf89bc577 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Aggiungi overload 'System.Uri' {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf index e5a0323ad0..727e4c8388 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + 'System.Uri' のオーバーロードを追加する {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf index 7795b04baf..c20d6fe19c 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + 'System.Uri' 오버로드 추가 {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf index f39df69e34..98d7c65dfb 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Dodaj przeciążenia 'System.Uri' {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf index cbb68cbfc1..234b83ed5c 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Adicionar sobrecarga 'System.Uri'. {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf index 73e7befba0..e75649b54c 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + Добавить перегрузки 'System.Uri' {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf index 559fe03f6a..a2e23d44cc 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + 'System.Uri' aşırı yüklemeleri ekle {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf index 296e4adba7..e267bc998a 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Append .ConfigureAwait(true) @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + 添加 “System.Uri” 过载 {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf index 13b284bf19..69e0bb5805 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Append .ConfigureAwait(true) @@ -269,7 +269,7 @@ Add 'System.Uri' overloads - Add 'System.Uri' overloads + 新增 'System.Uri' 多載 {Locked="System.Uri"} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.OperationVisitor.cs index 32ef050ccd..e0ec80ebdf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.OperationVisitor.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities.Extensions; using Analyzer.Utilities.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis; @@ -25,6 +27,38 @@ public OperationVisitor( _osPlatformType = osPlatformType; } + internal static bool TryParseGuardAttributes(ISymbol symbol, ref GlobalFlowStateAnalysisValueSet value) + { + var attributes = symbol.GetAttributes(); + + if (symbol.GetMemberType()!.SpecialType != SpecialType.System_Boolean || + !HasAnyGuardAttribute(attributes)) + { + return false; + } + + using var infosBuilder = ArrayBuilder.GetInstance(); + if (PlatformMethodValue.TryDecode(attributes, infosBuilder)) + { + for (var i = 0; i < infosBuilder.Count; i++) + { + var newValue = GlobalFlowStateAnalysisValueSet.Create(infosBuilder[i]); + // if the incoming value is negated it should be merged with AND logic, else with OR. + value = i == 0 ? newValue : infosBuilder[i].Negated ? value.WithAdditionalAnalysisValues(newValue, false) : + GlobalFlowStateAnalysis.GlobalFlowStateAnalysisValueSetDomain.Instance.Merge(value, newValue); + } + + return true; + } + + value = GlobalFlowStateAnalysisValueSet.Unknown; + + return false; + + static bool HasAnyGuardAttribute(ImmutableArray attributes) => + attributes.Any(a => a.AttributeClass.Name is SupportedOSPlatformGuardAttribute or UnsupportedOSPlatformGuardAttribute); + } + public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDelegateOrLocalFunction( IMethodSymbol method, IOperation? visitedInstance, @@ -51,9 +85,37 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg return GlobalFlowStateAnalysisValueSet.Unknown; } + else if (TryParseGuardAttributes(method, ref value)) + { + return value; + } return value; } + + public override GlobalFlowStateAnalysisValueSet VisitFieldReference(IFieldReferenceOperation operation, object? argument) + { + var value = base.VisitFieldReference(operation, argument); + + if (TryParseGuardAttributes(operation.Field, ref value)) + { + return value; + } + + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override GlobalFlowStateAnalysisValueSet VisitPropertyReference(IPropertyReferenceOperation operation, object? argument) + { + var value = base.VisitPropertyReference(operation, argument); + + if (TryParseGuardAttributes(operation.Property, ref value)) + { + return value; + } + + return ComputeAnalysisValueForReferenceOperation(operation, value); + } } } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Value.cs index b4a53a7523..313395f1e1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Value.cs @@ -22,21 +22,19 @@ public sealed partial class PlatformCompatibilityAnalyzer { private readonly struct PlatformMethodValue : IAbstractAnalysisValue, IEquatable { - private PlatformMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) + internal PlatformMethodValue(string platformPropertyName, Version version, bool negated) { - InvokedMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName)); PlatformName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName)); Version = version ?? throw new ArgumentNullException(nameof(version)); Negated = negated; } - public string InvokedMethodName { get; } public string PlatformName { get; } public Version Version { get; } public bool Negated { get; } public IAbstractAnalysisValue GetNegatedValue() - => new PlatformMethodValue(InvokedMethodName, PlatformName, Version, !Negated); + => new PlatformMethodValue(PlatformName, Version, !Negated); public static bool TryDecode( IMethodSymbol invokedPlatformCheckMethod, @@ -50,7 +48,7 @@ public static bool TryDecode( { if (TryExtractPlatformName(invokedPlatformCheckMethod.Name, out var platformName)) { - var info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, new Version(0, 0), negated: false); + var info = new PlatformMethodValue(platformName, EmptyVersion, negated: false); infosBuilder.Add(info); return true; } @@ -63,7 +61,7 @@ public static bool TryDecode( Debug.Assert(osPlatformNamesBuilder.Count > 0); for (var i = 0; i < osPlatformNamesBuilder.Count; i++) { - var info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, osPlatformNamesBuilder[i], new Version(0, 0), negated: false); + var info = new PlatformMethodValue(osPlatformNamesBuilder[i], EmptyVersion, negated: false); infosBuilder.Add(info); } @@ -79,7 +77,7 @@ public static bool TryDecode( if (invokedPlatformCheckMethod.Name == IsOSPlatform && TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) { - var info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + var info = new PlatformMethodValue(platformName, version, negated: false); infosBuilder.Add(info); return true; } @@ -87,7 +85,7 @@ public static bool TryDecode( { // OperatingSystem.IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0) Debug.Assert(invokedPlatformCheckMethod.Name == "IsOSPlatformVersionAtLeast"); - var info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); + var info = new PlatformMethodValue(literal.ConstantValue.Value.ToString(), version, negated: false); infosBuilder.Add(info); return true; } @@ -98,7 +96,7 @@ public static bool TryDecode( if (TryExtractPlatformName(invokedPlatformCheckMethod.Name, out var platformName) && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) { - var info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + var info = new PlatformMethodValue(platformName, version, negated: false); infosBuilder.Add(info); return true; } @@ -109,6 +107,20 @@ public static bool TryDecode( return false; } + public static bool TryDecode(ImmutableArray attributes, ArrayBuilder infosBuilder) + { + foreach (var attribute in attributes) + { + if (attribute.AttributeClass.Name is SupportedOSPlatformGuardAttribute or UnsupportedOSPlatformGuardAttribute && + TryParsePlatformNameAndVersion(attribute, out var platformName, out var version)) + { + var info = new PlatformMethodValue(platformName, version, negated: attribute.AttributeClass.Name == UnsupportedOSPlatformGuardAttribute); + infosBuilder.Add(info); + } + } + return infosBuilder.Any(); + } + private static bool TryExtractPlatformName(string methodName, [NotNullWhen(true)] out string? platformName) { if (!methodName.StartsWith(IsPrefix, StringComparison.Ordinal)) @@ -225,7 +237,7 @@ static Version CreateVersion(ArrayBuilder versionBuilder) public override string ToString() { - var result = $"{InvokedMethodName};{PlatformName};{Version}"; + var result = $"{PlatformName};{Version}"; if (Negated) { result = $"!{result}"; @@ -235,10 +247,9 @@ public override string ToString() } public bool Equals(PlatformMethodValue other) - => InvokedMethodName.Equals(other.InvokedMethodName, StringComparison.OrdinalIgnoreCase) && - PlatformName.Equals(other.PlatformName, StringComparison.OrdinalIgnoreCase) && - Version.Equals(other.Version) && - Negated == other.Negated; + => PlatformName.Equals(other.PlatformName, StringComparison.OrdinalIgnoreCase) && + Version.Equals(other.Version) && + Negated == other.Negated; public override bool Equals(object obj) => obj is PlatformMethodValue otherInfo && Equals(otherInfo); @@ -246,7 +257,6 @@ public override bool Equals(object obj) public override int GetHashCode() { return RoslynHashCode.Combine( - InvokedMethodName.GetHashCode(), PlatformName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs index 335b2c5499..45da319de4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs @@ -49,6 +49,8 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer // version of internal attribute type which will cause ambiguity, to avoid that we are comparing the attributes by their name private const string SupportedOSPlatformAttribute = nameof(SupportedOSPlatformAttribute); private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); + private const string UnsupportedOSPlatformGuardAttribute = nameof(UnsupportedOSPlatformGuardAttribute); + private const string SupportedOSPlatformGuardAttribute = nameof(SupportedOSPlatformGuardAttribute); // Platform guard method name, prefix, suffix private const string IsOSPlatform = nameof(IsOSPlatform); @@ -56,7 +58,9 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer private const string OptionalSuffix = "VersionAtLeast"; private const string Net = "net"; private const string macOS = nameof(macOS); + private const string OSX = nameof(OSX); private const string MacSlashOSX = "macOS/OSX"; + private static readonly Version EmptyVersion = new(0, 0); internal static DiagnosticDescriptor OnlySupportedCsReachable = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, @@ -418,9 +422,13 @@ static bool IsKnownValueGuarded( } attribute.UnsupportedFirst = null; } + else if (value.AnalysisValues.Contains(new PlatformMethodValue(info.PlatformName, EmptyVersion, false))) + { + csAttributes = SetCallSiteUnsupportedAttribute(csAttributes, info); + } if (attribute.UnsupportedSecond != null && - info.Version.IsGreaterThanOrEqualTo(attribute.UnsupportedSecond)) + attribute.UnsupportedSecond.IsGreaterThanOrEqualTo(info.Version)) { attribute.UnsupportedSecond = null; } @@ -456,16 +464,12 @@ static bool IsKnownValueGuarded( RemoveUnsupportedWithLessVersion(info.Version, attribute); RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName); } - - if (attribute.SupportedSecond != null && - info.Version.IsGreaterThanOrEqualTo(attribute.SupportedSecond)) + else { - attribute.SupportedSecond = null; - RemoveUnsupportedWithLessVersion(info.Version, attribute); - RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName); + capturedVersions.TryGetValue(info.PlatformName, out var unsupportedVersion); + csAttributes = SetCallSiteSupportedAttribute(csAttributes, info, unsupportedVersion); } - csAttributes = SetAsCallSiteSupportedAttribute(csAttributes, info); RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName); } } @@ -473,7 +477,7 @@ static bool IsKnownValueGuarded( { // it is checking one exact platform, other unsupported should be suppressed RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName); - csAttributes = SetAsCallSiteSupportedAttribute(csAttributes, info); + csAttributes = SetCallSiteSupportedAttribute(csAttributes, info, null); } } } @@ -510,12 +514,11 @@ static bool IsKnownValueGuarded( return true; } - static SmallDictionary SetAsCallSiteSupportedAttribute(SmallDictionary? csAttributes, PlatformMethodValue info) + static SmallDictionary SetCallSiteSupportedAttribute(SmallDictionary? csAttributes, + PlatformMethodValue info, Version? unsupportedVersion) { - if (csAttributes == null) - { - csAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase); - } + csAttributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase); + if (csAttributes.TryGetValue(info.PlatformName, out var attributes)) { if (attributes.SupportedFirst == null) @@ -526,11 +529,36 @@ static SmallDictionary SetAsCallSiteSupportedAttribute(SmallDi { attributes.SupportedSecond = info.Version; } + attributes.UnsupportedFirst = unsupportedVersion; } else { - csAttributes.Add(info.PlatformName, new Versions() { SupportedFirst = info.Version }); + csAttributes.Add(info.PlatformName, new Versions() { SupportedFirst = info.Version, UnsupportedFirst = unsupportedVersion }); } + + return csAttributes; + } + + static SmallDictionary SetCallSiteUnsupportedAttribute(SmallDictionary? csAttributes, PlatformMethodValue info) + { + csAttributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase); + + if (csAttributes.TryGetValue(info.PlatformName, out var attributes)) + { + if (attributes.UnsupportedFirst == null) + { + attributes.UnsupportedFirst = info.Version; + } + else + { + attributes.UnsupportedSecond = info.Version; + } + } + else + { + csAttributes.Add(info.PlatformName, new Versions() { UnsupportedFirst = info.Version }); + } + return csAttributes; } @@ -1342,7 +1370,7 @@ static bool UnsupportedFirstSuppressed(Versions attribute, Versions callSiteAttr callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst.IsGreaterThanOrEqualTo(attribute.SupportedFirst) || SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!); - // As optional if call site supports that platform, their versions should match + // As optianal if call site supports that platform, their versions should match static bool OptionalOsSupportSuppressed(Versions callSiteAttribute, Versions attribute) => (callSiteAttribute.SupportedFirst == null || callSiteAttribute.SupportedFirst.IsGreaterThanOrEqualTo(attribute.SupportedFirst)) && (callSiteAttribute.SupportedSecond == null || callSiteAttribute.SupportedSecond.IsGreaterThanOrEqualTo(attribute.SupportedFirst)); @@ -1409,6 +1437,11 @@ static void MergePlatformAttributes(ImmutableArray immediateAttri SmallDictionary? childAttributes = null; foreach (AttributeData attribute in immediateAttributes) { + if (attribute.AttributeClass.Name is SupportedOSPlatformGuardAttribute or UnsupportedOSPlatformGuardAttribute) + { + parentAttributes = new PlatformAttributes(); + return; + } if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) { TryAddValidAttribute(ref childAttributes, attribute); @@ -1590,19 +1623,9 @@ static Versions NormalizeAttribute(Versions attributes) private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary? attributes, AttributeData attribute) { - if (!attribute.ConstructorArguments.IsEmpty && - attribute.ConstructorArguments[0] is { } argument && - argument.Kind == TypedConstantKind.Primitive && - argument.Type.SpecialType == SpecialType.System_String && - !argument.IsNull && - !argument.Value.Equals(string.Empty) && - TryParsePlatformNameAndVersion(argument.Value.ToString(), out string platformName, out Version? version)) + if (TryParsePlatformNameAndVersion(attribute, out var platformName, out var version)) { attributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase); - if (platformName.Equals("OSX", StringComparison.OrdinalIgnoreCase)) - { - platformName = macOS; - } if (!attributes.TryGetValue(platformName, out var _)) { @@ -1616,6 +1639,34 @@ attribute.ConstructorArguments[0] is { } argument && return false; } + private static bool TryParsePlatformNameAndVersion(AttributeData attribute, out string platformName, [NotNullWhen(true)] out Version? version) + { + if (HasNonEmptyStringArgument(attribute, out var argument)) + { + return TryParsePlatformNameAndVersion(argument, out platformName, out version); + } + + version = null; + platformName = string.Empty; + return false; + } + + private static bool HasNonEmptyStringArgument(AttributeData attribute, [NotNullWhen(true)] out string? stringArgument) + { + if (!attribute.ConstructorArguments.IsEmpty && + attribute.ConstructorArguments[0] is { } argument && + argument.Type.SpecialType == SpecialType.System_String && + !argument.IsNull && + !argument.Value.Equals(string.Empty)) + { + stringArgument = argument.Value.ToString(); + return true; + } + + stringArgument = null; + return false; + } + private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) { version = null; @@ -1626,7 +1677,7 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o { if (i > 0 && Version.TryParse(osString[i..], out Version? parsedVersion)) { - osPlatformName = osString.Substring(0, i); + osPlatformName = GetNameAsMacOsWhenOSX(osString.Substring(0, i)); version = parsedVersion; return true; } @@ -1635,11 +1686,14 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o } } - osPlatformName = osString; - version = new Version(0, 0); + osPlatformName = GetNameAsMacOsWhenOSX(osString); + version = EmptyVersion; return true; } + private static string GetNameAsMacOsWhenOSX(string platformName) => + platformName.Equals(OSX, StringComparison.OrdinalIgnoreCase) ? macOS : platformName; + private static void AddAttribute(string name, Version version, Versions attributes) { if (name == SupportedOSPlatformAttribute) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 2f12139e13..723dba5b18 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1546,6 +1546,28 @@ Replace with 'CancellationToken.ThrowIfCancellationRequested' + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + Use span-based 'string.Concat' + + + Use 'AsSpan' with 'string.Concat' + Use 'ContainsKey' @@ -1601,4 +1623,4 @@ Replace 'WhenAll' call with argument - + \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideStreamMemoryBasedAsyncOverrides.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideStreamMemoryBasedAsyncOverrides.cs new file mode 100644 index 0000000000..5b9ce61dfd --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideStreamMemoryBasedAsyncOverrides.cs @@ -0,0 +1,225 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; + +namespace Microsoft.NetCore.Analyzers.Runtime +{ + /// + /// CA1840: Reports a diagnostic if a class that directly subclasses overrides + /// and/or , + /// and does not override the corrasponding memory-based version. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + public sealed class ProvideStreamMemoryBasedAsyncOverrides : DiagnosticAnalyzer + { + internal const string RuleId = "CA1844"; + + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resx.ProvideStreamMemoryBasedAsyncOverridesTitle), Resx.ResourceManager, typeof(Resx)); + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resx.ProvideStreamMemoryBasedAsyncOverridesMessage), Resx.ResourceManager, typeof(Resx)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(Resx.ProvideStreamMemoryBasedAsyncOverridesDescription), Resx.ResourceManager, typeof(Resx)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.Performance, + RuleLevel.IdeSuggestion, + s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + private const string ReadAsyncName = nameof(System.IO.Stream.ReadAsync); + private const string WriteAsyncName = nameof(System.IO.Stream.WriteAsync); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private static void OnCompilationStart(CompilationStartAnalysisContext context) + { + var compilation = context.Compilation; + + if (!TryGetRequiredSymbols(compilation, out RequiredSymbols symbols)) + return; + + context.RegisterSymbolAction(AnalyzeNamedType, SymbolKind.NamedType); + + return; + + // Local functions. + void AnalyzeNamedType(SymbolAnalysisContext context) + { + var type = (INamedTypeSymbol)context.Symbol; + + // We only report a diagnostic if the type directly subclasses stream. We don't report diagnostics + // if there are any bases in the middle. + + if (!symbols.StreamType.Equals(type.BaseType, SymbolEqualityComparer.Default)) + return; + + // We use 'FirstOrDefault' because there could be multiple overrides for the same method if + // there are compiler errors. + + IMethodSymbol? readAsyncArrayOverride = GetOverridingMethodSymbols(type, symbols.ReadAsyncArrayMethod).FirstOrDefault(); + IMethodSymbol? readAsyncMemoryOverride = GetOverridingMethodSymbols(type, symbols.ReadAsyncMemoryMethod).FirstOrDefault(); + IMethodSymbol? writeAsyncArrayOverride = GetOverridingMethodSymbols(type, symbols.WriteAsyncArrayMethod).FirstOrDefault(); + IMethodSymbol? writeAsyncMemoryOverride = GetOverridingMethodSymbols(type, symbols.WriteAsyncMemoryMethod).FirstOrDefault(); + + // For both ReadAsync and WriteAsync, if the array-based form is overridden and the memory-based + // form is not, we report a diagnostic. We report separate diagnostics for ReadAsync and WriteAsync. + + if (readAsyncArrayOverride is not null && readAsyncMemoryOverride is null) + { + var diagnostic = CreateDiagnostic(type, readAsyncArrayOverride, symbols.ReadAsyncMemoryMethod); + context.ReportDiagnostic(diagnostic); + } + + if (writeAsyncArrayOverride is not null && writeAsyncMemoryOverride is null) + { + var diagnostic = CreateDiagnostic(type, writeAsyncArrayOverride, symbols.WriteAsyncMemoryMethod); + context.ReportDiagnostic(diagnostic); + } + } + + static Diagnostic CreateDiagnostic(INamedTypeSymbol violatingType, IMethodSymbol arrayBasedOverride, IMethodSymbol memoryBasedMethod) + { + RoslynDebug.Assert(arrayBasedOverride.OverriddenMethod is not null); + + // We want to underline the name of the violating type in the class declaration. If the violating type + // is a partial class, we underline all partial declarations. + + return violatingType.CreateDiagnostic( + Rule, + violatingType.Name, + arrayBasedOverride.Name, + memoryBasedMethod.Name); + } + } + + private static bool TryGetRequiredSymbols(Compilation compilation, out RequiredSymbols requiredSymbols) + { + var int32Type = compilation.GetSpecialType(SpecialType.System_Int32); + var byteType = compilation.GetSpecialType(SpecialType.System_Byte); + + if (int32Type is null || byteType is null) + { + requiredSymbols = default; + return false; + } + + var byteArrayType = compilation.CreateArrayTypeSymbol(byteType); + var memoryOfByteType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemMemory1)?.Construct(byteType); + var readOnlyMemoryOfByteType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlyMemory1)?.Construct(byteType); + var streamType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOStream); + var cancellationTokenType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingCancellationToken); + + if (memoryOfByteType is null || readOnlyMemoryOfByteType is null || streamType is null || cancellationTokenType is null) + { + requiredSymbols = default; + return false; + } + + // Even though framework types should never contain compiler errors, we still use 'FirstOrDefault' to avoid having the + // analyzer crash if it runs against the source code for 'System.Private.CoreLib', which could be malformed. + + var readAsyncArrayMethod = GetOverloads(streamType, ReadAsyncName, byteArrayType, int32Type, int32Type, cancellationTokenType).FirstOrDefault(); + var readAsyncMemoryMethod = GetOverloads(streamType, ReadAsyncName, memoryOfByteType, cancellationTokenType).FirstOrDefault(); + var writeAsyncArrayMethod = GetOverloads(streamType, WriteAsyncName, byteArrayType, int32Type, int32Type, cancellationTokenType).FirstOrDefault(); + var writeAsyncMemoryMethod = GetOverloads(streamType, WriteAsyncName, readOnlyMemoryOfByteType, cancellationTokenType).FirstOrDefault(); + + if (readAsyncArrayMethod is null || readAsyncMemoryMethod is null || writeAsyncArrayMethod is null || writeAsyncMemoryMethod is null) + { + requiredSymbols = default; + return false; + } + + requiredSymbols = new RequiredSymbols( + streamType, memoryOfByteType, readOnlyMemoryOfByteType, + readAsyncArrayMethod, readAsyncMemoryMethod, + writeAsyncArrayMethod, writeAsyncMemoryMethod); + return true; + } + + // There could be more than one overload with an exact match if there are compiler errors. + private static ImmutableArray GetOverloads(ITypeSymbol containingType, string methodName, params ITypeSymbol[] argumentTypes) + { + return containingType.GetMembers(methodName) + .OfType() + .WhereAsArray(m => IsMatch(m, argumentTypes)); + + static bool IsMatch(IMethodSymbol method, ITypeSymbol[] argumentTypes) + { + if (method.Parameters.Length != argumentTypes.Length) + return false; + + for (int index = 0; index < argumentTypes.Length; ++index) + { + if (!argumentTypes[index].Equals(method.Parameters[index].Type, SymbolEqualityComparer.Default)) + return false; + } + + return true; + } + } + + /// + /// If overrides on its immediate base class, returns the + /// for the overriding method. Returns null if does not override . + /// + /// The type that may have overridden a method on its immediate base-class. + /// + /// The for the method that overrides , or null if + /// is not overridden. + private static ImmutableArray GetOverridingMethodSymbols(ITypeSymbol derivedType, IMethodSymbol overriddenMethod) + { + RoslynDebug.Assert(derivedType.BaseType.Equals(overriddenMethod.ContainingType, SymbolEqualityComparer.Default)); + + return derivedType.GetMembers(overriddenMethod.Name) + .OfType() + .WhereAsArray(m => m.IsOverride && overriddenMethod.Equals(m.GetOverriddenMember(), SymbolEqualityComparer.Default)); + } + + // We use a struct instead of a record-type to save on allocations. + // There is no trade-off for doing so because this type is never passed by-value. + // We never do equality operations on instances. +#pragma warning disable CA1815 // Override equals and operator equals on value types + private readonly struct RequiredSymbols +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public RequiredSymbols( + ITypeSymbol streamType, ITypeSymbol memoryOfByteType, ITypeSymbol readOnlyMemoryOfByteType, + IMethodSymbol readAsyncArrayMethod, IMethodSymbol readAsyncMemoryMethod, + IMethodSymbol writeAsyncArrayMethod, IMethodSymbol writeAsyncMemoryMethod) + { + StreamType = streamType; + MemoryOfByteType = memoryOfByteType; + ReadOnlyMemoryOfByteType = readOnlyMemoryOfByteType; + ReadAsyncArrayMethod = readAsyncArrayMethod; + ReadAsyncMemoryMethod = readAsyncMemoryMethod; + WriteAsyncArrayMethod = writeAsyncArrayMethod; + WriteAsyncMemoryMethod = writeAsyncMemoryMethod; + } + + public ITypeSymbol StreamType { get; } + public ITypeSymbol MemoryOfByteType { get; } + public ITypeSymbol ReadOnlyMemoryOfByteType { get; } + public IMethodSymbol ReadAsyncArrayMethod { get; } + public IMethodSymbol ReadAsyncMemoryMethod { get; } + public IMethodSymbol WriteAsyncArrayMethod { get; } + public IMethodSymbol WriteAsyncMemoryMethod { get; } + } + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.Fixer.cs new file mode 100644 index 0000000000..5908aa22a6 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.Fixer.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; +using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; +using RequiredSymbols = Microsoft.NetCore.Analyzers.Runtime.UseSpanBasedStringConcat.RequiredSymbols; + +namespace Microsoft.NetCore.Analyzers.Runtime +{ + public abstract class UseSpanBasedStringConcatFixer : CodeFixProvider + { + private protected const string AsSpanName = nameof(MemoryExtensions.AsSpan); + private protected const string AsSpanStartParameterName = "start"; + private protected const string ToStringName = nameof(ToString); + + private protected abstract SyntaxNode ReplaceInvocationMethodName(SyntaxGenerator generator, SyntaxNode invocationSyntax, string newName); + + private protected abstract bool IsSystemNamespaceImported(Project project, IReadOnlyList namespaceImports); + + private protected abstract IOperation WalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand); + + private protected abstract bool IsNamedArgument(IArgumentOperation argumentOperation); + + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UseSpanBasedStringConcat.RuleId); + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var document = context.Document; + var diagnostic = context.Diagnostics.First(); + var cancellationToken = context.CancellationToken; + var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var compilation = model.Compilation; + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + if (!RequiredSymbols.TryGetSymbols(compilation, out var symbols)) + return; + if (root.FindNode(context.Span, getInnermostNodeForTie: true) is not SyntaxNode concatExpressionSyntax) + return; + + // OperatorKind will be BinaryOperatorKind.Concatenate, even when '+' is used instead of '&' in Visual Basic. + if (model.GetOperation(concatExpressionSyntax, cancellationToken) is not IBinaryOperation concatOperation + || concatOperation.OperatorKind is not (BinaryOperatorKind.Add or BinaryOperatorKind.Concatenate)) + { + return; + } + + var operands = UseSpanBasedStringConcat.FlattenBinaryOperation(concatOperation); + + // Bail out if we don't have a long enough span-based string.Concat overload. + if (!symbols.TryGetRoscharConcatMethodWithArity(operands.Length, out IMethodSymbol? roscharConcatMethod)) + return; + + // Bail if none of the operands are a non-conditional substring invocation. This could be the case if the + // only substring invocations in the expression were conditional invocations. + if (!operands.Any(IsAnyNonConditionalSubstringInvocation)) + return; + + var codeAction = CodeAction.Create( + Resx.UseSpanBasedStringConcatCodeFixTitle, + FixConcatOperationChain, + Resx.UseSpanBasedStringConcatCodeFixTitle); + context.RegisterCodeFix(codeAction, diagnostic); + + return; + + // Local functions + + bool IsAnyNonConditionalSubstringInvocation(IOperation operation) + { + var value = WalkDownBuiltInImplicitConversionOnConcatOperand(operation); + return value is IInvocationOperation invocation && symbols.IsAnySubstringMethod(invocation.TargetMethod); + } + + async Task FixConcatOperationChain(CancellationToken cancellationToken) + { + RoslynDebug.Assert(roscharConcatMethod is not null); + + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + SyntaxNode stringTypeNameSyntax = generator.TypeExpressionForStaticMemberAccess(symbols.StringType); + SyntaxNode concatMemberAccessSyntax = generator.MemberAccessExpression(stringTypeNameSyntax, roscharConcatMethod.Name); + + // Save leading and trailing trivia so it can be attached to the outside of the string.Concat invocation node. + var leadingTrivia = operands.First().Syntax.GetLeadingTrivia(); + var trailingTrivia = operands.Last().Syntax.GetTrailingTrivia(); + + var arguments = ImmutableArray.CreateBuilder(operands.Length); + foreach (var operand in operands) + arguments.Add(ConvertOperandToArgument(symbols, generator, operand)); + + // Strip off leading and trailing trivia from first and last operand nodes, respectively, and + // reattach it to the outside of the newly-created string.Concat invocation node. + arguments[0] = arguments[0].WithoutLeadingTrivia(); + arguments[^1] = arguments[^1].WithoutTrailingTrivia(); + SyntaxNode concatMethodInvocationSyntax = generator.InvocationExpression(concatMemberAccessSyntax, arguments.MoveToImmutable()) + .WithLeadingTrivia(leadingTrivia) + .WithTrailingTrivia(trailingTrivia); + + SyntaxNode newRoot = generator.ReplaceNode(root, concatExpressionSyntax, concatMethodInvocationSyntax); + + // Import 'System' namespace if it's absent. + if (!IsSystemNamespaceImported(context.Document.Project, generator.GetNamespaceImports(newRoot))) + { + SyntaxNode systemNamespaceImport = generator.NamespaceImportDeclaration(nameof(System)); + newRoot = generator.AddNamespaceImports(newRoot, systemNamespaceImport); + } + + editor.ReplaceNode(root, newRoot); + return editor.GetChangedDocument(); + } + } + + public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + private SyntaxNode ConvertOperandToArgument(in RequiredSymbols symbols, SyntaxGenerator generator, IOperation operand) + { + var value = WalkDownBuiltInImplicitConversionOnConcatOperand(operand); + + // Convert substring invocations to equivalent AsSpan invocation. + if (value is IInvocationOperation invocation && symbols.IsAnySubstringMethod(invocation.TargetMethod)) + { + SyntaxNode invocationSyntax = invocation.Syntax; + + // Swap out parameter names if named-arguments are used. + if (TryGetNamedStartIndexArgument(symbols, invocation, out var namedStartIndexArgument)) + { + var renamedArgumentSyntax = generator.Argument(AsSpanStartParameterName, RefKind.None, namedStartIndexArgument.Value.Syntax); + invocationSyntax = generator.ReplaceNode(invocationSyntax, namedStartIndexArgument.Syntax, renamedArgumentSyntax); + } + var asSpanInvocationSyntax = ReplaceInvocationMethodName(generator, invocationSyntax, AsSpanName); + return generator.Argument(asSpanInvocationSyntax); + } + // Character literals become string literals. + else if (value.Type.SpecialType == SpecialType.System_Char && value is ILiteralOperation literalOperation && literalOperation.ConstantValue.HasValue) + { + var stringLiteral = generator.LiteralExpression(literalOperation.ConstantValue.Value.ToString()).WithTriviaFrom(literalOperation.Syntax); + return generator.Argument(stringLiteral); + } + else + { + return generator.Argument(value.Syntax); + } + + bool TryGetNamedStartIndexArgument(in RequiredSymbols symbols, IInvocationOperation substringInvocation, [NotNullWhen(true)] out IArgumentOperation? namedStartIndexArgument) + { + RoslynDebug.Assert(symbols.IsAnySubstringMethod(substringInvocation.TargetMethod)); + + foreach (var argument in substringInvocation.Arguments) + { + if (IsNamedArgument(argument) && symbols.IsAnySubstringStartIndexParameter(argument.Parameter)) + { + namedStartIndexArgument = argument; + return true; + } + } + + namedStartIndexArgument = default; + return false; + } + } + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.cs new file mode 100644 index 0000000000..3805009526 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.cs @@ -0,0 +1,311 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; + +namespace Microsoft.NetCore.Analyzers.Runtime +{ + public abstract class UseSpanBasedStringConcat : DiagnosticAnalyzer + { + internal const string RuleId = "CA1845"; + + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resx.UseSpanBasedStringConcatTitle), Resx.ResourceManager, typeof(Resx)); + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resx.UseSpanBasedStringConcatMessage), Resx.ResourceManager, typeof(Resx)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(Resx.UseSpanBasedStringConcatDescription), Resx.ResourceManager, typeof(Resx)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.Performance, + RuleLevel.IdeSuggestion, + s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + /// + /// If the specified binary operation is a string concatenation operation, we try to walk up to the top-most + /// string-concatenation operation that it is part of. If it is not a string-concatenation operation, we simply + /// return false. + /// + private protected abstract bool TryGetTopMostConcatOperation(IBinaryOperation binaryOperation, [NotNullWhen(true)] out IBinaryOperation? rootBinaryOperation); + + /// + /// Remove the built in implicit conversion on operands to concat. + /// In VB, the conversion can be to either string or object. + /// In C#, the conversion is always to object. + /// + private protected abstract IOperation WalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand); + + public sealed override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public sealed override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private void OnCompilationStart(CompilationStartAnalysisContext context) + { + if (!RequiredSymbols.TryGetSymbols(context.Compilation, out RequiredSymbols symbols)) + return; + + context.RegisterOperationBlockStartAction(OnOperationBlockStart); + return; + + // Local functions + void OnOperationBlockStart(OperationBlockStartAnalysisContext context) + { + // Maintain set of all top-most concat operations so we don't report sub-expressions of an + // already-reported violation. + // We also don't report any diagnostic if the concat operation has too many operands for the span-based + // Concat overloads to handle. + var topMostConcatOperations = PooledConcurrentSet.GetInstance(); + + context.RegisterOperationAction(PopulateTopMostConcatOperations, OperationKind.Binary); + context.RegisterOperationBlockEndAction(ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls); + + void PopulateTopMostConcatOperations(OperationAnalysisContext context) + { + // If the current operation is a string-concatenation operation, walk up to the top-most concat + // operation and add it to the set. + var binary = (IBinaryOperation)context.Operation; + if (!TryGetTopMostConcatOperation(binary, out var topMostConcatOperation)) + return; + + topMostConcatOperations.Add(topMostConcatOperation); + } + + void ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls(OperationBlockAnalysisContext context) + { + // We report diagnostics for all top-most concat operations that contain + // direct or conditional substring invocations when there is an applicable span-based overload of + // the string.Concat method. + // We don't report when the concatenation contains anything other than strings or character literals. + foreach (var operation in topMostConcatOperations) + { + if (ShouldBeReported(operation)) + { + context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); + } + } + + topMostConcatOperations.Free(context.CancellationToken); + } + } + + bool ShouldBeReported(IBinaryOperation topMostConcatOperation) + { + var concatOperands = FlattenBinaryOperation(topMostConcatOperation); + + // Bail if no suitable overload of 'string.Concat' exists. + if (!symbols.TryGetRoscharConcatMethodWithArity(concatOperands.Length, out _)) + return false; + + bool anySubstringInvocations = false; + foreach (var operand in concatOperands) + { + var value = WalkDownBuiltInImplicitConversionOnConcatOperand(operand); + switch (value.Type?.SpecialType) + { + // Report diagnostics only when operands are exclusively strings and character literals. + case SpecialType.System_String: + case SpecialType.System_Char when value is ILiteralOperation: + if (IsAnyDirectOrConditionalSubstringInvocation(value)) + anySubstringInvocations = true; + break; + default: + return false; + } + } + + return anySubstringInvocations; + } + + bool IsAnyDirectOrConditionalSubstringInvocation(IOperation operation) + { + if (operation is IConditionalAccessOperation conditionallAccessOperation) + operation = conditionallAccessOperation.WhenNotNull; + + return operation is IInvocationOperation invocation && symbols.IsAnySubstringMethod(invocation.TargetMethod); + } + } + + internal static ImmutableArray FlattenBinaryOperation(IBinaryOperation root) + { + var walker = new BinaryOperandWalker(); + walker.Visit(root); + + return walker.Operands.ToImmutable(); + } + + private sealed class BinaryOperandWalker : OperationWalker + { + private BinaryOperatorKind _operatorKind; + + public BinaryOperandWalker() : base() { } + + public ImmutableArray.Builder Operands { get; } = ImmutableArray.CreateBuilder(); + + public override void DefaultVisit(IOperation operation) + { + Operands.Add(operation); + } + + public override void VisitBinaryOperator(IBinaryOperation operation) + { + if (_operatorKind is BinaryOperatorKind.None) + { + _operatorKind = operation.OperatorKind; + } + else if (_operatorKind != operation.OperatorKind) + { + DefaultVisit(operation); + return; + } + + Visit(operation.LeftOperand); + Visit(operation.RightOperand); + } + } + + internal static IOperation CSharpWalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand) + { + if (operand is not IConversionOperation conversion) + return operand; + if (!conversion.IsImplicit || conversion.Conversion.IsUserDefined) + return conversion; + if (conversion.Type.SpecialType is SpecialType.System_Object) + return conversion.Operand; + + return conversion; + } + + internal static IOperation BasicWalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand) + { + if (operand is not IConversionOperation conversion) + return operand; + if (!conversion.IsImplicit || conversion.Conversion.IsUserDefined) + return conversion; + if (conversion.Type.SpecialType is SpecialType.System_Object or SpecialType.System_String) + return conversion.Operand; + + return conversion; + } + + // Use readonly struct instead of record type to save on allocations, since it's not passed by-value. + // We aren't comparing these. +#pragma warning disable CA1815 // Override equals and operator equals on value types + internal readonly struct RequiredSymbols +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + private RequiredSymbols( + INamedTypeSymbol stringType, INamedTypeSymbol roscharType, + IMethodSymbol substringStart, IMethodSymbol substringStartLength, + IMethodSymbol asSpanStart, IMethodSymbol asSpanStartLength) + { + StringType = stringType; + ReadOnlySpanOfCharType = roscharType; + SubstringStart = substringStart; + SubstringStartLength = substringStartLength; + AsSpanStart = asSpanStart; + AsSpanStartLength = asSpanStartLength; + } + + public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols symbols) + { + var stringType = compilation.GetSpecialType(SpecialType.System_String); + var charType = compilation.GetSpecialType(SpecialType.System_Char); + + if (stringType is null || charType is null) + { + symbols = default; + return false; + } + + var readOnlySpanOfCharType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1)?.Construct(charType); + var memoryExtensionsType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemMemoryExtensions); + + if (readOnlySpanOfCharType is null || memoryExtensionsType is null) + { + symbols = default; + return false; + } + + var intParamInfo = ParameterInfo.GetParameterInfo(compilation.GetSpecialType(SpecialType.System_Int32)); + var stringParamInfo = ParameterInfo.GetParameterInfo(stringType); + + var substringMembers = stringType.GetMembers(nameof(string.Substring)).OfType(); + var substringStart = substringMembers.GetFirstOrDefaultMemberWithParameterInfos(intParamInfo); + var substringStartLength = substringMembers.GetFirstOrDefaultMemberWithParameterInfos(intParamInfo, intParamInfo); + + var asSpanMembers = memoryExtensionsType.GetMembers(nameof(MemoryExtensions.AsSpan)).OfType(); + var asSpanStart = asSpanMembers.GetFirstOrDefaultMemberWithParameterInfos(stringParamInfo, intParamInfo)?.ReduceExtensionMethod(stringType); + var asSpanStartLength = asSpanMembers.GetFirstOrDefaultMemberWithParameterInfos(stringParamInfo, intParamInfo, intParamInfo)?.ReduceExtensionMethod(stringType); + + if (substringStart is null || substringStartLength is null || asSpanStart is null || asSpanStartLength is null) + { + symbols = default; + return false; + } + + symbols = new RequiredSymbols( + stringType, readOnlySpanOfCharType, + substringStart, substringStartLength, + asSpanStart, asSpanStartLength); + return true; + } + + public INamedTypeSymbol StringType { get; } + public INamedTypeSymbol ReadOnlySpanOfCharType { get; } + public IMethodSymbol SubstringStart { get; } + public IMethodSymbol SubstringStartLength { get; } + public IMethodSymbol AsSpanStart { get; } + public IMethodSymbol AsSpanStartLength { get; } + + public IMethodSymbol? GetAsSpanEquivalent(IMethodSymbol? substringMethod) + { + if (SymbolEqualityComparer.Default.Equals(substringMethod, SubstringStart)) + return AsSpanStart; + if (SymbolEqualityComparer.Default.Equals(substringMethod, SubstringStartLength)) + return AsSpanStartLength; + return null; + } + + public bool IsAnySubstringMethod(IMethodSymbol? method) + { + return SymbolEqualityComparer.Default.Equals(method, SubstringStart) || + SymbolEqualityComparer.Default.Equals(method, SubstringStartLength); + } + + public bool IsAnySubstringStartIndexParameter(IParameterSymbol? parameter) + { + return SymbolEqualityComparer.Default.Equals(parameter, SubstringStart.Parameters.First()) || + SymbolEqualityComparer.Default.Equals(parameter, SubstringStartLength.Parameters.First()); + } + + public bool TryGetRoscharConcatMethodWithArity(int arity, [NotNullWhen(true)] out IMethodSymbol? concatMethod) + { + var roscharParamInfo = ParameterInfo.GetParameterInfo(ReadOnlySpanOfCharType); + var argumentList = new ParameterInfo[arity]; + for (int index = 0; index < arity; index++) + argumentList[index] = roscharParamInfo; + + concatMethod = StringType.GetMembers(nameof(string.Concat)) + .OfType() + .GetFirstOrDefaultMemberWithParameterInfos(argumentList); + return concatMethod is not null; + } + } + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Security/Helpers/InsecureDeserializationTypeDecider.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Security/Helpers/InsecureDeserializationTypeDecider.cs index a59c704d36..e6368d1255 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Security/Helpers/InsecureDeserializationTypeDecider.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Security/Helpers/InsecureDeserializationTypeDecider.cs @@ -144,7 +144,7 @@ public bool IsTypeInsecure( ITypeSymbol? Compute(ITypeSymbol typeSymbol) { // Sort type symbols by display string so that we get consistent results. - using PooledSortedSet associatedTypeSymbols = PooledSortedSet.GetInstance( + SortedSet associatedTypeSymbols = new SortedSet( this.SymbolByDisplayStringComparer); GetAssociatedTypes(typeSymbol, associatedTypeSymbols); foreach (ITypeSymbol t in associatedTypeSymbols) @@ -239,8 +239,7 @@ void GetInsecureSymbol( // Sort type symbols by display strings. // Keep track of member types we see, and we'll recurse through those afterwards. - using PooledSortedSet typesToRecurse = PooledSortedSet.GetInstance( - this.SymbolByDisplayStringComparer); + SortedSet typesToRecurse = new SortedSet(this.SymbolByDisplayStringComparer); foreach (ISymbol member in typeSymbol.GetMembers()) { switch (member) @@ -415,7 +414,7 @@ in typeSymbol.GetAttributes(this.XmlSerializationAttributeTypes.XmlIncludeAttrib /// Set to populate with associated types. private static void GetAssociatedTypes( ITypeSymbol type, - PooledSortedSet results) + SortedSet results) { if (type == null || !results.Add(type)) { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf index e743969f74..6af04a2095 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1787,6 +1787,21 @@ Poskytujte metody deserializace pro volitelné pole + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Odebrat redundantní volání @@ -2362,6 +2377,26 @@ Pokud je to možné, zvažte použití řízení přístupu Azure na základě role namísto sdíleného přístupového podpisu (SAS). Pokud i přesto potřebujete používat sdílený přístupový podpis, zadejte SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf index d61d923195..cfaa562463 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1787,6 +1787,21 @@ Deserialisierungsmethoden für optionale Felder angeben + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Redundanten Aufruf entfernen @@ -2362,6 +2377,26 @@ Erwägen Sie (sofern möglich) die Verwendung der rollenbasierten Zugriffssteuerung von Azure anstelle einer Shared Access Signature (SAS). Wenn Sie weiterhin eine SAS benötigen, verwenden Sie "SharedAccessProtocol.HttpsOnly". + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf index 5f1be5b3ba..0018af8f92 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1787,6 +1787,21 @@ Proporcionar métodos de deserialización para campos opcionales + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Quitar llamada redundante @@ -2362,6 +2377,26 @@ Considere la posibilidad de usar el control de acceso basado en rol de Azure en lugar de una firma de acceso compartido (SAS), si es posible. Si tiene que usar una firma de acceso compartido, especifique SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf index a810d54587..f915bf31cd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1787,6 +1787,21 @@ Spécifiez des méthodes de désérialisation pour les champs facultatifs + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Supprimer un appel redondant @@ -2362,6 +2377,26 @@ Si possible, utilisez le contrôle d'accès en fonction du rôle d'Azure à la place d'une signature d'accès partagé. Si vous devez quand même utiliser une signature d'accès partagé, spécifiez SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf index 009e25cf3c..8eb665ee47 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1787,6 +1787,21 @@ Fornire metodi di deserializzazione per i campi facoltativi + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Rimuovi la chiamata ridondante @@ -2362,6 +2377,26 @@ Se possibile, provare a usare il controllo degli accessi in base al ruolo di Azure, invece della firma di accesso condiviso. Se è necessario usare una firma di accesso condiviso, specificare SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf index 678915ec78..8e53387376 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1787,6 +1787,21 @@ 省略可能なフィールドに、逆シリアル化メソッドを指定します + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call 冗長な呼び出しを削除する @@ -2362,6 +2377,26 @@ 可能な場合は、Shared Access Signature (SAS) の代わりに、Azure のロールベースのアクセス制御を使用することを検討してください。依然として SAS を使用する必要がある場合は、SharedAccessProtocol.HttpsOnly を指定します。 + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf index 808e70bf7f..40a9ba2dee 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -434,7 +434,7 @@ When deserializing an instance of class {0}, method {1} can call dangerous method {2}. The potential method invocations are: {3}. - 클래스 {0}의 인스턴스를 역직렬화할 때 메서드 {1}이(가) 위험한 메서드 {2}을(를) 호출할 수 있습니다. 잠재적인 메서드 호출은 {3}입니다. + {0} 클래스의 인스턴스를 역직렬화할 때 {1} 메서드가 위험한 메서드 {2}을(를) 호출할 수 있습니다. 잠재적인 메서드 호출은 {3}입니다 @@ -1787,6 +1787,21 @@ 선택적 필드에 deserialization 메서드를 제공하십시오. + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call 중복 호출 제거 @@ -2362,6 +2377,26 @@ 가능한 경우 SAS(공유 액세스 서명) 대신 Azure의 역할 기반 액세스 제어를 사용하는 것이 좋습니다. 계속 SAS를 사용해야 하는 경우 SharedAccessProtocol.HttpsOnly를 지정하세요. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. @@ -2454,4 +2489,4 @@ - \ No newline at end of file + diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf index 9985c7a67c..592daa7204 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1787,6 +1787,21 @@ Udostępnij metody deserializacji dla pól opcjonalnych + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Usuń nadmiarowe wywołanie @@ -2362,6 +2377,26 @@ Jeśli to możliwe, rozważ użycie kontroli dostępu opartej na rolach platformy Azure zamiast sygnatury dostępu współdzielonego (SAS). Jeśli nadal chcesz używać sygnatury SAS, określ właściwość SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf index 3699fca06f..e285035d7e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf @@ -1787,6 +1787,21 @@ Fornecer métodos de desserialização para campos opcionais + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Remova a chamada redundante @@ -2362,6 +2377,26 @@ Se possível, considere usar o controle de acesso baseado em função do Azure em vez de uma SAS (Assinatura de Acesso Compartilhado). Se você ainda precisar usar uma SAS, especifique SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf index 83581ad4d8..85434b1f50 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1787,6 +1787,21 @@ Обеспечьте наличие методов десериализации в необязательных полях + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Удалить избыточный вызов @@ -2362,6 +2377,26 @@ Если возможно, попробуйте использовать управление доступом на основе ролей Azure, а не подписанный URL-адрес (SAS). Если все-таки требуется использовать SAS, укажите SharedAccessProtocol.HttpsOnly. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. @@ -2454,4 +2489,4 @@ - \ No newline at end of file + diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf index 389f5e0fd8..dc94be6024 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1787,6 +1787,21 @@ İsteğe bağlı yöntemler için serileştirme kaldırma yöntemler sağlayın + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call Gereksiz çağrıyı kaldır @@ -2362,6 +2377,26 @@ Mümkünse Paylaşılan Erişim İmzası (SAS) yerine Azure'un rol tabanlı erişim denetimini kullanmayı düşünün. Yine de SAS kullanmanız gerekiyorsa, SharedAccessProtocol.HttpsOnly belirtin. + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf index 6b3e6f0aba..dc86c9a46a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Add the 'NonSerialized' attribute to this field. @@ -1787,6 +1787,21 @@ 为可选字段提供反序列化方法 + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call 删除冗余的调用 @@ -2362,6 +2377,26 @@ 如果可能,请考虑使用 Azure 基于角色的访问控制,而不是共享访问签名(SAS)。如果仍需使用 SAS,请指定 SharedAccessProtocol.HttpsOnly。 + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf index 0248343e0b..16bfe4a6d0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Add the 'NonSerialized' attribute to this field. @@ -1787,6 +1787,21 @@ 必須為選擇性欄位提供還原序列化方法 + + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + + + + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + '{0}' overrides array-based '{1}' but does not override memory-based '{2}'. Consider overriding memory-based '{2}' to improve performance. + 0 = type that subclasses Stream directly, 1 = array-based method, 2 = memory-based method + + + Provide memory-based overrides of async methods when subclassing 'Stream' + Provide memory-based overrides of async methods when subclassing 'Stream' + + Remove redundant call 移除冗餘的呼叫 @@ -2362,6 +2377,26 @@ 如果可行的話,請考慮從共用存取簽章 (SAS) 改為使用 Azure 的角色型存取控制。如果您仍需要使用 SAS,請指定 SharedAccessProtocol.HttpsOnly。 + + Use 'AsSpan' with 'string.Concat' + Use 'AsSpan' with 'string.Concat' + + + + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + + + + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + Use span-based 'string.Concat' and 'AsSpan' instead of 'Substring' + + + + Use span-based 'string.Concat' + Use span-based 'string.Concat' + + Platform compatibility analyzer requires a valid platform name and version. Platform compatibility analyzer requires a valid platform name and version. diff --git a/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hans.xlf index 06115a6691..f0971825da 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Specify MessageBoxOptions diff --git a/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hant.xlf index 69276662f7..bf1a077098 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetFramework.Analyzers/xlf/MicrosoftNetFrameworkAnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Specify MessageBoxOptions diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 0ae95e39eb..a3222d52e0 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1368,6 +1368,30 @@ Using 'WaitAll' with a single task may result in performance loss, await or retu |CodeFix|True| --- +## [CA1844](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1844): Provide memory-based overrides of async methods when subclassing 'Stream' + +To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods. + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|False| +--- + +## [CA1845](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1845): Use span-based 'string.Concat' + +It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator. + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|True| +--- + ## [CA2000](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2000): Dispose objects before losing scope If a disposable object is not explicitly disposed before all references to it are out of scope, the object will be disposed at some indeterminate time when the garbage collector runs the finalizer of the object. Because an exceptional event might occur that will prevent the finalizer of the object from running, the object should be explicitly disposed instead. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index 04c072862f..b7a3af4bb9 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -227,6 +227,25 @@ ] } }, + "CA1845": { + "id": "CA1845", + "shortDescription": "Use span-based 'string.Concat'", + "fullDescription": "It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator.", + "defaultLevel": "note", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1845", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "CSharpUseSpanBasedStringConcat", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2014": { "id": "CA2014", "shortDescription": "Do not use stackalloc in loops", @@ -2543,6 +2562,26 @@ ] } }, + "CA1844": { + "id": "CA1844", + "shortDescription": "Provide memory-based overrides of async methods when subclassing 'Stream'", + "fullDescription": "To improve performance, override the memory-based async methods when subclassing 'Stream'. Then implement the array-based methods in terms of the memory-based methods.", + "defaultLevel": "note", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1844", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ProvideStreamMemoryBasedAsyncOverrides", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2000": { "id": "CA2000", "shortDescription": "Dispose objects before losing scope", @@ -5364,6 +5403,25 @@ ] } }, + "CA1845": { + "id": "CA1845", + "shortDescription": "Use span-based 'string.Concat'", + "fullDescription": "It is more efficient to use 'AsSpan' and 'string.Concat', instead of 'Substring' and a concatenation operator.", + "defaultLevel": "note", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1845", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "BasicUseSpanBasedStringConcat", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2016": { "id": "CA2016", "shortDescription": "Forward the 'CancellationToken' parameter to methods", diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 35779dc2e0..abf017a075 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,3 +9,5 @@ CA1840 | | Prefer Dictionary.Contains methods | CA1842 | | Do not use 'WhenAll' with a single task | CA1843 | | Do not use 'WaitAll' with a single task | +CA1844 | | Provide memory-based overrides of async methods when subclassing 'Stream' | +CA1845 | | Use span-based 'string.Concat' | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs index c09054b61c..d9cc54016f 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldHaveCorrectSuffixTests.cs @@ -1755,6 +1755,115 @@ End Class" }.RunAsync(); } + [Fact, WorkItem(5035, "https://github.com/dotnet/roslyn-analyzers/issues/5035")] + public async Task CA1710_AllowEmptySuffix2() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System.Collections; +using System.Collections.Generic; + +public class C : IReadOnlyDictionary +{ + public object this[string key] => throw new System.NotImplementedException(); + + public IEnumerable Keys => throw new System.NotImplementedException(); + + public IEnumerable Values => throw new System.NotImplementedException(); + + public int Count => throw new System.NotImplementedException(); + + public bool ContainsKey(string key) + { + throw new System.NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + throw new System.NotImplementedException(); + } + + public bool TryGetValue(string key, out object value) + { + throw new System.NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new System.NotImplementedException(); + } +} +" + }, + AnalyzerConfigFiles = { ("/.editorconfig", @"root = true + +[*] +dotnet_code_quality.CA1710.additional_required_suffixes = T:System.Collections.Generic.IReadOnlyDictionary`2->{} +") }, + } + }.RunAsync(); + } + + [Fact, WorkItem(5035, "https://github.com/dotnet/roslyn-analyzers/issues/5035")] + public async Task CA1710_AllowEmptySuffix3() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System.Collections; +using System.Collections.Generic; + +public class C : IReadOnlyDictionary, ICollection> +{ + public object this[string key] => throw new System.NotImplementedException(); + + public IEnumerable Keys => throw new System.NotImplementedException(); + + public IEnumerable Values => throw new System.NotImplementedException(); + + public int Count => throw new System.NotImplementedException(); + + public bool IsReadOnly => throw new System.NotImplementedException(); + + public void Add(KeyValuePair item) => throw new System.NotImplementedException(); + + public void Clear() => throw new System.NotImplementedException(); + + public bool Contains(KeyValuePair item) => throw new System.NotImplementedException(); + + public bool ContainsKey(string key) => throw new System.NotImplementedException(); + + public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new System.NotImplementedException(); + + public IEnumerator> GetEnumerator() => throw new System.NotImplementedException(); + + public bool Remove(KeyValuePair item) => throw new System.NotImplementedException(); + + public bool TryGetValue(string key, out object value) => throw new System.NotImplementedException(); + + IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); +} +" + }, + AnalyzerConfigFiles = { ("/.editorconfig", @"root = true + +[*] +dotnet_code_quality.CA1710.additional_required_suffixes = T:System.Collections.Generic.IReadOnlyDictionary`2->{} +") }, + ExpectedDiagnostics = { GetCA1710CSharpResultAt(5, 14, "C", "Collection") } + } + }.RunAsync(); + } + [Theory, WorkItem(3065, "https://github.com/dotnet/roslyn-analyzers/issues/3065")] [InlineData("")] [InlineData("dotnet_code_quality.CA1710.exclude_indirect_base_types = false")] @@ -1953,4 +2062,4 @@ private static DiagnosticResult GetCA1710CSharpResultAt(int line, int column, st #pragma warning restore RS0030 // Do not used banned APIs .WithArguments(typeName, suffix); } -} \ No newline at end of file +} diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameOfInPlaceOfStringTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameOfInPlaceOfStringTests.cs index 4a32d17ac7..52798bb562 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameOfInPlaceOfStringTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/UseNameOfInPlaceOfStringTests.cs @@ -17,6 +17,20 @@ public class UseNameofInPlaceOfStringTests { #region Unit tests for no analyzer diagnostic + [Fact] + [WorkItem(3023, "https://github.com/dotnet/roslyn-analyzers/issues/3023")] + public async Task NoDiagnostic_ArgList() + { + await VerifyCS.VerifyAnalyzerAsync(@" +public class C +{ + public void M(__arglist) + { + M(__arglist()); + } +}"); + } + [Fact] public async Task NoDiagnostic_NoArguments() { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs index bcd737c54c..9234da7cbc 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs @@ -3945,6 +3945,454 @@ bool IsWindows11OrLater() await VerifyAnalyzerAsyncCs(source, "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive"); } + [Fact] + public async Task GuardMembersWithSupportedGuardAttributes() + { + var source = @" +using System; +using System.Diagnostics; +using System.Runtime.Versioning; + +class Test +{ + [SupportedOSPlatformGuard(""linux"")] + private bool IsLunuxSupported() => true; + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""Windows"")] + internal bool LinuxAndWindowsSupported { get; set; } + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""macOS"")] + [SupportedOSPlatformGuard(""Windows"")] + private readonly bool _http3Enabled; + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""macOS"")] + [SupportedOSPlatformGuard(""Windows"")] + [SupportedOSPlatformGuard(""Android"")] + private bool IsHttp3PlusAndroidEnabled() => false; + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""macOS"")] + [SupportedOSPlatformGuard(""Windows"")] + [UnsupportedOSPlatformGuard(""Android"")] + private readonly bool _http3EnabledNotAndroid; + + void M1() + { + if (IsLunuxSupported()) // one of the support guarded, so no warning + { + SupportedOnWindowsLinuxOsx(); + SupportedOnLinux(); + } + + if (_http3Enabled) + { + SupportedOnWindowsLinuxOsx(); + [|SupportedOnLinux()|]; // only supported on linux but call site is reachable on 3, linux, windows, macos + } + else + { + [|SupportedOnWindowsLinuxOsx()|]; // This call site is reachable on all platforms. 'Test.SupportedOnWindowsLinuxOsx()' is only supported on: 'macOS/OSX', 'Linux', 'windows'. + {|#0:SupportedOnLinux()|}; // This call site is reachable on all platforms. 'Test.SupportedOnLinux()' is only supported on: 'linux'. + } + + if (IsHttp3PlusAndroidEnabled()) // Android is not in the support list + { + [|SupportedOnWindowsLinuxOsx()|]; // This call site is reachable on: 'Android'. 'Test.M2()' is only supported on: 'windows', 'macOS/OSX', 'Linux'. + } + + if (_http3EnabledNotAndroid) + { + SupportedOnWindowsLinuxOsx(); + [|SupportedOnLinux()|]; + } + + [|SupportedOnWindowsLinuxOsx()|]; + Debug.Assert(LinuxAndWindowsSupported); + SupportedOnWindowsLinuxOsx(); + [|SupportedOnLinux()|]; + } + + [SupportedOSPlatform(""windows"")] + [SupportedOSPlatform(""Linux"")] + [SupportedOSPlatform(""OSX"")] + void SupportedOnWindowsLinuxOsx() { } + + [SupportedOSPlatform(""linux"")] + void SupportedOnLinux() { } +}" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.OnlySupportedCsAllPlatforms).WithLocation(0). + WithArguments("Test.SupportedOnLinux()", "'linux'")); + } + + [Fact] + public async Task GuardMembersWithUnsupportedGuardAttributes() + { + var source = @" +using System; +using System.Diagnostics; +using System.Runtime.Versioning; + +class Test +{ + [UnsupportedOSPlatformGuard(""linux"")] + private readonly bool _linuxNotSupported; + + [UnsupportedOSPlatformGuard(""linux"")] + [UnsupportedOSPlatformGuard(""Windows"")] + public bool LinuxAndWindowsNotSupported { get; } + + [UnsupportedOSPlatformGuard(""linux"")] + [UnsupportedOSPlatformGuard(""ios"")] + [UnsupportedOSPlatformGuard(""Windows"")] + private bool IsLinuxWindowsIosNotSupported() => true; + + [UnsupportedOSPlatformGuard(""linux"")] + [UnsupportedOSPlatformGuard(""ios"")] + [UnsupportedOSPlatformGuard(""Windows"")] + [UnsupportedOSPlatformGuard(""Android"")] + private readonly bool _linuxWindowsIosAndroidNotSupported; + + void M1() + { + if (_linuxNotSupported) + { + UnsupportedOnLinux(); + [|UnsupportedOnLinuxWindowsIos()|]; // This call site is reachable on all platforms. 'Test.UnsupportedOnLinuxWindowsIos()' is unsupported on: 'windows', 'ios'. + } + + if (LinuxAndWindowsNotSupported) + { + UnsupportedOnLinux(); + {|#0:UnsupportedOnLinuxWindowsIos()|}; // This call site is reachable on all platforms. 'Test.UnsupportedOnLinuxWindowsIos()' is unsupported on: 'ios'. + } + + if (_linuxWindowsIosAndroidNotSupported) + { + UnsupportedOnLinux(); + UnsupportedOnLinuxWindowsIos(); + } + else + { + [|UnsupportedOnLinux()|]; // This call site is reachable on: 'Windows', 'linux', 'Android'. 'Test.UnsupportedOnLinux()' is unsupported on: 'Linux'. + [|UnsupportedOnLinuxWindowsIos()|]; // This call site is reachable on: 'Android', 'Windows'. 'Test.UnsupportedOnLinuxWindowsIos()' is unsupported on: 'Linux', 'ios', 'windows'. + } + + Debug.Assert(IsLinuxWindowsIosNotSupported()); + UnsupportedOnLinux(); + UnsupportedOnLinuxWindowsIos(); + } + + + [UnsupportedOSPlatform(""Linux"")] + void UnsupportedOnLinux() { } + + [UnsupportedOSPlatform(""windows"")] + [UnsupportedOSPlatform(""Linux"")] + [UnsupportedOSPlatform(""ios"")] + void UnsupportedOnLinuxWindowsIos() { } +}" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsAllPlatforms).WithLocation(0). + WithArguments("Test.UnsupportedOnLinuxWindowsIos()", "'ios'")); + } + + [Fact] + public async Task GuardMembersWithVersionedSupportedUnsupportedGuardAttributes() + { + var source = @" +using System; +using System.Runtime.Versioning; + +class Test +{ + [SupportedOSPlatformGuard(""Windows10.0"")] + private bool IsWindow10Supported() => true; + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""Windows10.0"")] + [SupportedOSPlatformGuard(""Osx14.1"")] + private readonly bool _linuxAndWindows10MacOS14Supported; + + [UnsupportedOSPlatformGuard(""linux"")] + [UnsupportedOSPlatformGuard(""ios9.0"")] + [UnsupportedOSPlatformGuard(""Windows8.0"")] + private bool LinuxWindows8Ios9NotSupported { get; } + + void M1() + { + if (IsWindow10Supported()) + { + {|#0:UnsupportedOnLinuxWindows10Ios91()|}; // This call site is reachable on: 'Windows' 10.0 and later. 'Test.UnsupportedOnLinuxWindows10Ios91()' is unsupported on: 'windows' 10.0 and later. + SupportedOnWindows10LinuxMacOS14(); + SupportedOnWindows8(); + } + + if (_linuxAndWindows10MacOS14Supported) + { + [|UnsupportedOnLinuxWindows10Ios91()|]; // This call site is reachable on: 'Windows' 10.0 and later. 'Test.UnsupportedOnLinuxWindows10Ios91()' is unsupported on: 'windows' 10.0 and later, 'Linux', 'ios' 9.1 and later. + SupportedOnWindows10LinuxMacOS14(); + [|SupportedOnWindows8()|]; // This call site is reachable on: 'linux'. 'Test.SupportedOnWindows8()' is only supported on: 'windows' 8.0 and later. + } + + if (LinuxWindows8Ios9NotSupported) + { + UnsupportedOnLinuxWindows10Ios91(); + [|SupportedOnWindows10LinuxMacOS14()|]; // This call site is reachable on all platforms. 'Test.SupportedOnWindows10LinuxMacOS14()' is only supported on: 'windows' 10.0 and later, 'Linux', 'macOS' 14.0 and later. + {|#1:SupportedOnWindows8()|}; // This call site is reachable on all platforms. 'Test.SupportedOnWindows8()' is only supported on: 'windows' 8.0 and later. + } + } + + [UnsupportedOSPlatform(""windows10.0"")] + [UnsupportedOSPlatform(""Linux"")] + [UnsupportedOSPlatform(""ios9.1"")] + void UnsupportedOnLinuxWindows10Ios91() { } + + [SupportedOSPlatform(""windows10.0"")] + [SupportedOSPlatform(""Linux"")] + [SupportedOSPlatform(""macOS14.0"")] + void SupportedOnWindows10LinuxMacOS14() { } + + [SupportedOSPlatform(""windows8.0"")] + void SupportedOnWindows8() { } +}" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsReachable).WithLocation(0). + WithArguments("Test.UnsupportedOnLinuxWindows10Ios91()", GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityVersionAndLater, "windows", "10.0"), + GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityVersionAndLater, "Windows", "10.0")), + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.OnlySupportedCsAllPlatforms).WithLocation(1).WithArguments("Test.SupportedOnWindows8()", + GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityVersionAndLater, "windows", "8.0"))); + } + + [Fact] + public async Task GuardMembersWithSupportedUnsupportedVersionRangeGuardAttributes() + { + var source = @" +using System; +using System.Runtime.Versioning; + +class Test +{ + [SupportedOSPlatformGuard(""Windows"")] + [UnsupportedOSPlatformGuard(""Windows10.0"")] + public bool SupportedUntilWindow10 => true; + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""Windows"")] + [UnsupportedOSPlatformGuard(""Windows10.0"")] + private bool IsSupportedUntilWindow10AndLinux() => false; + + [UnsupportedOSPlatformGuard(""ios"")] + [SupportedOSPlatformGuard(""ios14.0"")] + [UnsupportedOSPlatformGuard(""ios18.0"")] + [UnsupportedOSPlatformGuard(""Windows8.0"")] + private readonly bool _windows8IosNotSupportedSupportedIos14_18; + + void M1() + { + if (SupportedUntilWindow10) + { + {|#0:UnsupportedOnWindows8IosSupportsIos14_19()|}; // This call site is reachable on: 'Windows' 10.0 and before. 'Test.UnsupportedOnWindows8IosSupportsIos14_19()' is unsupported on: 'Windows' 8.0 and later. + SupportedOnWindowsUntil10AndLinux(); + SupportedOnWindowsUntil10(); + } + + if (IsSupportedUntilWindow10AndLinux()) + { + [|UnsupportedOnWindows8IosSupportsIos14_19()|]; // This call site is reachable on: 'linux', 'Windows' 10.0 and before. 'Test.UnsupportedOnWindows8IosSupportsIos14_19()' is supported on: 'ios' from version 14.0 to 19.0, 'Windows' 8.0 and later. + SupportedOnWindowsUntil10AndLinux(); + {|#1:SupportedOnWindowsUntil10()|}; // This call site is reachable on: 'linux'. 'Test.SupportedOnWindowsUntil10()' is only supported on: 'Windows'. + } + else + { + [|UnsupportedOnWindows8IosSupportsIos14_19()|]; // This call site is reachable on all platforms. 'Test.UnsupportedOnWindows8IosSupportsIos14_19()' is supported on: 'ios' from version 14.0 to 19.0, 'Windows' 8.0 and later. + [|SupportedOnWindowsUntil10AndLinux()|]; // This call site is reachable on all platforms. 'Test.SupportedOnWindowsUntil10AndLinux()' is only supported on: 'linux', 'Windows' 10.0 and before. + [|SupportedOnWindowsUntil10()|]; // This call site is reachable on all platforms. 'Test.SupportedOnWindowsUntil10()' is only supported on: 'Windows' 10.0 and before. + } + + if (_windows8IosNotSupportedSupportedIos14_18) + { + UnsupportedOnWindows8IosSupportsIos14_19(); + [|SupportedOnWindowsUntil10AndLinux()|]; // This call site is reachable on: 'ios' 14.0 and later. 'Test.SupportedOnWindowsUntil10AndLinux()' is only supported on: 'linux', 'Windows'. + [|SupportedOnWindowsUntil10()|]; // This call site is reachable on: 'ios' 14.0 and later. 'Test.SupportedOnWindowsUntil10()' is only supported on: 'Windows'. + } + } + + [UnsupportedOSPlatform(""ios"")] + [SupportedOSPlatform(""ios14.0"")] + [UnsupportedOSPlatform(""ios19.0"")] + [UnsupportedOSPlatform(""Windows8.0"")] + void UnsupportedOnWindows8IosSupportsIos14_19() { } + + [SupportedOSPlatform(""linux"")] + [SupportedOSPlatform(""Windows"")] + [UnsupportedOSPlatform(""Windows10.0"")] + void SupportedOnWindowsUntil10AndLinux() { } + + [SupportedOSPlatform(""Windows"")] + [UnsupportedOSPlatform(""Windows10.0"")] + void SupportedOnWindowsUntil10() { } +}" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsReachable).WithLocation(0).WithArguments("Test.UnsupportedOnWindows8IosSupportsIos14_19()", + GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityVersionAndLater, "Windows", "8.0"), + GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityVersionAndBefore, "Windows", "10.0")), + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.OnlySupportedCsReachable).WithLocation(1).WithArguments("Test.SupportedOnWindowsUntil10()", "'Windows'", "'linux'")); + } + + [Fact] + public async Task GuardAttributesFalsePositives() + { + var source = @" +using System; +using System.Diagnostics; +using System.Runtime.Versioning; + +class Test +{ + [SupportedOSPlatformGuard(""Windows10.0"")] + private string IsWindow10Supported() => ""true""; + + [SupportedOSPlatformGuard(""linux"")] + [SupportedOSPlatformGuard(""Windows10.0"")] + private bool _linuxAndWindows10Supported; + + [UnsupportedOSPlatformGuard(""linux"")] + [UnsupportedOSPlatformGuard(""ios9.0"")] + private bool LinuxIos9NotSupported { get; set;} + + void M1() + { + if (IsWindow10Supported() == ""true"") // return type string, no effect + { + [|SupportedOnWindows10Linux()|]; + [|SupportedOnWindows8()|]; + } + _linuxAndWindows10Supported = true; // normal statements no effect + IsWindow10Supported(); + [|SupportedOnWindows10Linux()|]; + var value = _linuxAndWindows10Supported; + if (value) + { + IsWindow10Supported(); + [|SupportedOnWindows8()|]; + SupportedOnWindows10Linux(); + } + if (_linuxAndWindows10Supported == true) + { + [|SupportedOnWindows8()|]; + [|SupportedOnWindows10Linux()|]; // 39 + } + var result = LinuxIos9NotSupported == true; // not a guarding a block + [|UnsupportedOnLinuxIos91()|]; + + Debug.Assert(LinuxIos9NotSupported == false); // no effect when there is expression + [|UnsupportedOnLinuxIos91()|]; + + Debug.Assert(LinuxIos9NotSupported); // Assert should work + UnsupportedOnLinuxIos91(); + } + + [UnsupportedOSPlatform(""Linux"")] + [UnsupportedOSPlatform(""ios9.1"")] + void UnsupportedOnLinuxIos91() { } + + [SupportedOSPlatform(""windows10.0"")] + [SupportedOSPlatform(""Linux"")] + void SupportedOnWindows10Linux() { } + + [SupportedOSPlatform(""windows8.0"")] + void SupportedOnWindows8() { } +}" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); + } + + [Fact] + public async Task GuardMemberWithinPlatformSpecificTypeShouldNowWarn() + { + var source = @" +using System; +using System.Runtime.Versioning; + +class Test +{ + void M1() + { + [|UnsupportedBrowserType.M1()|]; + [|UnsupportedBrowserType.M2()|]; + if (UnsupportedBrowserType.IsSupported) + { + UnsupportedBrowserType.M1(); + UnsupportedBrowserType.M2(); + } + var t = [|new UnsupportedBrowserType()|]; + + [|WindowsOnlyType.M1()|]; + [|WindowsOnlyType.M2()|]; + if (WindowsOnlyType.IsSupported) + { + WindowsOnlyType.M1(); + WindowsOnlyType.M2(); + } + var w = [|new WindowsOnlyType()|]; + } +} + +[UnsupportedOSPlatform(""browser"")] +class UnsupportedBrowserType +{ + public static void M1() { } + [UnsupportedOSPlatformGuard(""browser"")] + public static bool IsSupported { get; } + public static void M2() { } +} +[SupportedOSPlatform(""windows"")] +class WindowsOnlyType +{ + public static void M1() { } + [SupportedOSPlatformGuard(""windows"")] + public static bool IsSupported { get; } + public static void M2() { } +} +" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); + } + + private readonly string MockAttributesCsSource = @" +namespace System.Runtime.Versioning +{ + [AttributeUsage(AttributeTargets.Class | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class SupportedOSPlatformGuardAttribute : Attribute + { + public SupportedOSPlatformGuardAttribute(string platformName) { } + } + + [AttributeUsage(AttributeTargets.Class | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class UnsupportedOSPlatformGuardAttribute : Attribute + { + public UnsupportedOSPlatformGuardAttribute(string platformName) { } + } +}"; + private readonly string TargetTypesForTest = @" namespace PlatformCompatDemo.SupportedUnupported { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs index 066d7bc40e..91c434b83b 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs @@ -20,7 +20,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { public partial class PlatformCompatabilityAnalyzerTests { - private const string s_msBuildPlatforms = "build_property._SupportedPlatformList=windows,browser,macOS, ios;\nbuild_property.TargetFramework=net5.0"; + private const string s_msBuildPlatforms = "build_property._SupportedPlatformList=windows,browser,macOS, ios, linux;\nbuild_property.TargetFramework=net5.0"; [Fact(Skip = "TODO need to be fixed: Test for for wrong arguments, not sure how to report the Compiler error diagnostic")] public async Task TestOsPlatformAttributesWithNonStringArgument() @@ -1671,14 +1671,14 @@ public static void TestWithMacOsSupported() [UnsupportedOSPlatform(""MacOS"")] public static void TestWithMacOsLinuxSupported() { - Target.UnsupportedOnOSXAndLinux(); + [|Target.UnsupportedOnOSXAndLinux()|]; // This call site is reachable on all platforms. 'Target.UnsupportedOnOSXAndLinux()' is unsupported on: 'linux'. Target.UnsupportedOnOSX14(); } [UnsupportedOSPlatform(""OSX"")] public static void TestWithOsxSupported() { - Target.UnsupportedOnOSXAndLinux(); + [|Target.UnsupportedOnOSXAndLinux()|]; // This call site is reachable on all platforms. 'Target.UnsupportedOnOSXAndLinux()' is unsupported on: 'linux'. Target.UnsupportedOnOSX14(); } @@ -1691,14 +1691,14 @@ public static void TestWithLinuxSupported() public void CrossPlatform() { - {|#1:Target.UnsupportedOnOSXAndLinux()|}; // This call site is reachable on all platforms. 'Target.UnsupportedOnOSXAndLinux()' is unsupported on: 'macOS/OSX'. + [|Target.UnsupportedOnOSXAndLinux()|]; // This call site is reachable on all platforms. 'Target.UnsupportedOnOSXAndLinux()' is unsupported on: 'macOS/OSX'. [|Target.UnsupportedOnOSX14()|]; // This call site is reachable on all platforms. 'Target.UnsupportedOnOSX14()' is unsupported on: 'macOS/OSX' 14.0 and later. } [UnsupportedOSPlatform(""macOs13.0"")] public void TestWithSupportedOnBrowserWarns() { - {|#2:Target.UnsupportedOnOSXAndLinux()|}; // This call site is reachable on: 'macOS/OSX' 13.0 and before. 'Target.UnsupportedOnOSXAndLinux()' is unsupported on: 'macOS/OSX' all versions. + [|Target.UnsupportedOnOSXAndLinux()|]; // This call site is reachable on: 'macOS/OSX' 13.0 and before. 'Target.UnsupportedOnOSXAndLinux()' is unsupported on: 'macOS/OSX' all versions. Target.UnsupportedOnOSX14(); } } @@ -1711,11 +1711,7 @@ public static void UnsupportedOnOSX14() { } } }"; await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, - VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsReachable).WithLocation(0).WithArguments("Target.UnsupportedOnOSXAndLinux()", "'macOS/OSX'", "'macOS/OSX'"), - VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsAllPlatforms).WithLocation(1).WithArguments("Target.UnsupportedOnOSXAndLinux()", "'macOS/OSX'"), - VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsReachable).WithLocation(2).WithArguments("Target.UnsupportedOnOSXAndLinux()", - GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityAllVersions, "macOS/OSX"), - GetFormattedString(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityVersionAndBefore, "macOS/OSX", "13.0"))); + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.UnsupportedCsReachable).WithLocation(0).WithArguments("Target.UnsupportedOnOSXAndLinux()", "'macOS/OSX'", "'macOS/OSX'")); } [Fact] diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideStreamMemoryAsyncOverridesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideStreamMemoryAsyncOverridesTests.cs new file mode 100644 index 0000000000..3b49e488aa --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideStreamMemoryAsyncOverridesTests.cs @@ -0,0 +1,1476 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; + +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.Analyzers.Runtime.ProvideStreamMemoryBasedAsyncOverrides, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.Analyzers.Runtime.ProvideStreamMemoryBasedAsyncOverrides, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests +{ + public class ProvideStreamMemoryBasedAsyncOverridesTests + { + #region Reports Diagnostic + [Fact] + public Task ReadAsyncArray_NoReadAsyncMemory_ReportsDiagnostic_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class {{|#0:FooStream|}} : Stream + {{ + {CSAbstractMembers} + {CSReadAsyncArray} + }} +}}"; + + var diagnostic = VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("FooStream", CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory); + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { diagnostic } + }; + return test.RunAsync(); + } + + [Fact] + public Task ReadAsyncArray_NoReadAsyncMemory_ReportsDiagnostic_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class {{|#0:FooStream|}} : Inherits Stream + {VBAbstractMembers} + {VBReadAsyncArray} + End Class +End Namespace"; + + var diagnostic = VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("FooStream", VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory); + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { diagnostic } + }; + return test.RunAsync(); + } + + [Fact] + public Task WriteAsyncArray_NoWriteAsyncMemory_ReportsDiagnostic_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class {{|#0:BarStream|}} : Stream + {{ + {CSAbstractMembers} + {CSWriteAsyncArray} + }} +}}"; + + var diagnostic = VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("BarStream", CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory); + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { diagnostic } + }; + return test.RunAsync(); + } + + [Fact] + public Task WriteAsyncArray_NoWriteAsyncMemory_ReportsDiagnostic_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class {{|#0:BarStream|}} : Inherits Stream + {VBAbstractMembers} + {VBWriteAsyncArray} + End Class +End Namespace"; + + var diagnostic = VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("BarStream", VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory); + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { diagnostic } + }; + return test.RunAsync(); + } + + [Fact] + public Task BothArrayOverrides_MissingAllMemoryOverrides_ReportsMultipleBiagnostics_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + {CSReadAsyncArray} + {CSWriteAsyncArray} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory), + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory) + } + }; + return test.RunAsync(); + } + + [Fact] + public Task BothArrayOverrides_MissingAllMemoryOverrides_ReportsMultipleDiagnostics_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + {VBReadAsyncArray} + {VBWriteAsyncArray} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory), + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory) + + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSReadAsyncArray, CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory)] + [InlineData(CSWriteAsyncArray, CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory)] + public Task SingleArrayOverride_MultiplePartialsInSameFile_ReportsAllLocations_CS(string arrayMethod, string displayArrayMethod, string displayMemoryMethod) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public partial class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + }} +}} + +namespace Testopolis +{{ + partial class {{|#1:River|}} + {{ + {arrayMethod} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithArguments("River", displayArrayMethod, displayMemoryMethod), + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBReadAsyncArray, VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory)] + [InlineData(VBWriteAsyncArray, VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory)] + public Task SingleArrayOverride_MultiplePartialsInSameFile_ReportsAllLocations_VB(string arrayMethod, string displayArrayMethod, string displayMemoryMethod) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Partial Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + {arrayMethod} + End Class +End Namespace + +Namespace Testopolis + Partial Class {{|#1:River|}} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithArguments("River", displayArrayMethod, displayMemoryMethod) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSReadAsyncArray, CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory)] + [InlineData(CSWriteAsyncArray, CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory)] + public Task SingleArrayOverride_MultiplePartialsInSeparateFiles_ReportsAllLocations_CS(string arrayMethod, string displayArrayMethod, string displayMemoryMethod) + { + string fooSource = $@" +{CSUsings} +namespace Testopolis +{{ + public partial class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + }} +}}"; + string barSource = $@" +{CSUsings} +namespace Testopolis +{{ + partial class {{|#1:River|}} + {{ + {arrayMethod} + }} +}}"; + string bazSource = $@" +{CSUsings} +namespace Testopolis +{{ + partial class {{|#2:River|}} + {{ + }} +}}"; + var test = new VerifyCS.Test + { + TestState = { Sources = { fooSource, barSource, bazSource } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", displayArrayMethod, displayMemoryMethod) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBReadAsyncArray, VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory)] + [InlineData(VBWriteAsyncArray, VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory)] + public Task SingleArrayOverride_MultiplePartialsInSeparateFiles_ReportsAllLocations_VB(string arrayMethod, string displayArrayMethod, string displayMemoryMethod) + { + string fooSource = $@" +{VBUsings} +Namespace Testopolis + Partial Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + End Class +End Namespace"; + string barSource = $@" +{VBUsings} +Namespace Testopolis + Partial Class {{|#1:River|}} + {arrayMethod} + End Class +End Namespace"; + string bazSource = $@" +{VBUsings} +Namespace Testopolis + Partial Class {{|#2:River|}} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestState = { Sources = { fooSource, barSource, bazSource } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", displayArrayMethod, displayMemoryMethod) + } + }; + return test.RunAsync(); + } + + [Fact] + public Task BothArrayOverrides_MultiplePartialsInSameFile_ReportsAllLocations_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public partial class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + {CSReadAsyncArray} + }} +}} + +namespace Testopolis +{{ + partial class {{|#1:River|}} + {{ + }} +}} + +namespace Testopolis +{{ + partial class {{|#2:River|}} + {{ + {CSWriteAsyncArray} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory), + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory) + } + }; + return test.RunAsync(); + } + + [Fact] + public Task BothArrayOverrides_MultiplePartialsInSameFile_ReportsAllLocations_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Partial Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + {VBReadAsyncArray} + End Class +End Namespace + +Namespace Testopolis + Partial Class {{|#1:River|}} + End Class +End Namespace + +Namespace Testopolis + Partial Class {{|#2:River|}} + {VBWriteAsyncArray} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory), + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory) + } + }; + return test.RunAsync(); + } + + [Fact] + public Task BothArrayOverrides_MultiplePartialsInSeparateFiles_ReportsAllLocations_CS() + { + string fooSource = $@" +{CSUsings} +namespace Testopolis +{{ + public partial class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + {CSReadAsyncArray} + }} +}}"; + string barSource = $@" +{CSUsings} +namespace Testopolis +{{ + partial class {{|#1:River|}} + {{ + }} +}}"; + string bazSource = $@" +{CSUsings} +namespace Testopolis +{{ + partial class {{|#2:River|}} + {{ + {CSWriteAsyncArray} + }} +}}"; + + var test = new VerifyCS.Test + { + TestState = { Sources = { fooSource, barSource, bazSource } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory), + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory) + } + }; + return test.RunAsync(); + } + + [Fact] + public Task BothArrayOverrides_MultiplePartialsInSeparateFiles_ReportsAllLocations_VB() + { + string fooSource = $@" +{VBUsings} +Namespace Testopolis + Partial Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + {VBReadAsyncArray} + End Class +End Namespace"; + string barSource = $@" +{VBUsings} +Namespace Testopolis + Partial Class {{|#1:River|}} + End Class +End Namespace"; + string bazSource = $@" +{VBUsings} +Namespace Testopolis + Partial Class {{|#2:River|}} + {VBWriteAsyncArray} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestState = { Sources = { fooSource, barSource, bazSource } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory), + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithLocation(1) + .WithLocation(2) + .WithArguments("River", VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory) + } + }; + return test.RunAsync(); + } + + // This test has no VB counterpart because in Visual Basic it is illegal to override one overload + // of a base-class method while implicitly hiding another overload. + [Theory] + [InlineData(CSReadAsyncArray, CSHideReadAsyncMemory, CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory)] + [InlineData(CSWriteAsyncArray, CSHideWriteAsyncMemory, CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory)] + public Task WhenMemoryMethodNotDeclaredOverride_ReportsDiagnostic(string arrayMethod, string memoryMethod, string displayArrayMethod, string displayMemoryMethod) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + {arrayMethod} +#pragma warning disable {CSMemberHidesBaseRuleId} + {memoryMethod} +#pragma warning restore {CSMemberHidesBaseRuleId} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", displayArrayMethod, displayMemoryMethod) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSReadAsyncArray, CSHideExplicitReadAsyncMemory, CSDisplayReadAsyncArray, CSDisplayReadAsyncMemory)] + [InlineData(CSWriteAsyncArray, CSHideExplicitWriteAsyncMemory, CSDisplayWriteAsyncArray, CSDisplayWriteAsyncMemory)] + public Task WhenMemoryMethodDeclaredNew_ReportsDiagnostic_CS(string arrayMethod, string memoryMethod, string displayArrayMethod, string displayMemoryMethod) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + {arrayMethod} + {memoryMethod} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", displayArrayMethod, displayMemoryMethod) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBReadAsyncArray, VBHideExplicitReadAsyncMemory, VBDisplayReadAsyncArray, VBDisplayReadAsyncMemory)] + [InlineData(VBWriteAsyncArray, VBHideExplicitWriteAsyncMemory, VBDisplayWriteAsyncArray, VBDisplayWriteAsyncMemory)] + public Task WhenMemoryMethodDeclaredNew_ReportsDiagnostic_VB(string arrayMethod, string memoryMethod, string displayArrayMethod, string displayMemoryMethod) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + {arrayMethod} + {memoryMethod} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(Rule) + .WithLocation(0) + .WithArguments("River", displayArrayMethod, displayMemoryMethod) + } + }; + return test.RunAsync(); + } + #endregion + + #region No Diagnostic + [Fact] + public Task ReadAsyncArray_WithReadAsyncMemory_NoDiagnostic_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class BazStream : Stream + {{ + {CSAbstractMembers} + {CSReadAsyncArray} + {CSReadAsyncMemory} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task ReadAsyncArray_WithReadAsyncMemory_NoDiagnostic_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class BazStream : Inherits Stream + {VBAbstractMembers} + {VBReadAsyncArray} + {VBReadAsyncMemory} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task WriteAsyncArray_WithWriteAsyncMemory_NoDiagnostic_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class WhippleStream : Stream + {{ + {CSAbstractMembers} + {CSWriteAsyncArray} + {CSWriteAsyncMemory} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task WriteAsyncArray_WithWriteAsyncMemory_NoDiagnostic_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class WhippleStream : Inherits Stream + {VBAbstractMembers} + {VBWriteAsyncArray} + {VBWriteAsyncMemory} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task ReadAsyncMemory_WithoutReadAsyncArray_NoDiagnostic_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} + {CSReadAsyncMemory} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task ReadAsyncMemory_WithoutReadAsyncArray_NoDiagnostic_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} + {VBReadAsyncMemory} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task WriteAsyncMemory_WithoutWriteAsyncArray_NoDiagnostic_CS() + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} + {CSWriteAsyncMemory} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task WriteAsyncMemory_WithoutWriteAsyncArray_NoDiagnostic_VB() + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} + {VBWriteAsyncMemory} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSReadAsyncArray)] + [InlineData(CSWriteAsyncArray)] + public Task WhenStreamIsGrandBase_andBaseDoesNotOverrideArrayMethod_NoDiagnostic_CS(string arrayMethodDefinition) + { + string @base = $@" +{CSUsings} +namespace Testopolis +{{ + public class BaseStream : Stream + {{ + {CSAbstractMembers} + }} +}}"; + string derived = $@" +{CSUsings} +namespace Testopolis +{{ + public class DerivedStream : BaseStream + {{ + {arrayMethodDefinition} + }} +}}"; + + var test = new VerifyCS.Test + { + TestState = { Sources = { @base, derived } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBReadAsyncArray)] + [InlineData(VBWriteAsyncArray)] + public Task WhenStreamIsGrandBase_andBaseDoesNotOverrideArrayMethod_NoDiagnostic_VB(string arrayMethodDefinition) + { + string @base = $@" +{VBUsings} +Namespace Testopolis + Public Class BaseStream : Inherits Stream + {VBAbstractMembers} + End Class +End Namespace"; + string derived = $@" +{VBUsings} +Namespace Testopolis + Public Class DerivedStream : Inherits BaseStream + {arrayMethodDefinition} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestState = { Sources = { @base, derived } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSReadAsyncArray)] + [InlineData(CSWriteAsyncArray)] + public Task WhenStreamIsGrandBase_andBaseOverridesArrayMethod_NoDiagnostic_CS(string arrayMethodDefinition) + { + string @base = $@" +{CSUsings} +namespace Testopolis +{{ +#pragma warning disable {RuleId} + public class BaseStream : Stream +#pragma warning restore {RuleId} + {{ + {CSAbstractMembers} + {arrayMethodDefinition} + }} +}}"; + string derived = $@" +{CSUsings} +namespace Testopolis +{{ + public class DerivedStream : BaseStream + {{ + }} +}}"; + + var test = new VerifyCS.Test + { + TestState = { Sources = { @base, derived } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBReadAsyncArray)] + [InlineData(VBWriteAsyncArray)] + public Task WhenStreamIsGrandBase_andBaseOverridesArray_NoDiagnostic_VB(string arrayMethodDefinition) + { + string @base = $@" +{VBUsings} +Namespace Testopolis +#Disable Warning {RuleId} + Public Class BaseStream : Inherits Stream +#Enable Warning {RuleId} + {VBAbstractMembers} + {arrayMethodDefinition} + End Class +End Namespace"; + string derived = $@" +{VBUsings} +Namespace Testopolis + Public Class DerivedStream : Inherits BaseStream + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestState = { Sources = { @base, derived } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSReadAsyncArray)] + [InlineData(CSWriteAsyncArray)] + public Task WhenStreamIsGrandBase_andBothBaseAndDerivedOverrideArrayMethod_NoDiagnostic_CS(string arrayMethodDefinition) + { + string @base = $@" +{CSUsings} +namespace Testopolis +{{ +#pragma warning disable {RuleId} + public class BaseStream : Stream +#pragma warning restore {RuleId} + {{ + {CSAbstractMembers} + {arrayMethodDefinition} + }} +}}"; + string derived = $@" +{CSUsings} +namespace Testopolis +{{ + public class DerivedStream : BaseStream + {{ + {arrayMethodDefinition} + }} +}}"; + + var test = new VerifyCS.Test + { + TestState = { Sources = { @base, derived } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBReadAsyncArray)] + [InlineData(VBWriteAsyncArray)] + public Task WhenStreamIsGrandBase_andBothBaseAndDerivedOverrideArrayMethod_NoDiagnostic_VB(string arrayMethodDefinition) + { + string @base = $@" +{VBUsings} +Namespace Testopolis +#Disable Warning {RuleId} + Public Class BaseStream : Inherits Stream +#Enable Warning {RuleId} + {VBAbstractMembers} + {arrayMethodDefinition} + End Class +End Namespace"; + string derived = $@" +{VBUsings} +Namespace Testopolis + Public Class DerivedStream : Inherits BaseStream + {arrayMethodDefinition} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestState = { Sources = { @base, derived } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSHideReadAsyncArray)] + [InlineData(CSHideWriteAsyncArray)] + public Task WhenArrayMethodNotDeclaredOverride_NoDiagnostic_CS(string arrayMethod) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} +#pragma warning disable {CSMemberHidesBaseRuleId} + {arrayMethod} +#pragma warning restore {CSMemberHidesBaseRuleId} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBHideReadAsyncArray)] + [InlineData(VBHideWriteAsyncArray)] + public Task WhenArrayMethodNotDeclaredOverride_NoDiagnostic_VB(string arrayMethod) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} +#Disable Warning {VBMemberHidesBaseRuleId} + {arrayMethod} +#Enable Warning {VBMemberHidesBaseRuleId} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(CSHideExplicitReadAsyncArray)] + [InlineData(CSHideExplicitWriteAsyncArray)] + public Task WhenArrayMethodDeclaredNew_NoDiagnostic_CS(string arrayMethod) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} + {arrayMethod} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(VBHideExplicitReadAsyncArray)] + [InlineData(VBHideExplicitWriteAsyncArray)] + public Task WhenArrayMethodDeclaredNew_NoDiagnostic_VB(string arrayMethod) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} + {arrayMethod} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + #endregion + + #region Does Not Crash On Illegal Code + [Theory] + [InlineData(ReadAsyncName, CSReadAsyncArray)] + [InlineData(WriteAsyncName, CSWriteAsyncArray)] + public Task DuplicateArrayOverrides_WithoutMemoryOverride_ReportsDiagnosticWithoutCrashing_CS(string methodName, string methodDefinition) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class {{|#0:River|}} : Stream + {{ + {CSAbstractMembers} + {methodDefinition} + {methodDefinition.Replace(methodName, $"{{|#1:{methodName}|}}")} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(RuleId, DiagnosticSeverity.Info) + .WithLocation(0), + new DiagnosticResult(CSMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(1) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, VBReadAsyncArray)] + [InlineData(WriteAsyncName, VBWriteAsyncArray)] + public Task DuplicateArrayOverrides_WithoutMemoryOverride_ReportsDiagnosticWithoutCrashing_VB(string methodName, string methodDefinition) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class {{|#0:River|}} : Inherits Stream + {VBAbstractMembers} + {SurroundWithMarkup(methodDefinition, methodName, 1)} + {methodDefinition} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(RuleId, DiagnosticSeverity.Info) + .WithLocation(0), + new DiagnosticResult(VBMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(1) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, CSReadAsyncArray, CSReadAsyncMemory)] + [InlineData(WriteAsyncName, CSWriteAsyncArray, CSWriteAsyncMemory)] + public Task DuplicateArrayOverrides_WithMemoryOverride_NoDiagnostic_NoCrash_CS(string methodName, string arrayMethodDefinition, string memoryMethodDefinition) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} + {arrayMethodDefinition} + {SurroundWithMarkup(arrayMethodDefinition, methodName, 0)} + {memoryMethodDefinition} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(CSMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(0) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, VBReadAsyncArray, VBReadAsyncMemory)] + [InlineData(WriteAsyncName, VBWriteAsyncArray, VBWriteAsyncMemory)] + public Task DuplicateArrayOverrides_WithMemoryOverride_NoDiagnostic_NoCrash_VB(string methodName, string arrayMethodDefinition, string memoryMethodDefinition) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} + {SurroundWithMarkup(arrayMethodDefinition, methodName, 0)} + {arrayMethodDefinition} + {memoryMethodDefinition} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(VBMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(0) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, CSReadAsyncArray, CSReadAsyncMemory)] + [InlineData(WriteAsyncName, CSWriteAsyncArray, CSWriteAsyncMemory)] + public Task DuplicateMemoryOverrides_WithArrayOverride_NoDiagnostic_NoCrash_CS(string methodName, string arrayMethodDefinition, string memoryMethodDefinition) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} + {arrayMethodDefinition} + {memoryMethodDefinition} + {SurroundWithMarkup(memoryMethodDefinition, methodName, 0)} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(CSMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(0) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, VBReadAsyncArray, VBReadAsyncMemory)] + [InlineData(WriteAsyncName, VBWriteAsyncArray, VBWriteAsyncMemory)] + public Task DuplicateMemoryOverrides_WithArrayOverride_NoDiagnostic_NoCrash_VB(string methodName, string arrayMethodDefinition, string memoryMethodDefinition) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} + {arrayMethodDefinition} + {SurroundWithMarkup(memoryMethodDefinition, methodName, 0)} + {memoryMethodDefinition} + + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(VBMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(0) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, CSReadAsyncMemory)] + [InlineData(WriteAsyncName, CSWriteAsyncMemory)] + public Task DuplicateMemoryOverrides_NoArrayOverride_NoDiagnostic_NoCrash_CS(string methodName, string memoryMethodDefinition) + { + string code = $@" +{CSUsings} +namespace Testopolis +{{ + public class River : Stream + {{ + {CSAbstractMembers} + {memoryMethodDefinition} + {SurroundWithMarkup(memoryMethodDefinition, methodName, 0)} + }} +}}"; + + var test = new VerifyCS.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(CSMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(0) + } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(ReadAsyncName, VBReadAsyncMemory)] + [InlineData(WriteAsyncName, VBWriteAsyncMemory)] + public Task DuplicateMemoryOverrides_NoArrayOverride_NoDiagnostic_NoCrash_VB(string methodName, string memoryMethodDefinition) + { + string code = $@" +{VBUsings} +Namespace Testopolis + Public Class River : Inherits Stream + {VBAbstractMembers} + {SurroundWithMarkup(memoryMethodDefinition, methodName, 0)} + {memoryMethodDefinition} + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestCode = code, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = + { + new DiagnosticResult(VBMemberHasMultipleDefinitionsRuleId, DiagnosticSeverity.Error) + .WithLocation(0) + } + }; + return test.RunAsync(); + } + #endregion + + #region Helpers + private const string ReadAsyncName = nameof(System.IO.Stream.ReadAsync); + private const string WriteAsyncName = nameof(System.IO.Stream.WriteAsync); + + private const string CSUsings = @"using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks;"; + private const string CSAbstractMembers = @"public override void Flush() => throw null; + public override int Read(byte[] buffer, int offset, int count) => throw null; + public override long Seek(long offset, SeekOrigin origin) => throw null; + public override void SetLength(long value) => throw null; + public override void Write(byte[] buffer, int offset, int count) => throw null; + public override bool CanRead { get; } + public override bool CanSeek { get; } + public override bool CanWrite { get; } + public override long Length { get; } + public override long Position { get; set; }"; + private const string CSReadAsyncArray = @"public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw null;"; + private const string CSReadAsyncMemory = @"public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => throw null;"; + private const string CSWriteAsyncArray = @"public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw null;"; + private const string CSWriteAsyncMemory = @"public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw null;"; + + private const string VBUsings = @"Imports System +Imports System.IO +Imports System.Threading +Imports System.Threading.Tasks"; + private const string VBAbstractMembers = @"Public Overrides ReadOnly Property CanRead As Boolean + Get + Throw New NotImplementedException() + End Get + End Property + Public Overrides ReadOnly Property CanSeek As Boolean + Get + Throw New NotImplementedException() + End Get + End Property + Public Overrides ReadOnly Property CanWrite As Boolean + Get + Throw New NotImplementedException() + End Get + End Property + Public Overrides ReadOnly Property Length As Long + Get + Throw New NotImplementedException() + End Get + End Property + Public Overrides Property Position As Long + Get + Throw New NotImplementedException() + End Get + Set(value As Long) + Throw New NotImplementedException() + End Set + End Property + Public Overrides Sub Flush() + Throw New NotImplementedException() + End Sub + Public Overrides Sub SetLength(value As Long) + Throw New NotImplementedException() + End Sub + Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer) + Throw New NotImplementedException() + End Sub + Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer + Throw New NotImplementedException() + End Function + Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long + Throw New NotImplementedException() + End Function"; + private const string VBReadAsyncArray = @"Public Overrides Function ReadAsync(buffer() As Byte, offset As Integer, count As Integer, ct As CancellationToken) As Task(Of Integer) + Throw New NotImplementedException() + End Function"; + private const string VBReadAsyncMemory = @"Public Overrides Function ReadAsync(buffer As Memory(Of Byte), Optional ct As CancellationToken = Nothing) As ValueTask(Of Integer) + Throw New NotImplementedException() + End Function"; + private const string VBWriteAsyncArray = @"Public Overrides Function WriteAsync(buffer() As Byte, offset As Integer, count As Integer, ct As CancellationToken) As Task + Throw New NotImplementedException() + End Function"; + private const string VBWriteAsyncMemory = @"Public Overrides Function WriteAsync(buffer As ReadOnlyMemory(Of Byte), Optional ct As CancellationToken = Nothing) As ValueTask + Throw New NotImplementedException() + End Function"; + + private const string CSDisplayReadAsyncArray = @"ReadAsync"; + private const string CSDisplayReadAsyncMemory = @"ReadAsync"; + private const string CSDisplayWriteAsyncArray = @"WriteAsync"; + private const string CSDisplayWriteAsyncMemory = @"WriteAsync"; + + private const string VBDisplayReadAsyncArray = @"ReadAsync"; + private const string VBDisplayReadAsyncMemory = @"ReadAsync"; + private const string VBDisplayWriteAsyncArray = @"WriteAsync"; + private const string VBDisplayWriteAsyncMemory = @"WriteAsync"; + + private const string CSHideReadAsyncArray = @"public Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw null;"; + private const string CSHideReadAsyncMemory = @"public ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => throw null;"; + private const string CSHideWriteAsyncArray = @"public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw null;"; + private const string CSHideWriteAsyncMemory = @"public ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw null;"; + + private const string VBHideReadAsyncArray = @"Public Function ReadAsync(buffer() As Byte, offset As Integer, count As Integer, cancellationToken As CancellationToken) As Task(Of Integer) + Throw New NotImplementedException() + End Function"; + private const string VBHideWriteAsyncArray = @"Public Function WriteAsync(buffer As Byte, offset As Integer, count As Integer, cancellationToken As CancellationToken) As Task + Throw New NotImplementedException() + End Function"; + + private const string CSHideExplicitReadAsyncArray = @"public new Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw null;"; + private const string CSHideExplicitReadAsyncMemory = @"public new ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => throw null;"; + private const string CSHideExplicitWriteAsyncArray = @"public new Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw null;"; + private const string CSHideExplicitWriteAsyncMemory = @"public new ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw null;"; + + private const string VBHideExplicitReadAsyncArray = @"Public Overloads Function ReadAsync(buffer() As Byte, offset As Integer, count As Integer, cancellationToken As CancellationToken) As Task(Of Integer) + Throw New NotImplementedException() + End Function"; + private const string VBHideExplicitReadAsyncMemory = @"Public Overloads Function ReadAsync(buffer As Memory(Of Byte), Optional cancellationToken As CancellationToken = Nothing) As ValueTask(Of Integer) + Throw New NotImplementedException() + End Function"; + private const string VBHideExplicitWriteAsyncArray = @"Public Overloads Function WriteAsync(buffer As Byte, offset As Integer, count As Integer, cancellationToken As CancellationToken) As Task + Throw New NotImplementedException() + End Function"; + private const string VBHideExplicitWriteAsyncMemory = @"Public Overloads Function WriteAsync(buffer As ReadOnlyMemory(Of Byte), Optional cancellationToken As CancellationToken = Nothing) As ValueTask + Throw New NotImplementedException() + End Function"; + + private static DiagnosticDescriptor Rule => ProvideStreamMemoryBasedAsyncOverrides.Rule; + private static string RuleId => ProvideStreamMemoryBasedAsyncOverrides.RuleId; + private const string CSMemberHidesBaseRuleId = "CS0114"; + private const string CSMemberHasMultipleDefinitionsRuleId = "CS0111"; + + private const string VBMemberHidesBaseRuleId = "BC40005"; + private const string VBMemberHasMultipleDefinitionsRuleId = "BC30269"; + + private static string SurroundWithMarkup(string source, string substring, int markupKey) + { + return source.Replace(substring, $"{{|#{markupKey}:{substring}|}}"); + } + #endregion + } +} diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcatTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcatTests.cs new file mode 100644 index 0000000000..feacc384a5 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcatTests.cs @@ -0,0 +1,833 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic; +using Xunit; + +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.CSharp.Analyzers.Runtime.CSharpUseSpanBasedStringConcat, + Microsoft.NetCore.CSharp.Analyzers.Runtime.CSharpUseSpanBasedStringConcatFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.VisualBasic.Analyzers.Runtime.BasicUseSpanBasedStringConcat, + Microsoft.NetCore.VisualBasic.Analyzers.Runtime.BasicUseSpanBasedStringConcatFixer>; + +namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests +{ + public class UseSpanBasedStringConcatTests + { + #region Reports Diagnostic + public static IEnumerable Data_SingleViolationInOneBlock_CS + { + get + { + yield return new[] + { + @"var _ = {|#0:foo.Substring(1) + bar|};", + @"var _ = string.Concat(foo.AsSpan(1), bar);" + }; + yield return new[] + { + @"var _ = {|#0:foo.Substring(1, 2) + bar|};", + @"var _ = string.Concat(foo.AsSpan(1, 2), bar);" + }; + yield return new[] + { + @"var _ = {|#0:foo + bar.Substring(1)|};", + @"var _ = string.Concat(foo, bar.AsSpan(1));" + }; + yield return new[] + { + @"var _ = {|#0:foo + bar.Substring(1) + baz|};", + @"var _ = string.Concat(foo, bar.AsSpan(1), baz);" + }; + yield return new[] + { + @"var _ = {|#0:foo.Substring(1) + bar.Substring(1) + baz.Substring(1)|};", + @"var _ = string.Concat(foo.AsSpan(1), bar.AsSpan(1), baz.AsSpan(1));" + }; + yield return new[] + { + @"var _ = {|#0:foo.Substring(1, 2) + bar + baz + baz.Substring(1, 2)|};", + @"var _ = string.Concat(foo.AsSpan(1, 2), bar, baz, baz.AsSpan(1, 2));" + }; + yield return new[] + { + @"Consume({|#0:foo + bar.Substring(1, 2)|});", + @"Consume(string.Concat(foo, bar.AsSpan(1, 2)));" + }; + yield return new[] + { + @"var _ = Fwd({|#0:foo.Substring(1) + bar|});", + @"var _ = Fwd(string.Concat(foo.AsSpan(1), bar));" + }; + yield return new[] + { + @"var _ = Fwd({|#0:foo.Substring(1) + bar.Substring(1)|});", + @"var _ = Fwd(string.Concat(foo.AsSpan(1), bar.AsSpan(1)));" + }; + } + } + + [Theory] + [MemberData(nameof(Data_SingleViolationInOneBlock_CS))] + public Task SingleViolationInOneBlock_ReportedAndFixed_CS(string testStatements, string fixedStatements) + { + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(testStatements), + FixedCode = CSUsings + CSWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyCS.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + public static IEnumerable Data_SingleViolationInOneBlock_VB + { + get + { + yield return new[] + { + @"Dim s = {|#0:foo.Substring(1) & bar|}", + @"Dim s = String.Concat(foo.AsSpan(1), bar)" + }; + yield return new[] + { + @"Dim s = {|#0:foo.Substring(1, 2) & bar|}", + @"Dim s = String.Concat(foo.AsSpan(1, 2), bar)" + }; + yield return new[] + { + @"Dim s = {|#0:foo & bar.Substring(1)|}", + @"Dim s = String.Concat(foo, bar.AsSpan(1))" + }; + yield return new[] + { + @"Dim s = {|#0:foo & bar.Substring(1) & baz|}", + @"Dim s = String.Concat(foo, bar.AsSpan(1), baz)" + }; + yield return new[] + { + @"Dim s = {|#0:foo.Substring(1) & bar.Substring(1) & baz.Substring(1)|}", + @"Dim s = String.Concat(foo.AsSpan(1), bar.AsSpan(1), baz.AsSpan(1))" + }; + yield return new[] + { + @"Dim s = {|#0:foo.Substring(1, 2) & bar & baz & baz.Substring(1, 2)|}", + @"Dim s = String.Concat(foo.AsSpan(1, 2), bar, baz, baz.AsSpan(1, 2))" + }; + yield return new[] + { + @"Consume({|#0:foo & bar.Substring(1, 2)|})", + @"Consume(String.Concat(foo, bar.AsSpan(1, 2)))" + }; + yield return new[] + { + @"Dim s = Fwd({|#0:foo.Substring(1) & bar|})", + @"Dim s = Fwd(String.Concat(foo.AsSpan(1), bar))" + }; + yield return new[] + { + @"Dim s = Fwd({|#0:foo.Substring(1) & bar.Substring(1)|})", + @"Dim s = Fwd(String.Concat(foo.AsSpan(1), bar.AsSpan(1)))" + }; + } + } + + [Theory] + [MemberData(nameof(Data_SingleViolationInOneBlock_VB))] + public Task SingleViolationInOneBlock_ReportedAndFixed_VB(string testStatement, string fixedStatement) + { + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(testStatement), + FixedCode = VBUsings + VBWithBody(fixedStatement), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + public static IEnumerable Data_MultipleViolationsInOneBlock_CS + { + get + { + yield return new object[] + { + @" +string alpha = {|#0:foo.Substring(1, 2) + bar.Substring(1) + baz|}; +string bravo = {|#1:foo + bar.Substring(3) + baz.Substring(1, 2)|}; +string charlie = {|#2:foo + bar + baz.Substring(1, 2)|}; +string delta = {|#3:foo.Substring(1) + bar.Substring(1) + baz.Substring(1, 2) + foo.Substring(1, 2)|};", + @" +string alpha = string.Concat(foo.AsSpan(1, 2), bar.AsSpan(1), baz); +string bravo = string.Concat(foo, bar.AsSpan(3), baz.AsSpan(1, 2)); +string charlie = string.Concat(foo, bar, baz.AsSpan(1, 2)); +string delta = string.Concat(foo.AsSpan(1), bar.AsSpan(1), baz.AsSpan(1, 2), foo.AsSpan(1, 2));", + new[] { 0, 1, 2, 3 } + }; + yield return new object[] + { + @"Consume({|#0:foo.Substring(1) + bar|}, {|#1:foo + bar.Substring(1)|});", + @"Consume(string.Concat(foo.AsSpan(1), bar), string.Concat(foo, bar.AsSpan(1)));", + new[] { 0, 1 } + }; + yield return new object[] + { + @"Consume(Fwd({|#0:foo.Substring(1) + bar|}), Fwd({|#1:foo + bar.Substring(1)|}));", + @"Consume(Fwd(string.Concat(foo.AsSpan(1), bar)), Fwd(string.Concat(foo, bar.AsSpan(1))));", + new[] { 0, 1 } + }; + } + } + + [Theory] + [MemberData(nameof(Data_MultipleViolationsInOneBlock_CS))] + public Task MultipleViolationsInOneBlock_AreReportedAndFixed_CS(string testStatements, string fixedStatements, int[] locations) + { + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(testStatements), + FixedCode = CSUsings + CSWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + test.ExpectedDiagnostics.AddRange(locations.Select(x => VerifyCS.Diagnostic(Rule).WithLocation(x))); + return test.RunAsync(); + } + + public static IEnumerable Data_MultipleViolationsInOneBlock_VB + { + get + { + yield return new object[] + { + @" +Dim alpha = {|#0:foo.Substring(1, 2) & bar.Substring(1) & baz|} +Dim bravo = {|#1:foo & bar.Substring(3) & baz.Substring(1, 2)|} +Dim charlie = {|#2:foo & bar & baz.Substring(1, 2)|} +Dim delta = {|#3:foo.Substring(1) & bar.Substring(1) & baz.Substring(1, 2) & foo.Substring(1, 2)|}", + @" +Dim alpha = String.Concat(foo.AsSpan(1, 2), bar.AsSpan(1), baz) +Dim bravo = String.Concat(foo, bar.AsSpan(3), baz.AsSpan(1, 2)) +Dim charlie = String.Concat(foo, bar, baz.AsSpan(1, 2)) +Dim delta = String.Concat(foo.AsSpan(1), bar.AsSpan(1), baz.AsSpan(1, 2), foo.AsSpan(1, 2))", + new[] { 0, 1, 2, 3 } + }; + yield return new object[] + { + @"Consume({|#0:foo.Substring(1) & bar|}, {|#1:foo & bar.Substring(1)|})", + @"Consume(String.Concat(foo.AsSpan(1), bar), String.Concat(foo, bar.AsSpan(1)))", + new[] { 0, 1 } + }; + yield return new object[] + { + @"Consume(Fwd({|#0:foo.Substring(1) & bar|}), Fwd({|#1:foo & bar.Substring(1)|}))", + @"Consume(Fwd(String.Concat(foo.AsSpan(1), bar)), Fwd(String.Concat(foo, bar.AsSpan(1))))", + new[] { 0, 1 } + }; + } + } + + [Theory] + [MemberData(nameof(Data_MultipleViolationsInOneBlock_VB))] + public Task MultipleViolationsInOneBlock_AreReportedAndFixed_VB(string testStatements, string fixedStatements, int[] locations) + { + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(testStatements), + FixedCode = VBUsings + VBWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + }; + test.ExpectedDiagnostics.AddRange(locations.Select(x => VerifyVB.Diagnostic(Rule).WithLocation(x))); + return test.RunAsync(); + } + + public static IEnumerable Data_NestedViolations_CS + { + get + { + yield return new object[] + { + @" +Consume({|#0:Fwd({|#1:foo + bar.Substring(1)|}) + baz.Substring(1)|});", + @" +Consume(string.Concat(Fwd(string.Concat(foo, bar.AsSpan(1))), baz.AsSpan(1)));", + new[] { 0, 1 }, + 2, 2, 2 + }; + yield return new object[] + { + @" +var _ = {|#0:Fwd({|#1:foo.Substring(1) + bar.Substring(1)|}) + Fwd({|#2:foo.Substring(1) + bar|}).Substring(1) + Fwd({|#3:foo + bar.Substring(1)|})|};", + @" +var _ = string.Concat(Fwd(string.Concat(foo.AsSpan(1), bar.AsSpan(1))), Fwd(string.Concat(foo.AsSpan(1), bar)).AsSpan(1), Fwd(string.Concat(foo, bar.AsSpan(1))));", + new[] { 0, 1, 2, 3 }, + 4, 3, 3 + }; + } + } + + [Theory] + [MemberData(nameof(Data_NestedViolations_CS))] + public Task NestedViolations_AreReportedAndFixed_CS( + string testStatements, string fixedStatements, int[] locations, + int? incrementalIterations, int? fixAllInDocumentIterations, int? fixAllIterations) + { + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(testStatements), + FixedCode = CSUsings + CSWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + NumberOfIncrementalIterations = incrementalIterations, + NumberOfFixAllInDocumentIterations = fixAllInDocumentIterations, + NumberOfFixAllIterations = fixAllIterations + }; + test.ExpectedDiagnostics.AddRange(locations.Select(x => VerifyCS.Diagnostic(Rule).WithLocation(x))); + return test.RunAsync(); + } + + public static IEnumerable Data_NestedViolations_VB + { + get + { + yield return new object[] + { + @" +Consume({|#0:Fwd({|#1:foo + bar.Substring(1)|}) + baz.Substring(1)|})", + @" +Consume(String.Concat(Fwd(String.Concat(foo, bar.AsSpan(1))), baz.AsSpan(1)))", + new[] { 0, 1 }, + 2, 2, 2 + }; + yield return new object[] + { + @" +Dim s = {|#0:Fwd({|#1:foo.Substring(1) & bar.Substring(1)|}) & Fwd({|#2:foo.Substring(1) & bar|}).Substring(1) & Fwd({|#3:foo & bar.Substring(1)|})|}", + @" +Dim s = String.Concat(Fwd(String.Concat(foo.AsSpan(1), bar.AsSpan(1))), Fwd(String.Concat(foo.AsSpan(1), bar)).AsSpan(1), Fwd(String.Concat(foo, bar.AsSpan(1))))", + new[] { 0, 1, 2, 3 }, + 4, 3, 3 + }; + } + } + + [Theory] + [MemberData(nameof(Data_NestedViolations_VB))] + public Task NestedViolations_AreReportedAndFixed_VB( + string testStatements, string fixedStatements, int[] locations, + int? incrementalIterations, int? fixAllInDocumentIterations, int? fixAllIterations) + { + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(testStatements), + FixedCode = VBUsings + VBWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + NumberOfIncrementalIterations = incrementalIterations, + NumberOfFixAllIterations = fixAllInDocumentIterations, + NumberOfFixAllInDocumentIterations = fixAllIterations + }; + test.ExpectedDiagnostics.AddRange(locations.Select(x => VerifyVB.Diagnostic(Rule).WithLocation(x))); + return test.RunAsync(); + } + + [Fact] + public Task ConditionalSubstringAccess_IsFlaggedButNotFixed_CS() + { + string statements = @"var s = {|#0:foo?.Substring(1) + bar|};"; + string source = CSUsings + CSWithBody(statements); + + var test = new VerifyCS.Test + { + TestCode = source, + FixedCode = source, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyCS.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Fact] + public Task ConditionalSubstringAccess_IsFlaggedButNotFixed_VB() + { + string statements = @"Dim s = {|#0:foo?.Substring(1) & bar|}"; + string source = VBUsings + VBWithBody(statements); + + var test = new VerifyVB.Test + { + TestCode = source, + FixedCode = source, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Fact] + public Task MissingSystemImport_IsAdded_WhenAbsent_CS() + { + var test = new VerifyCS.Test + { + TestCode = CSWithBody(@"var _ = {|#0:foo + bar.Substring(1)|};"), + FixedCode = $"{Environment.NewLine}{CSUsings}{Environment.NewLine}" + CSWithBody(@"var _ = string.Concat(foo, bar.AsSpan(1));"), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyCS.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + // Visual Basic supports implicit global imports. By default, 'System' is added as a global + // import when you create a project in Visual Studio. + [Fact] + public Task MissingSystemImport_IsNotAdded_WhenIncludedInGlobalImports_VB() + { + var test = new VerifyVB.Test + { + TestCode = VBWithBody(@"Dim s = {|#0:foo & bar.Substring(1)|}"), + FixedCode = VBWithBody(@"Dim s = String.Concat(foo, bar.AsSpan(1))"), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + test.SolutionTransforms.Add((s, id) => + { + var project = s.Projects.Single(); + var options = project.CompilationOptions as VisualBasicCompilationOptions; + var globalSystemImport = GlobalImport.Parse(nameof(System)); + options = options.WithGlobalImports(globalSystemImport); + return s.WithProjectCompilationOptions(project.Id, options); + }); + return test.RunAsync(); + } + + // We must add 'Imports System' if it is not included as a global import. + [Fact] + public Task MissingSystemImport_IsAdded_WhenAbsentFromGlobalImports_VB() + { + var test = new VerifyVB.Test + { + TestCode = VBWithBody(@"Dim s = {|#0:foo & bar.Substring(1)|}"), + FixedCode = $"{Environment.NewLine}{VBUsings}{Environment.NewLine}" + VBWithBody(@"Dim s = String.Concat(foo, bar.AsSpan(1))"), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"Substring(startIndex: 1)", @"AsSpan(start: 1)")] + [InlineData(@"Substring(startIndex: 1, length: 2)", @"AsSpan(start: 1, length: 2)")] + [InlineData(@"Substring(1, length: 2)", @"AsSpan(1, length: 2)")] + [InlineData(@"Substring(startIndex: 1, 2)", @"AsSpan(start: 1, 2)")] + [InlineData(@"Substring(length: 2, startIndex: 1)", @"AsSpan(length: 2, start: 1)")] + public Task NamedSubstringArguments_ArePreserved_CS(string substring, string asSpan) + { + string testStatements = $@"var s = {{|#0:foo.{substring} + bar|}};"; + string fixedStatements = $@"var s = string.Concat(foo.{asSpan}, bar);"; + + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(testStatements), + FixedCode = CSUsings + CSWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyCS.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"Substring(startIndex:=1)", @"AsSpan(start:=1)")] + [InlineData(@"Substring(startIndex:=1, length:=2)", @"AsSpan(start:=1, length:=2)")] + [InlineData(@"Substring(1, length:=2)", @"AsSpan(1, length:=2)")] + [InlineData(@"Substring(startIndex:=1, 2)", @"AsSpan(start:=1, 2)")] + [InlineData(@"Substring(length:=2, startIndex:=1)", @"AsSpan(length:=2, start:=1)")] + public Task NamedSubstringArguments_ArePreserved_VB(string substring, string asSpan) + { + string testStatements = $@"Dim s = {{|#0:foo.{substring} & bar|}}"; + string fixedStatements = $@"Dim s = String.Concat(foo.{asSpan}, bar)"; + + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(testStatements), + FixedCode = VBUsings + VBWithBody(fixedStatements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"foo.Substring(1) + (string)explicitTo", @"string.Concat(foo.AsSpan(1), (string)explicitTo)")] + [InlineData(@"(string)explicitTo + foo.Substring(1)", @"string.Concat((string)explicitTo, foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) + (string)thing", @"string.Concat(foo.AsSpan(1), (string)thing)")] + [InlineData(@"(string)thing + foo.Substring(1)", @"string.Concat((string)thing, foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) + (thing as string)", @"string.Concat(foo.AsSpan(1), thing as string)")] + [InlineData(@"(thing as string) + foo.Substring(1)", @"string.Concat(thing as string, foo.AsSpan(1))")] + public Task ExplicitConversions_ArePreserved_CS(string testExpression, string fixedExpression) + { + string helperTypes = @" +public class ExplicitTo +{ + public static explicit operator string(ExplicitTo operand) => operand?.ToString(); +} + +public class ExplicitFrom +{ + public static explicit operator ExplicitFrom(string operand) => new ExplicitFrom(); +}"; + string format = @" +var explicitTo = new ExplicitTo(); +object thing = bar; +var s = {0};"; + var culture = CultureInfo.InvariantCulture; + string testStatements = string.Format(culture, format, $@"{{|#0:{testExpression}|}}"); + string fixedStatements = string.Format(culture, format, fixedExpression); + + var test = new VerifyCS.Test + { + TestState = { Sources = { CSUsings + CSWithBody(testStatements), helperTypes } }, + FixedState = { Sources = { CSUsings + CSWithBody(fixedStatements), helperTypes } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyCS.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"foo.Substring(1) & CType(explicitTo, String)", @"String.Concat(foo.AsSpan(1), CType(explicitTo, String))")] + [InlineData(@"CType(explicitTo, String) & foo.Substring(1)", @"String.Concat(CType(explicitTo, String), foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) & DirectCast(thing, String)", @"String.Concat(foo.AsSpan(1), DirectCast(thing, String))")] + [InlineData(@"DirectCast(thing, String) & foo.Substring(1)", @"String.Concat(DirectCast(thing, String), foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) & TryCast(thing, String)", @"String.Concat(foo.AsSpan(1), TryCast(thing, String))")] + [InlineData(@"TryCast(thing, String) & foo.Substring(1)", @"String.Concat(TryCast(thing, String), foo.AsSpan(1))")] + public Task ExplicitConversions_ArePreserved_VB(string testExpression, string fixedExpression) + { + string helperTypes = @" +Public Class ExplicitTo + + Public Shared Narrowing Operator CType(operand As ExplicitTo) As String + Return New ExplicitTo() + End Operator +End Class + +Public Class ExplicitFrom + Public Shared Narrowing Operator CType(operand As String) As ExplicitFrom + Return New ExplicitFrom() + End Operator +End Class"; + string format = @" +Dim explicitTo = New ExplicitTo() +Dim thing As Object = bar +Dim s = {0}"; + var culture = CultureInfo.InvariantCulture; + string testStatements = string.Format(culture, format, $@"{{|#0:{testExpression}|}}"); + string fixedStatements = string.Format(culture, format, fixedExpression); + + var test = new VerifyVB.Test + { + TestState = { Sources = { VBUsings + VBWithBody(testStatements), helperTypes } }, + FixedState = { Sources = { VBUsings + VBWithBody(fixedStatements), helperTypes } }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + // No C# case because C# has only one concat operator. + [Theory] + [InlineData(@"foo.Substring(1) & bar + baz", @"String.Concat(foo.AsSpan(1), bar, baz)")] + [InlineData(@"foo & bar.Substring(1) + baz", @"String.Concat(foo, bar.AsSpan(1), baz)")] + [InlineData(@"foo & bar + baz.Substring(1)", @"String.Concat(foo, bar, baz.AsSpan(1))")] + [InlineData(@"foo.Substring(1) + bar & baz", @"String.Concat(foo.AsSpan(1), bar, baz)")] + [InlineData(@"foo + bar.Substring(1) & baz", @"String.Concat(foo, bar.AsSpan(1), baz)")] + [InlineData(@"foo + bar & baz.Substring(1)", @"String.Concat(foo, bar, baz.AsSpan(1))")] + [InlineData(@"foo.Substring(1) & bar + baz & baz.Substring(1)", @"String.Concat(foo.AsSpan(1), bar, baz, baz.AsSpan(1))")] + [InlineData(@"foo & bar.Substring(1) + baz & foo", @"String.Concat(foo, bar.AsSpan(1), baz, foo)")] + [InlineData(@"foo.Substring(1) & bar + baz & foo", @"String.Concat(foo.AsSpan(1), bar, baz, foo)")] + [InlineData(@"foo & bar + baz & foo.Substring(1)", @"String.Concat(foo, bar, baz, foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) + bar & baz + baz.Substring(1)", @"String.Concat(foo.AsSpan(1), bar, baz, baz.AsSpan(1))")] + [InlineData(@"foo + bar.Substring(1) & baz + foo", @"String.Concat(foo, bar.AsSpan(1), baz, foo)")] + [InlineData(@"foo.Substring(1) + bar & baz + foo", @"String.Concat(foo.AsSpan(1), bar, baz, foo)")] + [InlineData(@"foo + bar & baz + foo.Substring(1)", @"String.Concat(foo, bar, baz, foo.AsSpan(1))")] + public Task MixedAddAndConcatenateOperatorChains_AreReportedAndFixed_VB(string testExpression, string fixedExpression) + { + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody($@"Dim s = {{|#0:{testExpression}|}}"), + FixedCode = VBUsings + VBWithBody($@"Dim s = {fixedExpression}"), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"foo.Substring(1) + 'A'", @"string.Concat(foo.AsSpan(1), ""A"")")] + [InlineData(@"'A' + foo.Substring(1)", @"string.Concat(""A"", foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) + bar + 'A'", @"string.Concat(foo.AsSpan(1), bar, ""A"")")] + [InlineData(@"foo.Substring(1) + 'A' + bar", @"string.Concat(foo.AsSpan(1), ""A"", bar)")] + [InlineData(@"foo + 'A' + bar.Substring(1)", @"string.Concat(foo, ""A"", bar.AsSpan(1))")] + [InlineData(@"foo + bar.Substring(1) + 'A'", @"string.Concat(foo, bar.AsSpan(1), ""A"")")] + public Task CharLiterals_AreConvertedToStringLiterals_CS(string testExpression, string fixedExpression) + { + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody($"string s = {{|#0:{testExpression}|}};"), + FixedCode = CSUsings + CSWithBody($"string s = {fixedExpression};"), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyCS.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"foo.Substring(1) & ""A""c", @"String.Concat(foo.AsSpan(1), ""A"")")] + [InlineData(@"""A""c & foo.Substring(1)", @"String.Concat(""A"", foo.AsSpan(1))")] + [InlineData(@"foo.Substring(1) & bar & ""A""c", @"String.Concat(foo.AsSpan(1), bar, ""A"")")] + [InlineData(@"foo.Substring(1) & ""A""c & bar", @"String.Concat(foo.AsSpan(1), ""A"", bar)")] + [InlineData(@"foo & ""A""c & bar.Substring(1)", @"String.Concat(foo, ""A"", bar.AsSpan(1))")] + [InlineData(@"foo & bar.Substring(1) & ""A""c", @"String.Concat(foo, bar.AsSpan(1), ""A"")")] + public Task CharLiterals_AreConvertedToStringLiterals_VB(string testExpression, string fixedExpression) + { + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody($"Dim s = {{|#0:{testExpression}|}}"), + FixedCode = VBUsings + VBWithBody($"Dim s = {fixedExpression}"), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + ExpectedDiagnostics = { VerifyVB.Diagnostic(Rule).WithLocation(0) } + }; + return test.RunAsync(); + } + #endregion + + #region No Diagnostic + [Theory] + [InlineData("foo.Substring(1) + foo + foo + bar + baz")] + [InlineData("foo + foo.Substring(1) + bar + baz + baz")] + [InlineData("foo.Substring(1) + bar.Substring(1) + baz.Substring(1) + bar.Substring(1) + foo.Substring(1)")] + [InlineData("foo.Substring(1) + bar + baz + foo.Substring(1) + bar + baz")] + [InlineData("foo + bar.Substring(1) + baz + foo + bar.Substring(1) + baz")] + [InlineData("foo.Substring(1) + bar + baz.Substring(1) + foo.Substring(1) + bar + baz.Substring(1)")] + public Task TooManyArguments_NoDiagnostic_CS(string expression) + { + string statements = $@"var s = {expression};"; + + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(statements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData("foo.Substring(1) & foo & foo & bar & baz")] + [InlineData("foo & foo.Substring(1) & bar & baz & baz")] + [InlineData("foo.Substring(1) & bar.Substring(1) & baz.Substring(1) & bar.Substring(1) & foo.Substring(1)")] + [InlineData("foo.Substring(1) & bar & baz & foo.Substring(1) & bar & baz")] + [InlineData("foo & bar.Substring(1) & baz & foo & bar.Substring(1) & baz")] + [InlineData("foo.Substring(1) & bar & baz.Substring(1) & foo.Substring(1) & bar & baz.Substring(1)")] + public Task TooManyArguments_NoDiagnostic_VB(string expression) + { + string statements = $@"Dim s = {expression}"; + + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(statements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData("foo + bar")] + [InlineData("foo + bar + baz")] + [InlineData("foo + bar.ToUpper()")] + [InlineData("foo.ToLower() + bar")] + public Task NoSubstringInvocations_NoDiagnostic_CS(string expression) + { + string statements = $@"var s = {expression};"; + + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(statements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData("foo & bar")] + [InlineData("foo & bar & baz")] + [InlineData("foo & bar.ToUpper()")] + [InlineData("foo.ToLower() & bar")] + public Task NoSubstringInvocations_NoDiagnostic_VB(string expression) + { + string statements = $@"Dim s = {expression}"; + + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(statements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + // No VB case because VB can't overload operators. + [Theory] + [InlineData(@"foo.Substring(1) + evil")] + [InlineData(@"evil + foo.Substring(1)")] + [InlineData(@"foo + evil + bar.Substring(1)")] + [InlineData(@"foo.Substring(1) + evil + bar")] + [InlineData(@"foo.Substring(1) + evil + bar + baz")] + [InlineData(@"foo + bar + evil + baz.Substring(1)")] + [InlineData(@"foo + evil + bar.Substring(1) + evil")] + [InlineData(@"foo + evil + bar.Substring(1) + evil + baz.Substring(1)")] + public Task OverloadedAddOperator_NoDiagnostic_CS(string expression) + { + string statements = $@" +var evil = new EvilOverloads(); +var e = {expression};"; + + var test = new VerifyCS.Test + { + TestState = + { + Sources = + { + EvilOverloads, + CSUsings + CSWithBody(statements) + } + }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"foo.Substring(1) + thing")] + [InlineData(@"thing + foo.Substring(1)")] + [InlineData(@"foo.Substring(1) + count")] + [InlineData(@"count + foo.Substring(1)")] + [InlineData(@"foo.Substring(1) + charvar")] + [InlineData(@"charvar + foo.Substring(1)")] + [InlineData(@"foo.Substring(1) + bar + thing")] + [InlineData(@"foo + bar.Substring(1) + thing")] + [InlineData(@"foo.Substring(1) + thing + bar.Substring(1)")] + [InlineData(@"foo.Substring(1) + bar.Substring(1) + thing")] + public Task WithNonStringNonCharLiteralOperands_NoDiagnostic_CS(string expression) + { + string statements = $@" +object thing = new object(); +int count = 17; +char charvar = 'H'; +string s = {expression};"; + + var test = new VerifyCS.Test + { + TestCode = CSUsings + CSWithBody(statements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [InlineData(@"foo.Substring(1) & thing")] + [InlineData(@"thing & foo.Substring(1)")] + [InlineData(@"foo.Substring(1) & count")] + [InlineData(@"count & foo.Substring(1)")] + [InlineData(@"foo.Substring(1) & charvar")] + [InlineData(@"charvar & foo.Substring(1)")] + [InlineData(@"foo.Substring(1) & bar & thing")] + [InlineData(@"foo & bar.Substring(1) & thing")] + [InlineData(@"foo.Substring(1) & thing & bar.Substring(1)")] + [InlineData(@"foo.Substring(1) & bar.Substring(1) & thing")] + public Task WithNonStringNonCharLiteralOperands_NoDiagnostic_VB(string expression) + { + string statements = $@" +Dim thing As Object = New Object() +Dim count As Integer = 17 +Dim charvar As Char = ""H"" +Dim s As String = {expression}"; + + var test = new VerifyVB.Test + { + TestCode = VBUsings + VBWithBody(statements), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + #endregion + + #region Helpers + private static DiagnosticDescriptor Rule => UseSpanBasedStringConcat.Rule; + private const string CSUsings = @"using System;"; + private const string VBUsings = @"Imports System"; + private const string EvilOverloads = @" +public class EvilOverloads +{ + public static EvilOverloads operator +(EvilOverloads left, string right) => left; + public static EvilOverloads operator +(string left, EvilOverloads right) => right; + public static EvilOverloads operator +(EvilOverloads left, EvilOverloads right) => left; +}"; + + private static string CSWithBody(string statements) + { + return @" +public class Testopolis +{ + private void Consume(string consumed) { } + private void Consume(string s1, string s2) { } + private void Consume(string s1, string s2, string s3) { } + private string Fwd(string arg) => arg; + private string Transform(string first, string second) => second; + private string Transform(string first, string second, string third) => first; + private string Produce() => ""Hello World""; + + public void FrobThem(string foo, string bar, string baz) + { +" + IndentLines(statements, " ") + @" + } +}"; + } + + private static string VBWithBody(string statements) + { + return @" +Public Class Testopolis + Private Sub Consume(consumed As String) + End Sub + Private Sub Consume(s1 As String, s2 As String) + End Sub + Private Sub Consume(s1 As String, s2 As String, s3 As String) + End Sub + Private Function Fwd(arg As String) As String + Return arg + End Function + Private Function Transform(first As String, second As String) As String + Return second + End Function + Private Function Transform(first As String, second As String, third As String) As String + Return first + End Function + Private Function Produce() As String + Return ""Hello World"" + End Function + + Public Sub FrobThem(foo As String, bar As String, baz As String) +" + IndentLines(statements, " ") + @" + End Sub +End Class"; + } + + private static string IndentLines(string body, string indent) + { + return indent + body.TrimStart().Replace(Environment.NewLine, Environment.NewLine + indent, StringComparison.Ordinal); + } + #endregion + } +} diff --git a/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Shipped.md b/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Shipped.md index 81335f109c..5dafc4953a 100644 --- a/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Shipped.md +++ b/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Shipped.md @@ -1,7 +1,8 @@ ## Release 5.0 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- CA2218 | Usage | Info | BasicOverrideGetHashCodeOnOverridingEqualsAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2218) -CA2224 | Usage | Info | BasicOverrideEqualsOnOverloadingOperatorEqualsAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2224) \ No newline at end of file +CA2224 | Usage | Info | BasicOverrideEqualsOnOverloadingOperatorEqualsAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2224) diff --git a/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.Fixer.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.Fixer.vb new file mode 100644 index 0000000000..70cb5e3ee0 --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.Fixer.vb @@ -0,0 +1,63 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Editing +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.NetCore.Analyzers.Runtime + +Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime + + + Public NotInheritable Class BasicUseSpanBasedStringConcatFixer : Inherits UseSpanBasedStringConcatFixer + + Private Protected Overrides Function ReplaceInvocationMethodName(generator As SyntaxGenerator, invocationSyntax As SyntaxNode, newName As String) As SyntaxNode + + Dim cast = DirectCast(invocationSyntax, InvocationExpressionSyntax) + Dim memberAccessSyntax = DirectCast(cast.Expression, MemberAccessExpressionSyntax) + Dim oldNameSyntax = memberAccessSyntax.Name + Dim newNameSyntax = generator.IdentifierName(newName).WithTriviaFrom(oldNameSyntax) + Return invocationSyntax.ReplaceNode(oldNameSyntax, newNameSyntax) + End Function + + Private Protected Overrides Function IsSystemNamespaceImported(project As Project, namespaceImports As IReadOnlyList(Of SyntaxNode)) As Boolean + + Dim options = DirectCast(project.CompilationOptions, VisualBasicCompilationOptions) + If options.GlobalImports.Any(Function(x) String.Compare(x.Name, NameOf(System), StringComparison.OrdinalIgnoreCase) = 0) Then + Return True + End If + + For Each node As SyntaxNode In namespaceImports + Dim importsStatement = TryCast(node, ImportsStatementSyntax) + If importsStatement Is Nothing Then + Continue For + End If + For Each importsClause As ImportsClauseSyntax In importsStatement.ImportsClauses + Dim simpleClause = TryCast(importsClause, SimpleImportsClauseSyntax) + Dim identifierName = TryCast(simpleClause?.Name, IdentifierNameSyntax) + If identifierName Is Nothing Then + Continue For + End If + If identifierName.Identifier.ValueText = NameOf(System) Then + Return True + End If + Next + Next + Return False + End Function + + Private Protected Overrides Function WalkDownBuiltInImplicitConversionOnConcatOperand(operand As IOperation) As IOperation + + Return UseSpanBasedStringConcat.BasicWalkDownBuiltInImplicitConversionOnConcatOperand(operand) + End Function + + Private Protected Overrides Function IsNamedArgument(argumentOperation As IArgumentOperation) As Boolean + + Dim argumentSyntax = TryCast(argumentOperation.Syntax, ArgumentSyntax) + Return argumentSyntax IsNot Nothing AndAlso argumentSyntax.IsNamed + End Function + End Class +End Namespace + diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.vb new file mode 100644 index 0000000000..6d3bf839ea --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.vb @@ -0,0 +1,54 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.NetCore.Analyzers.Runtime +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis + +Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime + + + Public NotInheritable Class BasicUseSpanBasedStringConcat : Inherits UseSpanBasedStringConcat + + Private Protected Overrides Function TryGetTopMostConcatOperation(binaryOperation As IBinaryOperation, ByRef rootBinaryOperation As IBinaryOperation) As Boolean + + If Not IsStringConcatOperation(binaryOperation) Then + rootBinaryOperation = Nothing + Return False + End If + + Dim parentBinaryOperation = binaryOperation + Dim current As IBinaryOperation + Do + current = parentBinaryOperation + parentBinaryOperation = TryCast(WalkUpImplicitConversionToObject(current.Parent), IBinaryOperation) + Loop While parentBinaryOperation IsNot Nothing AndAlso IsStringConcatOperation(parentBinaryOperation) + + rootBinaryOperation = current + Return True + End Function + + Private Protected Overrides Function WalkDownBuiltInImplicitConversionOnConcatOperand(operand As IOperation) As IOperation + + Return BasicWalkDownBuiltInImplicitConversionOnConcatOperand(operand) + End Function + + Private Shared Function IsStringConcatOperation(operation As IBinaryOperation) As Boolean + + 'OperatorKind will be Concatenate even when the "+" operator is used, provided both operands are strings. + Return operation.OperatorKind = BinaryOperatorKind.Concatenate + End Function + + Private Shared Function WalkUpImplicitConversionToObject(operation As IOperation) As IOperation + + Dim conversion = TryCast(operation, IConversionOperation) + If conversion IsNot Nothing AndAlso conversion.Type.SpecialType = SpecialType.System_Object AndAlso + conversion.IsImplicit AndAlso Not conversion.Conversion.IsUserDefined Then + Return conversion.Parent + Else + Return operation + End If + End Function + End Class +End Namespace + diff --git a/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md b/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md index d05d8d0c7b..da24a7daa8 100644 --- a/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md @@ -1,6 +1,7 @@ ; Please do not edit this file manually, it should only be updated through code fix application. ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- HAA0101 | Performance | Warning | CallSiteImplicitAllocationAnalyzer @@ -14,4 +15,4 @@ HAA0401 | Performance | Warning | EnumeratorAllocationAnalyzer HAA0601 | Performance | Warning | TypeConversionAllocationAnalyzer HAA0602 | Performance | Warning | TypeConversionAllocationAnalyzer HAA0603 | Performance | Warning | TypeConversionAllocationAnalyzer -HAA0604 | Performance | Info | TypeConversionAllocationAnalyzer \ No newline at end of file +HAA0604 | Performance | Info | TypeConversionAllocationAnalyzer diff --git a/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf b/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf index 9957f0bea1..220c4bd104 100644 --- a/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf +++ b/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + The compiler will emit a class that will hold this as a field to allow capturing of this closure diff --git a/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf b/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf index e023c6abea..a2861a06d9 100644 --- a/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf +++ b/src/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + The compiler will emit a class that will hold this as a field to allow capturing of this closure diff --git a/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf b/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf index cfe101a570..934aa18108 100644 --- a/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf +++ b/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Avoid allocation by using 'Array.Empty<T>()' diff --git a/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf b/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf index 433697671b..d911d7fbf8 100644 --- a/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf +++ b/src/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Avoid allocation by using 'Array.Empty<T>()' diff --git a/src/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md index 19d1f8b1b2..4458b84d8e 100644 --- a/src/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,9 +1,10 @@ ; Please do not edit this file manually, it should only be updated through code fix application. ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- HAA0501 | Performance | Info | ExplicitAllocationAnalyzer HAA0502 | Performance | Info | ExplicitAllocationAnalyzer HAA0503 | Performance | Info | ExplicitAllocationAnalyzer, [Documentation](http://msdn.microsoft.com/en-us/library/bb397696.aspx) -HAA0506 | Performance | Info | ExplicitAllocationAnalyzer \ No newline at end of file +HAA0506 | Performance | Info | ExplicitAllocationAnalyzer diff --git a/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf b/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf index a26a4409b0..36ca2a3d1a 100644 --- a/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf +++ b/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Explicit new anonymous object allocation diff --git a/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf b/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf index 24c339ecea..8aeb788a6d 100644 --- a/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf +++ b/src/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Explicit new anonymous object allocation diff --git a/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md b/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md index 61fa7ea983..1c699390ec 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md +++ b/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md @@ -1,6 +1,7 @@ ## Release 2.9.8 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0016 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) @@ -14,6 +15,7 @@ RS0027 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https:/ ## Release 3.3.0 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0036 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) diff --git a/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md b/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md index 41578eccc5..77cbb77f85 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md @@ -1,5 +1,7 @@ ; Please do not edit this file manually, it should only be updated through code fix application. + ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- -RS0050 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) \ No newline at end of file +RS0050 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) diff --git a/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs b/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs index 7682836733..edc95eb10a 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs +++ b/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs @@ -536,7 +536,7 @@ private string GetSiblingNamesToRemoveFromUnshippedTextCore(ISymbol symbol) { if (sibling.IsImplicitlyDeclared) { - if (!sibling.IsConstructor()) + if (sibling is not IMethodSymbol { MethodKind: MethodKind.Constructor or MethodKind.PropertyGet or MethodKind.PropertySet }) { continue; } diff --git a/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs b/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs index 74cb506098..ddf40ea465 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs +++ b/src/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs @@ -318,8 +318,8 @@ private static bool TryGetApiData(AnalyzerOptions analyzerOptions, Compilation c return false; } - shippedData = ReadApiData(shippedText.Path, shippedText.GetText(cancellationToken), isShippedApi: true); - unshippedData = ReadApiData(unshippedText.Path, unshippedText.GetText(cancellationToken), isShippedApi: false); + shippedData = ReadApiData(shippedText.Value.path, shippedText.Value.text, isShippedApi: true); + unshippedData = ReadApiData(unshippedText.Value.path, unshippedText.Value.text, isShippedApi: false); return true; } @@ -376,27 +376,41 @@ private static bool TryGetEditorConfigOptionForMissingFiles(AnalyzerOptions anal private static bool TryGetApiText( ImmutableArray additionalTexts, CancellationToken cancellationToken, - [NotNullWhen(returnValue: true)] out AdditionalText? shippedText, - [NotNullWhen(returnValue: true)] out AdditionalText? unshippedText) + [NotNullWhen(returnValue: true)] out (string path, SourceText text)? shippedText, + [NotNullWhen(returnValue: true)] out (string path, SourceText text)? unshippedText) { shippedText = null; unshippedText = null; StringComparer comparer = StringComparer.Ordinal; - foreach (AdditionalText text in additionalTexts) + foreach (AdditionalText additionalText in additionalTexts) { cancellationToken.ThrowIfCancellationRequested(); - string fileName = Path.GetFileName(text.Path); - if (comparer.Equals(fileName, ShippedFileName)) - { - shippedText = text; - continue; - } + string fileName = Path.GetFileName(additionalText.Path); + + bool isShippedFile = comparer.Equals(fileName, ShippedFileName); + bool isUnshippedFile = comparer.Equals(fileName, UnshippedFileName); - if (comparer.Equals(fileName, UnshippedFileName)) + if (isShippedFile || isUnshippedFile) { - unshippedText = text; + SourceText text = additionalText.GetText(cancellationToken); + + if (text == null) + { + continue; + } + + var data = (additionalText.Path, text); + if (isShippedFile) + { + shippedText = data; + } + + if (isUnshippedFile) + { + unshippedText = data; + } continue; } } diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf index ae059863f6..0c562c7984 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Symbol {0} se označil jako odebraný, ale neodstranil se ze zdrojového kódu. Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + Veřejné rozhraní API se označilo jako odebrané, ale existuje ve zdrojovém kódu. diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf index 2dd3f97ba2..e124dc1601 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Das Symbol ‚{0}‘ ist als entfernt markiert, wird jedoch im Quellcode nicht gelöscht. Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + Die öffentliche API ist als entfernt markiert, ist jedoch im Quellcode noch vorhanden. diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf index 26c487f0f5..b826a53273 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + El símbolo ' {0} ' está marcado como retirado, pero no se elimina en el código fuente Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + La API pública se marcó como quitada, pero existe en el código fuente diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf index 2a21486d52..17633796af 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Le symbole « {0} » est marqué comme supprimé, mais il n’est pas supprimé dans le code source Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + L’API publique est marquée comme étant supprimée, mais elle existe dans le code source diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf index de7900cb01..b80fab0cba 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Il simbolo '{0}' è contrassegnato come rimosso, ma non è stato eliminato nel codice sorgente Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + L'API pubblica è contrassegnata come rimossa, ma è presente nel codice sorgente diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf index ebf9dd2a59..a5be5ec395 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + 記号 '{0}' が削除済みとしてマークされていますが、ソース コードでは削除されていません Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + パブリック API が削除済みとマークされていますが、ソース コード内に存在します diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf index f5f74eace7..536583047b 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + '{0}' 기호가 제거됨으로 표시되지만 소스 코드에서는 삭제되지 않습니다. Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + 공용 API가 제거됨으로 표시되지만 소스 코드에는 있습니다. diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf index 5c7775174b..39f1c272c8 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Symbol „{0}” został oznaczony jako usunięty, ale nie jest usuwany w kodzie źródłowym Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + Publiczny interfejs API został oznaczony jako usunięty, ale istnieje w kodzie źródłowym diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf index 95319fa840..3df2ddb256 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Símbolo "{0}" está marcado como removido, mas não é excluído no código-fonte Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + A API pública está marcada como removida, mas existe no código-fonte diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf index db9e1ef908..352abedce5 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + Символ "{0}" помечен как удаленный, но он не удален в исходном коде Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + Открытый API помечен как удаленный, но он существует в исходном коде diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf index fee7959e65..edeb7f7e1c 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + '{0}' sembolü kaldırıldı olarak işaretlenmiş ancak kaynak kodunda silinmemiş Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + Genel API kaldırıldı olarak işaretlenmiş ancak kaynak kodunda mevcut diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf index 39c9f82c4d..1fd1e49ac6 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Add all items in document '{0}' to the public API @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + 符号“{0}”标记为已删除,但未在源代码中删除 Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + 公共 API 标记为已删除,但它存在于源代码中 diff --git a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf index f119f4a723..35008ba4b6 100644 --- a/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf +++ b/src/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Add all items in document '{0}' to the public API @@ -189,12 +189,12 @@ Symbol '{0}' is marked as removed but it isn't deleted in source code - Symbol '{0}' is marked as removed but it isn't deleted in source code + 符號 '{0}' 標記為已移除,但未在原始程式碼中刪除 Public API is marked as removed but it exists in source code - Public API is marked as removed but it exists in source code + 公用 API 標記為已移除,但存在於原始程式碼中 diff --git a/src/PublicApiAnalyzers/UnitTests/DeclarePublicApiAnalyzerTests.cs b/src/PublicApiAnalyzers/UnitTests/DeclarePublicApiAnalyzerTests.cs index e7ce02222e..d6d3501ae4 100644 --- a/src/PublicApiAnalyzers/UnitTests/DeclarePublicApiAnalyzerTests.cs +++ b/src/PublicApiAnalyzers/UnitTests/DeclarePublicApiAnalyzerTests.cs @@ -152,12 +152,23 @@ private async Task VerifyCSharpAdditionalFileFixAsync(string source, string? shi await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedApiText, oldUnshippedApiText, newUnshippedApiText); } - private async Task VerifyAdditionalFileFixAsync(string language, string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText) + private async Task VerifyNet50CSharpAdditionalFileFixAsync(string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText) + { + await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedApiText, oldUnshippedApiText, newUnshippedApiText, ReferenceAssemblies.Net.Net50); + } + + private async Task VerifyAdditionalFileFixAsync(string language, string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText, + ReferenceAssemblies? referenceAssemblies = null) { var test = language == LanguageNames.CSharp ? new CSharpCodeFixTest() : (CodeFixTest)new VisualBasicCodeFixTest(); + if (referenceAssemblies is not null) + { + test.ReferenceAssemblies = referenceAssemblies; + } + test.TestState.Sources.Add(source); if (shippedApiText != null) test.TestState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.ShippedFileName, shippedApiText)); @@ -2537,6 +2548,27 @@ public partial class {|RS0016:{|RS0016:C|}|} await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); } + [Fact, WorkItem(4133, "https://github.com/dotnet/roslyn-analyzers/issues/4133")] + public async Task Record_ImplicitProperty_Fix() + { + var source = @" +public record R(int {|RS0016:P|}); +"; + + var shippedText = @""; + var unshippedText = @"R +R.R(int P) -> void +R.P.get -> int +"; + var fixedUnshippedText = @"R +R.P.init -> void +R.R(int P) -> void +R.P.get -> int +"; + + await VerifyNet50CSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + #endregion } } diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md b/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md index c0cfcba7b3..35b525cedd 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md @@ -1,6 +1,7 @@ ## Release 3.3.0 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0038 | RoslynDiagnosticsMaintainability | Warning | PreferNullLiteral diff --git a/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md index d324c9d3f7..20cbd4362a 100644 --- a/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -1,6 +1,7 @@ ; Please do not edit this file manually, it should only be updated through code fix application. ### Removed Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0100 | RoslynDiagnosticsMaintainability | Warning | CSharpWrapStatementsDiagnosticAnalyzer diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md index 1cd3b9e4a2..1e31d4dcef 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md @@ -1,6 +1,7 @@ ## Release 2.9.8 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0001 | RoslyDiagnosticsPerformance | Warning | SpecializedEnumerableCreationAnalyzer @@ -17,6 +18,7 @@ RS0034 | RoslyDiagnosticsReliability | Warning | ExportedPartsShouldHaveImportin ## Release 3.3.0 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0040 | RoslynDiagnosticsReliability | Warning | DefaultableTypeShouldHaveDefaultableFieldsAnalyzer @@ -25,6 +27,7 @@ RS0043 | RoslynDiagnosticsMaintainability | Warning | DoNotCallGetTestAccessor RS0101 | RoslynDiagnosticsMaintainability | Warning | AbstractBlankLinesDiagnosticAnalyzer ### Changed Rules + Rule ID | New Category | New Severity | Old Category | Old Severity | Notes --------|--------------|--------------|--------------|--------------|------- RS0001 | RoslynDiagnosticsPerformance | Warning | RoslyDiagnosticsPerformance | Warning | SpecializedEnumerableCreationAnalyzer @@ -38,6 +41,7 @@ RS0033 | RoslynDiagnosticsReliability | Warning | RoslyDiagnosticsReliability | RS0034 | RoslynDiagnosticsReliability | Warning | RoslyDiagnosticsReliability | Warning | ExportedPartsShouldHaveImportingConstructor ### Removed Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0013 | RoslyDiagnosticsPerformance | Disabled | DiagnosticDescriptorAccessAnalyzer diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md index 93493b7c48..3c7707bbd5 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,11 +1,13 @@ ; Please do not edit this file manually, it should only be updated through code fix application. ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- RS0049 | RoslynDiagnosticsReliability | Warning | TemporaryArrayAsRefAnalyzer ### Removed Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- -RS0101 | RoslynDiagnosticsMaintainability | Warning | AbstractBlankLinesDiagnosticAnalyzer \ No newline at end of file +RS0101 | RoslynDiagnosticsMaintainability | Warning | AbstractBlankLinesDiagnosticAnalyzer diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf index a6038f26db..8fdb70141b 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + Add 'Shared' attribute diff --git a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf index 3e42d8d799..2671a33baa 100644 --- a/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf +++ b/src/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + Add 'Shared' attribute diff --git a/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md b/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md index e982af3c56..3392211be3 100644 --- a/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md @@ -1,6 +1,7 @@ ## Release 2.9.8 ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- -RS0004 | Usage | Disabled | BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsAnalyzer \ No newline at end of file +RS0004 | Usage | Disabled | BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsAnalyzer diff --git a/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md +++ b/src/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md +++ b/src/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md index 8ba051bec0..97bb6e71fa 100644 --- a/src/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,8 +1,9 @@ ; Please do not edit this file manually, it should only be updated through code fix application. ### New Rules + Rule ID | Category | Severity | Notes --------|----------|----------|------- CA1704 | Naming | Warning | IdentifiersShouldBeSpelledCorrectlyAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1704) CA1714 | Naming | Disabled | EnumsShouldHavePluralNamesAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1714) -CA1717 | Naming | Disabled | EnumsShouldHavePluralNamesAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1717) \ No newline at end of file +CA1717 | Naming | Disabled | EnumsShouldHavePluralNamesAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1717) diff --git a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf index cf231d1d62..4c8ac81bcc 100644 --- a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf +++ b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf @@ -79,7 +79,7 @@ Consider providing a more meaningful name than assembly name '{0}' - Envisagez de fournir un nom plus significatif que le nom d'assembly '{0}' + Si possible, indiquez un nom plus significatif que le nom d'assembly '{0}' diff --git a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf index 9bc9b58a84..ed320baed9 100644 --- a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf +++ b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf @@ -39,7 +39,7 @@ Correct the spelling of '{0}' in assembly name '{1}' - 어셈블리 이름 '{1}'에서 '{0}'의 맞춤법을 수정하세요. + 어셈블리 이름 {1}에서 '{0}'의 맞춤법을 수정하세요. @@ -49,37 +49,37 @@ Correct the spelling of '{0}' in type name '{1}' - 형식 이름 '{1}'에서 '{0}'의 맞춤법을 수정하세요. + 형식 이름 {1}에서 '{0}'의 맞춤법을 수정하세요. Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation - 멤버 이름 '{1}'에서 '{0}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. + 멤버 이름 {1}에서 '{0}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation - 메서드 '{0}'의 매개 변수 이름 '{2}'에서 '{1}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. + {0} 메서드의 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation - 대리자 '{0}'의 매개 변수 이름 '{2}'에서 '{1}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. + {0} 대리자의 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' - 형식 '{0}'의 제네릭 형식 매개 변수 이름 '{2}'에서 '{1}'의 맞춤법을 수정하세요. + {0} 형식의 제네릭 형식 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하세요. On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' - 메서드 '{0}'의 제네릭 형식 매개 변수 이름 '{2}'에서 '{1}'의 맞춤법을 수정하세요. + {0} 메서드의 제네릭 형식 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하세요. Consider providing a more meaningful name than assembly name '{0}' - 어셈블리 이름 '{0}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + 어셈블리 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. @@ -89,32 +89,32 @@ Consider providing a more meaningful name than type name '{0}' - 형식 이름 '{0}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + 형식 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. Consider providing a more meaningful name than member name '{0}' - 멤버 이름 '{0}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + 멤버 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. In method '{0}', consider providing a more meaningful name than parameter name '{1}' - 메서드 '{0}'에서 매개 변수 이름 '{1}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + {0} 메서드에서 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' - 대리자 '{0}'에서 매개 변수 이름 '{1}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + {0} 대리자에서 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' - 형식 '{0}'에서 제네릭 형식 매개 변수 이름 '{1}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + {0} 형식에서 제네릭 형식 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' - 메서드 '{0}'에서 제네릭 형식 매개 변수 이름 '{1}'보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + {0} 메서드에서 제네릭 형식 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. diff --git a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf index e5d42164d8..c9649ee14e 100644 --- a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf +++ b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf @@ -39,7 +39,7 @@ Correct the spelling of '{0}' in assembly name '{1}' - Исправьте написание "{0}" в имени сборки "{1}". + Исправьте правописание "{0}" в имени сборки {1}. @@ -49,37 +49,37 @@ Correct the spelling of '{0}' in type name '{1}' - Исправьте написание "{0}" в имени типа "{1}". + Исправьте правописание "{0}" в имени типа {1}. Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation - Исправьте написание "{0}" в имени элемента "{1}" или удалите его целиком, если используется венгерская нотация. + Исправьте правописание "{0}" в имени элемента {1} или удалите его целиком, если используется венгерская нотация. In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation - В методе "{0}" исправьте написание "{1}" в имени параметра "{2}" или удалите его целиком, если используется венгерская нотация. + В методе {0} исправьте правописание "{1}" в имени параметра {2} или удалите его целиком, если используется венгерская нотация. In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation - В делегате "{0}" исправьте написание "{1}" в имени параметра "{2}" или удалите его целиком, если используется венгерская нотация. + В делегате {0} исправьте правописание "{1}" в имени параметра {2} или удалите его целиком, если используется венгерская нотация. On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' - В типе "{0}" исправьте написание "{1}" в имени параметра универсального типа "{2}". + В типе {0} исправьте правописание "{1}" в имени параметра универсального типа {2}. On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' - В методе "{0}" исправьте написание "{1}" в имени параметра универсального типа "{2}". + В методе {0} исправьте правописание "{1}" в имени параметра универсального типа {2}. Consider providing a more meaningful name than assembly name '{0}' - Подберите более осмысленное имя для сборки "{0}". + Подберите более значимое имя для сборки {0}. @@ -89,32 +89,32 @@ Consider providing a more meaningful name than type name '{0}' - Подберите более осмысленное имя для типа "{0}". + Подберите более значимое имя для типа {0}. Consider providing a more meaningful name than member name '{0}' - Подберите более осмысленное имя для элемента "{0}". + Подберите более значимое имя для элемента {0}. In method '{0}', consider providing a more meaningful name than parameter name '{1}' - В методе "{0}" подберите более осмысленное имя для параметра "{1}". + В методе {0} подберите более значимое имя для параметра {1}. In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' - В делегате "{0}" подберите более осмысленное имя для параметра "{1}". + В делегате {0} подберите более значимое имя для параметра {1}. On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' - В типе "{0}" подберите более осмысленное имя для параметра универсального типа "{1}". + В типе {0} подберите более значимое имя для параметра универсального типа {1}. On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' - В методе "{0}" подберите более осмысленное имя для параметра универсального типа "{1}". + В методе {0} подберите более значимое имя для параметра универсального типа {1}. diff --git a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf index b59f99e071..2e74111dc0 100644 --- a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf +++ b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf @@ -1,6 +1,6 @@  - + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. diff --git a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf index 4ec110af63..7e1cb054b8 100644 --- a/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf +++ b/src/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf @@ -1,6 +1,6 @@  - + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. diff --git a/src/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md index 11d7085ab0..cdf4f1397e 100644 --- a/src/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md +++ b/src/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -1,2 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. - diff --git a/src/Tools/GenerateAnalyzerNuspec/Program.cs b/src/Tools/GenerateAnalyzerNuspec/Program.cs index d21ff14994..0f2678959d 100644 --- a/src/Tools/GenerateAnalyzerNuspec/Program.cs +++ b/src/Tools/GenerateAnalyzerNuspec/Program.cs @@ -85,6 +85,7 @@ result.AppendLine(@" "); result.AppendLine(@" "); +result.AppendLine(@" $CommonFileElements$"); if (fileList.Length > 0 || assemblyList.Length > 0 || libraryList.Length > 0 || folderList.Length > 0) { diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 55a6b04b07..7b44225310 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -12,7 +12,7 @@ Design: CA2210, CA1000-CA1070 Globalization: CA2101, CA1300-CA1310 Mobility: CA1600-CA1601 -Performance: HA, CA1800-CA1843 +Performance: HA, CA1800-CA1845 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5403 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2250 Naming: CA1700-CA1726 diff --git a/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs b/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs index ad5decde17..2462c32ec2 100644 --- a/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs +++ b/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs @@ -55,7 +55,7 @@ private static ObjectPool> CreatePool(IComparer? comparer /// /// Gets a pooled instance of a with an optional comparer. /// - /// Comparer to use, or null for the element type's default comparer. + /// Singleton (or at least a bounded number) comparer to use, or null for the element type's default comparer. /// An empty . public static PooledSortedSet GetInstance(IComparer? comparer = null) { diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index 331f5bd3da..99645274a8 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -195,6 +195,7 @@ internal static class WellKnownTypeNames public const string SystemGC = "System.GC"; public const string SystemGlobalizationCultureInfo = "System.Globalization.CultureInfo"; public const string SystemGuid = "System.Guid"; + public const string SystemHashCode = "System.HashCode"; public const string SystemIAsyncDisposable = "System.IAsyncDisposable"; public const string SystemIComparable = "System.IComparable"; public const string SystemIComparable1 = "System.IComparable`1"; @@ -224,6 +225,7 @@ internal static class WellKnownTypeNames public const string SystemLinqQueryable = "System.Linq.Queryable"; public const string SystemMarshalByRefObject = "System.MarshalByRefObject"; public const string SystemMemory1 = "System.Memory`1"; + public const string SystemMemoryExtensions = "System.MemoryExtensions"; public const string SystemNetHttpHttpClient = "System.Net.Http.HttpClient"; public const string SystemNetHttpHttpClientHandler = "System.Net.Http.HttpClientHandler"; public const string SystemNetHttpWinHttpHandler = "System.Net.Http.WinHttpHandler";