Reputation: 148
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?
Upvotes: 10
Views: 2132
Reputation: 1045
symbol.type.NullableAnnotation will give whether the expression is annotated or not
Upvotes: -1
Reputation: 2923
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
.
Upvotes: 1