File: src\Analyzers\CSharp\Analyzers\UseCollectionExpression\AbstractCSharpUseCollectionExpressionDiagnosticAnalyzer.cs
Web Access
Project: src\src\CodeStyle\CSharp\Analyzers\Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.UseCollectionInitializer;
 
namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression;
 
/// <summary>
/// Base type for all analyzers that offer to update code to use a collection-expression.
/// </summary>
internal abstract class AbstractCSharpUseCollectionExpressionDiagnosticAnalyzer
    : AbstractBuiltInCodeStyleDiagnosticAnalyzer
{
    public static readonly ImmutableDictionary<string, string?> ChangesSemantics =
        ImmutableDictionary<string, string?>.Empty.Add(UseCollectionInitializerHelpers.ChangesSemanticsName, "");
 
    protected readonly DiagnosticDescriptor UnnecessaryCodeDescriptor;
 
    protected AbstractCSharpUseCollectionExpressionDiagnosticAnalyzer(string diagnosticId, EnforceOnBuild enforceOnBuild)
        : base(
            [
                (CreateDescriptor(diagnosticId, enforceOnBuild, isUnnecessary: false), CodeStyleOptions2.PreferCollectionExpression)
            ])
    {
        UnnecessaryCodeDescriptor = CreateDescriptor(diagnosticId, enforceOnBuild, isUnnecessary: true);
    }
 
    private static DiagnosticDescriptor CreateDescriptor(string diagnosticId, EnforceOnBuild enforceOnBuild, bool isUnnecessary)
        => CreateDescriptorWithId(
            diagnosticId,
            enforceOnBuild,
            hasAnyCodeStyleOption: true,
            new LocalizableResourceString(nameof(AnalyzersResources.Simplify_collection_initialization), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)),
            new LocalizableResourceString(nameof(AnalyzersResources.Collection_initialization_can_be_simplified), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)),
            isUnnecessary: isUnnecessary);
 
    protected abstract void InitializeWorker(CodeBlockStartAnalysisContext<SyntaxKind> context, INamedTypeSymbol? expressionType);
 
    protected virtual bool IsSupported(Compilation compilation)
        => true;
 
    public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory()
        => DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
 
    protected sealed override void InitializeWorker(AnalysisContext context)
        => context.RegisterCompilationStartAction(context =>
        {
            var compilation = context.Compilation;
            if (!compilation.LanguageVersion().SupportsCollectionExpressions())
                return;
 
            if (!IsSupported(compilation))
                return;
 
            // We wrap the SyntaxNodeAction within a CodeBlockStartAction, which allows us to get callbacks for object
            // creation expression nodes, but analyze nodes across the entire code block and eventually report fading
            // diagnostics with location outside this node. Without the containing CodeBlockStartAction, our reported
            // diagnostic would be classified as a non-local diagnostic and would not participate in lightbulb for
            // computing code fixes.
            var expressionType = compilation.ExpressionOfTType();
            context.RegisterCodeBlockStartAction<SyntaxKind>(context => InitializeWorker(context, expressionType));
        });
}