File: src\Analyzers\CSharp\Analyzers\UseCollectionInitializer\CSharpUseCollectionInitializerDiagnosticAnalyzer.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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.UseCollectionExpression;
using Microsoft.CodeAnalysis.UseCollectionInitializer;
 
namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer;
 
using static SyntaxFactory;
 
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpUseCollectionInitializerDiagnosticAnalyzer :
    AbstractUseCollectionInitializerDiagnosticAnalyzer<
        SyntaxKind,
        ExpressionSyntax,
        StatementSyntax,
        BaseObjectCreationExpressionSyntax,
        MemberAccessExpressionSyntax,
        InvocationExpressionSyntax,
        ExpressionStatementSyntax,
        LocalDeclarationStatementSyntax,
        VariableDeclaratorSyntax,
        CSharpUseCollectionInitializerAnalyzer>
{
    protected override ISyntaxFacts SyntaxFacts
        => CSharpSyntaxFacts.Instance;
 
    protected override CSharpUseCollectionInitializerAnalyzer GetAnalyzer()
        => CSharpUseCollectionInitializerAnalyzer.Allocate();
 
    protected override bool AreCollectionInitializersSupported(Compilation compilation)
        => compilation.LanguageVersion() >= LanguageVersion.CSharp3;
 
    protected override bool AreCollectionExpressionsSupported(Compilation compilation)
        => compilation.LanguageVersion().SupportsCollectionExpressions();
 
    protected override bool CanUseCollectionExpression(
        SemanticModel semanticModel,
        BaseObjectCreationExpressionSyntax objectCreationExpression,
        INamedTypeSymbol? expressionType,
        ImmutableArray<CollectionMatch<SyntaxNode>> preMatches,
        bool allowSemanticsChange,
        CancellationToken cancellationToken,
        out bool changesSemantics)
    {
        // Synthesize the final collection expression we would replace this object-creation with.  That will allow us to
        // determine if we end up calling the right overload in cases of overloaded methods.
        var replacement = CollectionExpression(SeparatedList(
            GetMatchElements(preMatches).Concat(GetInitializerElements(objectCreationExpression.Initializer))));
 
        return UseCollectionExpressionHelpers.CanReplaceWithCollectionExpression(
            semanticModel, objectCreationExpression, replacement, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out changesSemantics);
 
        static IEnumerable<CollectionElementSyntax> GetMatchElements(ImmutableArray<CollectionMatch<SyntaxNode>> preMatches)
        {
            foreach (var match in preMatches)
            {
                if (match.Node is ExpressionSyntax expression)
                    yield return match.UseSpread ? SpreadElement(expression) : ExpressionElement(expression);
            }
        }
 
        static IEnumerable<CollectionElementSyntax> GetInitializerElements(InitializerExpressionSyntax? initializer)
        {
            if (initializer != null)
            {
                foreach (var expression in initializer.Expressions)
                    yield return ExpressionElement(expression);
            }
        }
    }
 
    protected override bool IsValidContainingStatement(StatementSyntax node)
    {
        // We don't want to offer this for using declarations because the way they are lifted means all
        // initialization is done before entering try block. For example
        // 
        // using var c = new List<int>() { 1 };
        //
        // is lowered to:
        //
        // var __c = new List<int>();
        // __c.Add(1);
        // var c = __c;
        // try
        // {
        // }
        // finally
        // {
        //     if (c != null)
        //     {
        //         ((IDisposable)c).Dispose();
        //     }
        // }
        //
        // As can be seen, if initializing throws any kind of exception, the newly created instance will not
        // be disposed properly.
        return node is not LocalDeclarationStatementSyntax localDecl ||
            localDecl.UsingKeyword == default;
    }
}