0

I am trying to use Roslyn analyzers to find all instances of using .ToString() on an enum value so that I can suggest a codefix to use the nameof() expression instead. How can I go about doing this? I've trudged through it for the string interpolation scenario in what feels like an overly complicated way, but still need to do it for the other scenarios listed below.

public void PrintStuffOut() {
    //all below lines should flag to suggest using nameof(Foo.Bar)
    Console.WriteLine(Foo.Bar);
    Console.WriteLine(Foo.Bar.ToString());
    Console.WriteLine($"Your enum is {Foo.Bar}");
    Console.WriteLine($"Your enum is {Foo.Bar.ToString()}");
    Console.WriteLine("Your enum is " + Foo.Bar);
    Console.WriteLine("Your enum is " + Foo.Bar.ToString());
}

public enum Foo {
    Bar
}

Here's what I have so far. This works, but surely this can be done far better


public override void Initialize(AnalysisContext context) {
    context.EnableConcurrentExecution();
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

    context.RegisterSyntaxNodeAction(AnalyzeInterpolatedString, SyntaxKind.InterpolatedStringExpression);
}

private void AnalyzeInterpolatedString(SyntaxNodeAnalysisContext ctx) {
    var interpolationSyntax = (InterpolatedStringExpressionSyntax)ctx.Node;
    foreach (var interpolatedChunk in interpolationSyntax.Contents.OfType<InterpolationSyntax>()) {
        var expTypeInfo = ctx.SemanticModel.GetTypeInfo(interpolatedChunk.Expression);
        //this if covers the case of when there is an explicit .ToString() on the enum's value in the string interpolation. Feels super hacky.
        if (expTypeInfo.Type != null && expTypeInfo.Type.SpecialType == SpecialType.System_String) {
            var childrenExpressions = interpolatedChunk.Expression.ChildNodes().OfType<MemberAccessExpressionSyntax>();
            foreach (var childExpression in childrenExpressions) {
                var childExpTypeInfo = ctx.SemanticModel.GetTypeInfo(childExpression.Expression);

                if (childExpTypeInfo.Type != null && childExpTypeInfo.Type.TypeKind == TypeKind.Enum) {
                    //super hacky way to get the enum name and value. I need to report back EnumName and Value in the example of EnumName.Value.ToString()
                    ctx.ReportDiagnostic(Diagnostic.Create(Descriptors.UseNameOfForEnum, interpolationSyntax.GetLocation(), interpolatedChunk.Expression.ToString().Split('.').Take(2).ToArray()));
                }
            }
        }
        //the else if here covers when there is no explicit .ToString() in the interpolated string
        else if (expTypeInfo.Type != null && expTypeInfo.Type.TypeKind == TypeKind.Enum) {
            //super hacky way to get the enum name and value. I need to report back EnumName and Value in the example of EnumName.Value.ToString()
            ctx.ReportDiagnostic(Diagnostic.Create(Descriptors.UseNameOfForEnum, interpolationSyntax.GetLocation(), interpolatedChunk.Expression.ToString().Split('.')));
        }
    }
}

I feel like I should be able to do something by registering an action on SyntaxKind.InvocationExpression, checking that the method being invoked is .ToString(), and that it's being invoked on an enum value. Is it possible to do it this way? Would that catch the scenarios above where I'm not explicitly using .ToString()? If not, what's the best way to go about accomplishing this?

Duu82
  • 157
  • 2
  • 11
  • Aside: I haven't had the opportunity to use it yet, but isn't `nameof` returning the name of the enum _type_ (as opposed to the current value)? – 500 - Internal Server Error Oct 05 '21 at 20:12
  • 1
    Check out how https://github.com/Microsoft/RoslynClrHeapAllocationAnalyzer is doing it. – Paulo Morgado Oct 06 '21 at 11:39
  • @500-InternalServerError - No. `nameof(Enum.Value)` will return `Value`, which is the same as `Enum.Value.ToString()` @PauloMorgado - The only thing I found of possible use in there is https://github.com/microsoft/RoslynClrHeapAllocationAnalyzer/blob/master/ClrHeapAllocationsAnalyzer/ConcatenationAllocationAnalyzer.cs, but I'm not seeing much value gained from it. – Duu82 Oct 06 '21 at 12:43
  • @PauloMorgado - Comments can only be edited within 5 minutes of posting. :) EDIT: Thank you for pointing me in that direction. I looked more at the analyzer in the link I posted above and I have already adapted it to get me 95% of the way there for the string concatenation I think. Do you know of any other examples to point me in the right direction for the other scenarios? – Duu82 Oct 06 '21 at 13:05

0 Answers0