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?
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.
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.
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.
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With