File: Snippets\SnippetProviders\AbstractForLoopSnippetProvider.cs
Web Access
Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Linq;
using Microsoft.CodeAnalysis.Shared.Extensions;

namespace Microsoft.CodeAnalysis.Snippets.SnippetProviders;

internal abstract class AbstractForLoopSnippetProvider<TStatementSyntax> : AbstractInlineStatementSnippetProvider<TStatementSyntax>
    where TStatementSyntax : SyntaxNode
{
    protected sealed override bool IsValidAccessingType(ITypeSymbol type, Compilation compilation)
    {
        if (IsSuitableIntegerType(type))
        {
            return true;
        }

        var hasLengthProperty = FindLengthProperty(type, compilation) is not null;
        var hasCountProperty = FindCountProperty(type, compilation) is not null;

        // We want to allow types, which have either `Length` or `Count` property, but not both to avoid ambiguity
        return hasLengthProperty ^ hasCountProperty;
    }

    protected static bool IsSuitableIntegerType(ITypeSymbol type)
        => type.IsIntegralType() || type.IsNativeIntegerType;

    protected static IPropertySymbol? FindLengthProperty(ITypeSymbol type, Compilation compilation)
        => FindAccessibleIntegerProperty(type, compilation, "Length");

    protected static IPropertySymbol? FindCountProperty(ITypeSymbol type, Compilation compilation)
        => FindAccessibleIntegerProperty(type, compilation, "Count");

    private static IPropertySymbol? FindAccessibleIntegerProperty(ITypeSymbol type, Compilation compilation, string propertyName)
    {
        return type
            .GetAccessibleMembersInThisAndBaseTypes<IPropertySymbol>(propertyName, compilation.Assembly)
            .FirstOrDefault(p => p is { GetMethod: { } getMethod } && getMethod.IsAccessibleWithin(compilation.Assembly) && IsSuitableIntegerType(p.Type));
    }
}