Search code examples
roslynroslyn-code-analysis

Roslyn Analyzer Object initializer


I'd like to have an analyzer detect all calls to setter properties not inside an object initializer of an object implementing a specific interfaces. I'm a bit lost of how to detect that, the docs are a bit thin. I can get an invocation expression, but how to check if it's inside an object initializer?

Any ideas?


Solution

  • To know if any particular code is inside an object initializer, you can simply look up the ancestors for any node that is of type InitializerExpressionSyntax.

    var initializer = node.Ancestors().OfType<InitializerExpressionSyntax>.FirstOrDefault();
    

    To know if any particular code is an assignment to a property setter, you'll have to do a bit more work. You'll need to ask the SemanticModel for the symbol being assigned for an AssignmentExpressionSyntax. Your analyzer should have access to the correct SemanticModel and SyntaxTree from its arguments/context.

    SemanticModel model = ...;
    AssignmentExpressionSyntax assignment = ...; // find the assignment
    var symbol = model.GetSymbolInfo(assignment).Symbol as IMethodSymbol;
    if (symbol?.MethodKind == MethodKind.PropertySet) { ... }
    

    To know if the object implements a particular interface you will need to find the symbol for the object. You might be able to find it by looking up the containing symbol chain of the property setter symbol. You can also find the symbol for the object by finding the ObjectCreationExpressionSyntax that should be the parent or ancestor of the InitializerExpressionSyntax you already have.

    Once you have that creation node you can ask the SemanticModel again. Use the GetTypeInfo method to get the type of the expression (the type/symbol being constructed) and not the symbol for the constructor.

    var creation = initializer.Ancestors().OfType<ObjectCreationSyntax>().FirstOrDefault();
    var createdType = model.GetTypeInfo(creation).Type as INamedTypeSymbol;
    

    Now, you just need to know if the type implements the interface.

    First you will need a symbol for the interface type. One quick way to get that is to look it up using its CLR metadata name.

    var interfaceType = model.Compilation.GetTypeByMetadataName("MyNamspace.MyInterfaceType");
    

    This part is usually done once at the initialization of the analyzer, so you don't have to keep looking it up, over and over again.

    Now you have everything you need to discover if the type being constructed implements the interface.

    if (createdType.AllInterfaces.Contains(interfaceType)) { ... }