| File: Microsoft.NetCore.Analyzers\Runtime\AvoidUnreliableStreamRead.Fixer.cs | Web Access |
| Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; 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; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Runtime { using static MicrosoftNetCoreAnalyzersResources; /// <summary> /// CA2022: <inheritdoc cref="AvoidUnreliableStreamReadTitle"/> /// </summary> [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidUnreliableStreamReadFixer : CodeFixProvider { private const string Async = nameof(Async); private const string ReadExactly = nameof(ReadExactly); private const string ReadExactlyAsync = nameof(ReadExactlyAsync); public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(AvoidUnreliableStreamReadAnalyzer.RuleId); public sealed override FixAllProvider GetFixAllProvider() { return WellKnownFixAllProviders.BatchFixer; } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var node = root.FindNode(context.Span, getInnermostNodeForTie: true); if (node is null) { return; } var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); var operation = semanticModel.GetOperation(node, context.CancellationToken); if (operation is not IInvocationOperation invocation || invocation.Instance is null) { return; } var compilation = semanticModel.Compilation; var streamType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOStream); if (streamType is null) { return; } var readExactlyMethods = streamType.GetMembers(ReadExactly) .OfType<IMethodSymbol>() .ToImmutableArray(); if (readExactlyMethods.IsEmpty) { return; } var codeAction = CodeAction.Create( AvoidUnreliableStreamReadCodeFixTitle, ct => ReplaceWithReadExactlyCall(context.Document, ct), nameof(AvoidUnreliableStreamReadCodeFixTitle)); context.RegisterCodeFix(codeAction, context.Diagnostics); async Task<Document> ReplaceWithReadExactlyCall(Document document, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var generator = editor.Generator; var arguments = invocation.Arguments.GetArgumentsInParameterOrder(); var isAsyncInvocation = invocation.TargetMethod.Name.EndsWith(Async, StringComparison.Ordinal); var methodExpression = generator.MemberAccessExpression( invocation.Instance.Syntax, isAsyncInvocation ? ReadExactlyAsync : ReadExactly); var methodInvocation = CanUseSpanOverload() ? generator.InvocationExpression( methodExpression, isAsyncInvocation && arguments.Length == 4 // Stream.ReadExactlyAsync(buffer, ct) ?[arguments[0].Syntax, arguments[3].Syntax] // Stream.ReadExactly(buffer) and Stream.ReadExactlyAsync(buffer) :[arguments[0].Syntax]) : generator.InvocationExpression( methodExpression, invocation.Arguments.Where(a => !a.IsImplicit).Select(a => a.Syntax)); editor.ReplaceNode(invocation.Syntax, methodInvocation.WithTriviaFrom(invocation.Syntax)); return document.WithSyntaxRoot(editor.GetChangedRoot()); bool CanUseSpanOverload() { return arguments.Length >= 3 && arguments[2].Value is IPropertyReferenceOperation propertyRef && propertyRef.Property.Name.Equals(WellKnownMemberNames.LengthPropertyName, StringComparison.Ordinal) && AreSameInstance(arguments[0].Value, propertyRef.Instance); } static bool AreSameInstance(IOperation? operation1, IOperation? operation2) { return (operation1, operation2) switch { (IFieldReferenceOperation fieldRef1, IFieldReferenceOperation fieldRef2) => fieldRef1.Member == fieldRef2.Member, (IPropertyReferenceOperation propRef1, IPropertyReferenceOperation propRef2) => propRef1.Member == propRef2.Member, (IParameterReferenceOperation paramRef1, IParameterReferenceOperation paramRef2) => paramRef1.Parameter == paramRef2.Parameter, (ILocalReferenceOperation localRef1, ILocalReferenceOperation localRef2) => localRef1.Local == localRef2.Local, _ => false, }; } } } } }