Skip to content

Commit

Permalink
adds tests for TypeSyntaxExtensions (#320)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianoc committed Feb 11, 2025
1 parent 8758ab2 commit f31854e
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 6 deletions.
44 changes: 44 additions & 0 deletions Cecilifier.Core.Tests/Tests/Unit/TypeSyntaxExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Linq;
using Cecilifier.Core.Extensions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NUnit.Framework;

namespace Cecilifier.Core.Tests.Tests.Unit;

[TestFixture]
public class TypeSyntaxExtensionsTests
{

[TestCase("class C { int underTest; }", "int", TestName = nameof(PredefinedTypeSyntax))]
[TestCase("unsafe class C { int* underTest; }", "int", TestName = "Predefined Pointer Type")]
[TestCase("class C { int[] underTest; }", "int", TestName = nameof(ArrayTypeSyntax))]
[TestCase("class C<T> { T underTest; }", "T", TestName = nameof(TypeParameterSyntax))]
[TestCase("class C { System.Action underTest; }", "System.Action", TestName = nameof(QualifiedNameSyntax))]
[TestCase("class C { int? underTest; }", "int", TestName = nameof(NullableTypeSyntax))]
[TestCase("class C { (int, string) underTest; }", "(int, string)", TestName = nameof(TupleTypeSyntax))]
[TestCase("ref struct C { ref System.Span<int> underTest; }", "System.Span<int>", TestName = nameof(RefTypeSyntax))]
[TestCase("unsafe class C { delegate*<int,void> underTest; }", "delegate*<int,void>", TestName = nameof(FunctionPointerTypeSyntax))]
[TestCase("using Foo=System; class C { Foo::String underTest; }", "Foo::String", TestName = nameof(AliasQualifiedNameSyntax))]
[TestCase("using System; class C { Action<int> underTest; }", "Action<int>", TestName = nameof(GenericNameSyntax))]
public void TestForCommonNodes(string code, string expected)
{
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var fields = syntaxTree.GetRoot().DescendantNodes();
var field = syntaxTree.GetRoot().DescendantNodes().OfType<FieldDeclarationSyntax>().Single();

var name = field.Declaration.Type.NameFrom();
Assert.That(name, Is.EqualTo(expected));
}

[Test]
public void OmittedTypeArgumentSyntaxTest()
{
var testCode = "var t = typeof(System.Action<>);";
var syntaxTree = CSharpSyntaxTree.ParseText(testCode);
var position = testCode.IndexOf("<>");
var type = syntaxTree.GetRoot().FindToken(position).Parent.DescendantNodes().OfType<OmittedTypeArgumentSyntax>().Single();

Assert.That(type.NameFrom(), Is.EqualTo("Action<>"));
}
}
18 changes: 12 additions & 6 deletions Cecilifier.Core/Extensions/TypeSyntaxExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Cecilifier.Core.Extensions;

internal static class TypesExtensions
internal static class TypeSyntaxExtensions
{
public static string NameFrom(this TypeSyntax type, bool expandAttributeName = false)
{
// Note that we don´t expect `type` to ever be a `SimpleNameSyntax` since this type is abstract.
return type switch
{
ArrayTypeSyntax arrayTypeSyntax => NameFrom(arrayTypeSyntax.ElementType),
AliasQualifiedNameSyntax aliasQualifiedNameSyntax => throw new NotImplementedException(),
AliasQualifiedNameSyntax aliasQualifiedNameSyntax => aliasQualifiedNameSyntax.ToString(),
FunctionPointerTypeSyntax functionPointerTypeSyntax => functionPointerTypeSyntax.ToString(),
GenericNameSyntax genericNameSyntax => NameFromIdentifier(genericNameSyntax.Identifier, expandAttributeName),
IdentifierNameSyntax identifierNameSyntax => NameFromIdentifier(identifierNameSyntax.Identifier, expandAttributeName),
QualifiedNameSyntax qualifiedNameSyntax => qualifiedNameSyntax.ToString(),
SimpleNameSyntax simpleNameSyntax => simpleNameSyntax.Identifier.Text,
NullableTypeSyntax nullableTypeSyntax => NameFrom(nullableTypeSyntax.ElementType),
OmittedTypeArgumentSyntax omittedTypeArgumentSyntax => throw new NotImplementedException(),
OmittedTypeArgumentSyntax omittedTypeArgumentSyntax => omittedTypeArgumentSyntax.Parent?.Parent?.ToString(),
PointerTypeSyntax pointerTypeSyntax => NameFrom(pointerTypeSyntax.ElementType),
PredefinedTypeSyntax predefinedTypeSyntax => predefinedTypeSyntax.Keyword.Text,
RefTypeSyntax refTypeSyntax => NameFrom(refTypeSyntax.Type),
TupleTypeSyntax tupleTypeSyntax => tupleTypeSyntax.ToString(),

_ => throw new InvalidOperationException($"Unexpected syntax: {type}"),
_ => ThrowCannotHappen()
};

[ExcludeFromCodeCoverage]
string ThrowCannotHappen()
{
throw new InvalidOperationException($"Unexpected syntax: {type} ({type.GetType().Name})");
}

static string NameFromIdentifier(SyntaxToken identifierToken, bool expandAttributeName)
{
var typeName = identifierToken.Text;
Expand Down

0 comments on commit f31854e

Please sign in to comment.