In the past we have covered working with types in your analyzer. We even built an analyzer that looked at generic types to work with dictionaries that have string keys to ensure they specified a string comparer. Since we already have that built, let's modify the existing analyzer to handle the ToDictionary call on an IEnumerable.

Let's start with the existing code for the analyzer:

public override void Initialize(AnalysisContext context)
{
    context.RegisterCompilationStartAction(compilationContext =>
    {
        var dictionaryTokenType = compilationContext.Compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2");
        var equalityComparerInterfaceType = compilationContext.Compilation.GetTypeByMetadataName("System.Collections.Generic.IEqualityComparer`1");

        if (dictionaryTokenType != null)
        {
            compilationContext.RegisterSyntaxNodeAction(symbolContext =>
            {
                var creationNode = (ObjectCreationExpressionSyntax)symbolContext.Node;
                var variableTypeInfo = symbolContext.SemanticModel.GetTypeInfo(symbolContext.Node).ConvertedType as INamedTypeSymbol;

                if (variableTypeInfo == null)
                    return;

                if (!variableTypeInfo.OriginalDefinition.Equals(dictionaryTokenType))
                    return;

                // We only care about dictionaries who use a string as the key
                if (variableTypeInfo.TypeArguments.First().SpecialType != SpecialType.System_String)
                    return;

                var arguments = creationNode.ArgumentList?.Arguments;

                if (arguments == null || arguments.Value.Count == 0)
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Node.GetLocation()));
                    return;
                }

                bool hasEqualityComparer = false;
                foreach (var argument in arguments)
                {
                    var argumentType = symbolContext.SemanticModel.GetTypeInfo(argument.Expression);

                    if (argumentType.ConvertedType == null)
                        return;

                    if (argumentType.ConvertedType.OriginalDefinition.Equals(equalityComparerInterfaceType))
                    {
                        hasEqualityComparer = true;
                        break;
                    }
                }

                if (!hasEqualityComparer)
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Node.GetLocation()));
                }
            }, SyntaxKind.ObjectCreationExpression);
        }
    });
}

In order to modify this code, we need to do a few things. First, we need to change from using ObjectCreatoinExpressoin to InvocationExpression. Simply modify the syntax kind on the RegisterSyntaxNodeAction and update the cast to use an InvocationExpressionSyntax (Note, I also changed the variable name for clarity):

compilationContext.RegisterSyntaxNodeAction(symbolContext =>
{
    var invocationNode = (InvocationExpressionSyntax)symbolContext.Node;
    /* rest of the analyzer */
}, SyntaxKind.InvocationExpression);

With that done, you can now run the code and it will catch all ToDictionary calls that return a Dictionary<string, X>, unfortunately, it will also catch any other method calls that happen to return a dictionary with a string key. To fix this, we need to check the method invocation to make sure it is a ToDictionary call. To do this, we can access the expression from the invocationNode and check the identifier text to see if is ToDictionary:

var expression = invocationNode.Expression as MemberAccessExpressionSyntax;
var name = expression?.Name.Identifier.Text;

if (name == null || !name.Equals("ToDictionary"))
{
    return;
}

That's it. The full code for the analyzer is:

public override void Initialize(AnalysisContext context)
{
    context.RegisterCompilationStartAction(compilationContext =>
    {
        var dictionaryTokenType = compilationContext.Compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2");
        var equalityComparerInterfaceType = compilationContext.Compilation.GetTypeByMetadataName("System.Collections.Generic.IEqualityComparer`1");

        if (dictionaryTokenType != null)
        {
            compilationContext.RegisterSyntaxNodeAction(symbolContext =>
            {
                var invocationNode = (InvocationExpressionSyntax)symbolContext.Node;
                var variableTypeInfo = symbolContext.SemanticModel.GetTypeInfo(symbolContext.Node).ConvertedType as INamedTypeSymbol;

                if (variableTypeInfo == null)
                    return;

                if (!variableTypeInfo.OriginalDefinition.Equals(dictionaryTokenType))
                    return;

                // We only care about dictionaries who use a string as the key
                if (variableTypeInfo.TypeArguments.First().SpecialType != SpecialType.System_String)
                    return;

                var expression = invocationNode.Expression as MemberAccessExpressionSyntax;
                var name = expression?.Name.Identifier.Text;

                if (name == null || !name.Equals("ToDictionary"))
                {
                    return;
                }

                var arguments = invocationNode.ArgumentList?.Arguments;

                if (arguments == null || arguments.Value.Count == 0)
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Node.GetLocation()));
                    return;
                }

                bool hasEqualityComparer = false;
                foreach (var argument in arguments)
                {
                    var argumentType = symbolContext.SemanticModel.GetTypeInfo(argument.Expression);

                    if (argumentType.ConvertedType == null)
                        return;

                    if (argumentType.ConvertedType.OriginalDefinition.Equals(equalityComparerInterfaceType))
                    {
                        hasEqualityComparer = true;
                        break;
                    }
                }

                if (!hasEqualityComparer)
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Node.GetLocation()));
                }
            }, SyntaxKind.InvocationExpression);
        }
    });
}

That is all you need to update to get a functioning analyzer that will handle the ToDictionary calls on an IEnumerable to ensure that you have dictionaries that explicitly define their string comparers. Obviously, this analyzer isn't perfect as it technically catches any method called ToDictionary regardless if the method is the IEnumerable ToDictionary or some other ToDictionary. I'll leave that fix as an exercise for the reader :).