Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ Rule ID | Category | Severity | Notes
--------|----------|----------|-------
CA1856 | Performance | Error | ConstantExpectedAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1856)
CA1857 | Performance | Warning | ConstantExpectedAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1857)
CA1858 | Performance | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1858)
CA1859 | Performance | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1859)
CA1860 | Performance | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1860)
CA1861 | Performance | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)
Original file line number Diff line number Diff line change
Expand Up @@ -2031,4 +2031,17 @@
<data name="PreventNumericIntPtrUIntPtrBehavioralChangesConversionThrowsMessage" xml:space="preserve">
<value>Starting with .NET 7 the explicit conversion '{0}' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.</value>
</data>
<data name="UseThrowHelperTitle" xml:space="preserve">
<value>Use Throw helper</value>
</data>
<data name="UseThrowHelperMessage" xml:space="preserve">
<value>Use '{0}.{1}' instead of explicitly throwing a new exception instance</value>
</data>
<data name="UseThrowHelperDescription" xml:space="preserve">
<value>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</value>
</data>
<data name="UseThrowHelperFix" xml:space="preserve">
<value>Use '{0}.{1}'</value>
</data>

</root>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;

namespace Microsoft.NetCore.Analyzers.Runtime
{
/// <summary>Fixer for <see cref="UseExceptionThrowHelpers"/>.</summary>
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
public sealed class UseExceptionThrowHelpersFixer : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(
UseExceptionThrowHelpers.UseArgumentNullExceptionThrowIfNullRuleId,
UseExceptionThrowHelpers.UseArgumentExceptionThrowIfNullOrEmptyRuleId,
UseExceptionThrowHelpers.UseArgumentOutOfRangeExceptionThrowIfRuleId,
UseExceptionThrowHelpers.UseObjectDisposedExceptionThrowIfRuleId);

public sealed override FixAllProvider GetFixAllProvider() => CustomFixAllProvider.Instance;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
Document doc = context.Document;
SemanticModel model = await doc.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

if (TryGetFixInfo(doc, root, model, context.Diagnostics[0], out INamedTypeSymbol? typeSymbol, out string? methodName, out SyntaxNode? node, out SyntaxNode? arg, out SyntaxNode? other))
{
string title = string.Format(MicrosoftNetCoreAnalyzersResources.UseThrowHelperFix, typeSymbol.Name, methodName);
context.RegisterCodeFix(
CodeAction.Create(title, equivalenceKey: title, createChangedDocument: async cancellationToken =>
{
DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false);
ApplyFix(typeSymbol, methodName, node, arg, other, editor);
return editor.GetChangedDocument();
}),
context.Diagnostics);
}
}

private static bool TryGetFixInfo(
Document doc,
SyntaxNode root,
SemanticModel model,
Diagnostic diagnostic,
[NotNullWhen(true)] out INamedTypeSymbol? typeSymbol,
[NotNullWhen(true)] out string? methodName,
[NotNullWhen(true)] out SyntaxNode? node,
[NotNullWhen(true)] out SyntaxNode? arg,
[NotNullWhen(true)] out SyntaxNode? other)
{
typeSymbol = null;
methodName = null;
arg = null;
other = null;

node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
if (node != null &&
diagnostic.AdditionalLocations.Count != 0 &&
diagnostic.AdditionalLocations[0] is Location argLocation)
{
arg = root.FindNode(argLocation.SourceSpan, getInnermostNodeForTie: true);
string id = diagnostic.Id;

if (diagnostic.AdditionalLocations.Count == 2)
{
Location otherLocation = diagnostic.AdditionalLocations[1];
other = otherLocation == Location.None ? // None is special-cased by the analyzer to mean "this"
SyntaxGenerator.GetGenerator(doc).ThisExpression() :
root.FindNode(otherLocation.SourceSpan, getInnermostNodeForTie: true);
}

switch (id)
{
case UseExceptionThrowHelpers.UseArgumentNullExceptionThrowIfNullRuleId:
typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentNullException);
methodName = "ThrowIfNull";
break;

case UseExceptionThrowHelpers.UseArgumentExceptionThrowIfNullOrEmptyRuleId:
typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentException);
methodName = "ThrowIfNullOrEmpty";
break;

case UseExceptionThrowHelpers.UseArgumentOutOfRangeExceptionThrowIfRuleId:
typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentOutOfRangeException);
diagnostic.Properties.TryGetValue(UseExceptionThrowHelpers.MethodNamePropertyKey, out methodName);
break;

