File: Microsoft.NetCore.Analyzers\Runtime\DoNotUseEndOfStreamInAsyncMethods.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.Collections.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.NetCore.Analyzers.Runtime
{
    using static MicrosoftNetCoreAnalyzersResources;

    /// <summary>
    /// CA2024: <inheritdoc cref="DoNotUseEndOfStreamInAsyncMethodsTitle"/>
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class DoNotUseEndOfStreamInAsyncMethodsAnalyzer : DiagnosticAnalyzer
    {
        private const string RuleId = "CA2024";
        private const string EndOfStream = nameof(EndOfStream);

        private static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
            RuleId,
            CreateLocalizableResourceString(nameof(DoNotUseEndOfStreamInAsyncMethodsTitle)),
            CreateLocalizableResourceString(nameof(DoNotUseEndOfStreamInAsyncMethodsMessage)),
            DiagnosticCategory.Reliability,
            RuleLevel.BuildWarning,
            CreateLocalizableResourceString(nameof(DoNotUseEndOfStreamInAsyncMethodsDescription)),
            isPortedFxCopRule: false,
            isDataflowRule: false);

        public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

        public sealed override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.RegisterCompilationStartAction(OnCompilationStart);
        }

        private void OnCompilationStart(CompilationStartAnalysisContext context)
        {
            var streamReaderType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOStreamReader);

            if (streamReaderType is null)
            {
                return;
            }

            var endOfStreamProperty = streamReaderType.GetMembers(EndOfStream)
                .OfType<IPropertySymbol>()
                .FirstOrDefault();

            if (endOfStreamProperty is null)
            {
                return;
            }

            context.RegisterOperationAction(AnalyzePropertyReference, OperationKind.PropertyReference);

            void AnalyzePropertyReference(OperationAnalysisContext context)
            {
                var operation = (IPropertyReferenceOperation)context.Operation;

                if (!SymbolEqualityComparer.Default.Equals(endOfStreamProperty, operation.Member))
                {
                    return;
                }

                var containingSymbol = operation.TryGetContainingAnonymousFunctionOrLocalFunction() ?? context.ContainingSymbol;

                if (containingSymbol is IMethodSymbol containingMethod && containingMethod.IsAsync)
                {
                    context.ReportDiagnostic(operation.CreateDiagnostic(Rule, operation.Syntax.ToString()));
                }
            }
        }
    }
}