Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check with Roslyn if a Type is a Nullable Reference

I am trying to analyze with roslyn if a type declaration is a "Nullable Reference" type (C#8)

I was planing on looking if the TypeSyntex was a NullableTypeSyntax and if the ITypeSymbol.IsReferenceType is true.

The following code works for the most part, but is fails when the declared type is generic i.e. List<T>?

void Main()
{
    string text = @"
        #nullable enable
        public class MyClass
        {
            public string? Get() => null;
            public List<string>? GetGeneric() => null;
        }";
    SyntaxTree tree = CSharpSyntaxTree.ParseText(text);
    PortableExecutableReference mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
    CSharpCompilation compilation = CSharpCompilation.Create("RefitCompilation", syntaxTrees: new[] { tree }, references: new[] { mscorlib });
    SemanticModel semanticModel = compilation.GetSemanticModel(tree);

    MethodDeclarationSyntax nonGenericMethodSyntax = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First();
    ITypeSymbol nonGenericReturnType = semanticModel.GetTypeInfo(nonGenericMethodSyntax.ReturnType).Type;
    bool isNullableTypeReference = nonGenericMethodSyntax.ReturnType is NullableTypeSyntax && nonGenericReturnType.IsReferenceType;
    Console.WriteLine($@"NonGeneric Nullalbe Reference: `{nonGenericMethodSyntax}`
        Is Nullable Type Reference: {isNullableTypeReference}
        Original Definition: {nonGenericReturnType.OriginalDefinition}, 
        IsNullableTypeSyntax: {nonGenericMethodSyntax.ReturnType is NullableTypeSyntax}
        Is Reference Type: {nonGenericReturnType.IsReferenceType}");

    Console.WriteLine();

    MethodDeclarationSyntax genericMethodSyntax = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Last();
    ITypeSymbol genericReturnType = semanticModel.GetTypeInfo(genericMethodSyntax.ReturnType).Type;
    isNullableTypeReference = genericMethodSyntax.ReturnType is NullableTypeSyntax && genericReturnType.IsReferenceType;
    Console.WriteLine($@"Generic Nullalbe Reference: `{genericMethodSyntax}`
        Is Nullable Type Reference: {isNullableTypeReference}
        Original Definition: {genericReturnType.OriginalDefinition}, 
        IsNullableTypeSyntax: {genericMethodSyntax.ReturnType is NullableTypeSyntax}
        Is Reference Type: {genericReturnType.IsReferenceType}");
}

which outputs

NonGeneric Nullalbe Reference: `public string? Get() => null;`
        Is Nullable Type Reference: True
        Original Definition: string, 
        IsNullableTypeSyntax: True
        Is Reference Type: True

Generic Nullalbe Reference: `public List<string>? GetGeneric() => null;`
        Is Nullable Type Reference: False
        Original Definition: System.Nullable<T>, 
        IsNullableTypeSyntax: True
        Is Reference Type: False

Why is List<T>? Original Definition System.Nullable<T>? and how can I determine if a type is a Nullable Reference type?

like image 798
Joel Weiss Avatar asked Nov 29 '19 01:11

Joel Weiss


People also ask

Why do we need nullable and non-nullable reference types?

Nullable reference types are helpful, in that they help find code that may dereference null and help guard it with null checks. Making current reference types Non-nullable is helpful in that it helps prevent variables from inadvertently containing an null value.

How to determine if an expression is nullable or not?

If a reference type T is the result of type inference, and at least one of the candidate expressions is nullable, then the result is nullable too. If an expression is by declaration of type T?, it will still be considered to be of type T if it occurs in a context where by flow analysis we consider it known that it is not null.

What is the default state of a nullable reference?

In a nullable enabled context, the compiler performs static analysis on variables of any reference type, both nullable and non-nullable. The compiler tracks the null-state of each reference variable as either not-null or maybe-null. The default state of a non-nullable reference is not-null. The default state of a nullable reference is maybe-null.

What is the difference between notnull and nullable in JavaScript?

The variables notNull and nullable are both represented by the String type. Because the non-nullable and nullable types are both stored as the same type, there are several locations where using a nullable reference type isn't allowed.


1 Answers

This is not a complete answer but I had the same issue and this is my interpretation of things.

In

string text = @"
    #nullable enable
    public class MyClass
    {
        public string? Get() => null;
        public List<string>? GetGeneric() => null;
    }";

the List<string>? is invalid, the System.Collections.Generic using is missing, therefore it is not possible to determine the type of List<string>.

What happens next is that the unknown List<T>? type is interpreted as nullable struct so we arrive at Nullable<List<string>>.

This case can be checked by interrogating the Nullable for its underlying type and checking its TypeKind, i.e.

((INamedTypeSymbol)type).TypeArguments[0].TypeKind

will yield Error.

like image 89
Zdeněk Jelínek Avatar answered Oct 04 '22 11:10

Zdeněk Jelínek