case UseExceptionThrowHelpers.UseObjectDisposedExceptionThrowIfRuleId when other is not null:
typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObjectDisposedException);
methodName = "ThrowIf";
break;
}
}

return typeSymbol != null && methodName != null && arg != null;
}

private static void ApplyFix(
INamedTypeSymbol typeSymbol,
string methodName,
SyntaxNode node,
SyntaxNode arg,
SyntaxNode other,
SyntaxEditor editor)
{
editor.ReplaceNode(
node,
editor.Generator.ExpressionStatement(
editor.Generator.InvocationExpression(
editor.Generator.MemberAccessExpression(
editor.Generator.TypeExpressionForStaticMemberAccess(typeSymbol), methodName),
other is not null ? new SyntaxNode[] { arg, other } : new SyntaxNode[] { arg })).WithTriviaFrom(node));
}

private sealed class CustomFixAllProvider : DocumentBasedFixAllProvider
{
public static readonly CustomFixAllProvider Instance = new();

protected override string CodeActionTitle => MicrosoftNetCoreAnalyzersResources.UseThrowHelperFix;

protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics)
{
DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false);
SyntaxNode root = editor.OriginalRoot;
SemanticModel model = editor.SemanticModel;

foreach (Diagnostic diagnostic in diagnostics)
{
if (TryGetFixInfo(document, root, model, diagnostic, out INamedTypeSymbol? typeSymbol, out string? methodName, out SyntaxNode? node, out SyntaxNode? arg, out SyntaxNode? other))
{
ApplyFix(typeSymbol, methodName, node, arg, other, editor);
}
}

return editor.GetChangedRoot();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">Použít znakový literál pro vyhledávání s jedním znakem</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">Analyzátor kompatibility platformy vyžaduje platný název a verzi platformy.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">Zeichenliteral für die Suche nach einem einzelnen Zeichen verwenden</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">Das Analysetool für Plattformkompatibilität erfordert einen gültigen Plattformnamen und eine gültige Version.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">Usar literal de carácter para una búsqueda de caracteres individuales</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">El analizador de compatibilidad de plataforma requiere un nombre de plataforma y una versión válidos.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">Utiliser le littéral char pour une recherche à caractère unique</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">Platform Analyzer requiert un nom de plateforme et une version valides.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">Usare il valore letterale char per la ricerca di un singolo carattere</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">Con l'analizzatore della compatibilità della piattaforma sono richiesti un nome e una versione di piattaforma validi.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">1 つの文字参照に単一文字検索を使用する</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">プラットフォーム互換性アナライザーには、有効なプラットフォーム名とバージョンが必要です。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,26 @@
<target state="translated">단일 문자 조회에 char 리터럴 사용</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperDescription">
<source>Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</source>
<target state="new">Throw helpers are simpler and more efficient than an if block constructing a new exception instance.</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperFix">
<source>Use '{0}.{1}'</source>
<target state="new">Use '{0}.{1}'</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperMessage">
<source>Use '{0}.{1}' instead of explicitly throwing a new exception instance</source>
<target state="new">Use '{0}.{1}' instead of explicitly throwing a new exception instance</target>
<note />
</trans-unit>
<trans-unit id="UseThrowHelperTitle">
<source>Use Throw helper</source>
<target state="new">Use Throw helper</target>
<note />
</trans-unit>
<trans-unit id="UseValidPlatformStringDescription">
<source>Platform compatibility analyzer requires a valid platform name and version.</source>
<target state="translated">플랫폼 호환성 분석기에는 유효한 플랫폼 이름과 버전이 필요합니다.</target>
Expand Down
Loading