Skip to content

Commit

Permalink
Merge pull request #168 from nenoNaninu/update_analyzer_to_support_ge…
Browse files Browse the repository at this point in the history
…neric

Update analyzer to support generic
  • Loading branch information
nenoNaninu committed Feb 13, 2024
2 parents 082cc01 + a608bf1 commit d7b987e
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 47 deletions.
25 changes: 7 additions & 18 deletions src/Tapper.Analyzer/TapperAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@ public class TapperAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
description: "Some members are not a named type.");

private static readonly DiagnosticDescriptor GenericTypeProhibitionRule = new DiagnosticDescriptor(
id: "TAPP003",
title: "It is prohibited to apply the TranspilationSourceAttribute to generic types",
messageFormat: "It is prohibited to apply the TranspilationSourceAttribute to generic types. {0} is generic type.",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "It is prohibited to apply the TranspilationSourceAttribute to generic types.");

private static readonly DiagnosticDescriptor UnsupportedTypeRule = new DiagnosticDescriptor(
id: "TAPP004",
title: "Unsupported type",
Expand All @@ -46,7 +37,7 @@ public class TapperAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
description: "Unsupported type.");

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AnnotationRule, NamedTypeRule, GenericTypeProhibitionRule, UnsupportedTypeRule);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AnnotationRule, NamedTypeRule, UnsupportedTypeRule);

public override void Initialize(AnalysisContext context)
{
Expand Down Expand Up @@ -124,13 +115,6 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)
return;
}

if (namedTypeSymbol.IsGenericType)
{
context.ReportDiagnostic(Diagnostic.Create(
GenericTypeProhibitionRule, namedTypeSymbol.Locations[0], namedTypeSymbol.ToDisplayString()));
return;
}

foreach (var member in namedTypeSymbol.GetPublicFieldsAndProperties().IgnoreStatic())
{
if (member.GetAttributes().Any(x =>
Expand All @@ -142,6 +126,11 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)

foreach (var type in member.GetRelevantTypesFromMemberSymbol())
{
if (type is ITypeParameterSymbol)
{
continue;
}

if (type is not INamedTypeSymbol memberNamedTypeSymbol)
{
context.ReportDiagnostic(Diagnostic.Create(
Expand Down Expand Up @@ -176,7 +165,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)
}

context.ReportDiagnostic(Diagnostic.Create(
AnnotationRule, member.Locations[0], type.ToDisplayString()));
AnnotationRule, member.Locations[0], sourceType.ToDisplayString()));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Tapper/DefaultTypeMapperProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public ITypeMapper GetTypeMapper(ITypeSymbol type)
return _genericTypeParameterMapper;
}

var sourceType = type is INamedTypeSymbol namedTypeSymbol
var sourceType = type is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType
? namedTypeSymbol.ConstructedFrom
: type;

Expand Down
31 changes: 15 additions & 16 deletions src/Tapper/TypeTranslators/DefaultMessageTypeTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public void Translate(ref CodeWriter codeWriter, INamedTypeSymbol typeSymbol, IT
.ToArray();


codeWriter.Append($"/** Transpiled from {typeSymbol.OriginalDefinition.ToDisplayString()} */{newLineString}");
codeWriter.Append($"export type {MessageTypeTranslatorHelper.GetGenericTypeName(typeSymbol)} = {{{newLineString}");
codeWriter.Append($"/** Transpiled from {typeSymbol.ToDisplayString()} */{newLineString}");
codeWriter.Append($"export type {MessageTypeTranslatorHelper.GetTypeDefinitionString(typeSymbol)} = {{{newLineString}");

foreach (var member in members)
{
Expand All @@ -44,7 +44,7 @@ public void Translate(ref CodeWriter codeWriter, INamedTypeSymbol typeSymbol, IT

if (MessageTypeTranslatorHelper.IsSourceType(typeSymbol.BaseType, options))
{
codeWriter.Append($" & {MessageTypeTranslatorHelper.GetConcreteTypeName(typeSymbol.BaseType, options)};");
codeWriter.Append($" & {MessageTypeTranslatorHelper.GetConstructedTypeName(typeSymbol.BaseType, options)};");
}

codeWriter.Append(newLineString);
Expand All @@ -53,31 +53,30 @@ public void Translate(ref CodeWriter codeWriter, INamedTypeSymbol typeSymbol, IT

file static class MessageTypeTranslatorHelper
{
public static string GetConcreteTypeName(INamedTypeSymbol typeSymbol, ITranspilationOptions options)
public static string GetTypeDefinitionString(INamedTypeSymbol typeSymbol)
{
var genericTypeArguments = "";
if (typeSymbol.IsGenericType)
{
var mappedGenericTypeArguments = typeSymbol.TypeArguments.Select(typeArg =>
{
var mapper = options.TypeMapperProvider.GetTypeMapper(typeArg);
return mapper.MapTo(typeArg, options);
});
genericTypeArguments = $"<{string.Join(", ", mappedGenericTypeArguments)}>";
return $"{typeSymbol.Name}<{string.Join(", ", typeSymbol.TypeParameters.Select(param => param.Name))}>";
}

return $"{typeSymbol.Name}{genericTypeArguments}";
return typeSymbol.Name;
}

public static string GetGenericTypeName(INamedTypeSymbol typeSymbol)
public static string GetConstructedTypeName(INamedTypeSymbol typeSymbol, ITranspilationOptions options)
{
var genericTypeParameters = "";
if (typeSymbol.IsGenericType)
{
genericTypeParameters = $"<{string.Join(", ", typeSymbol.TypeParameters.Select(param => param.Name))}>";
var mappedGenericTypeArguments = typeSymbol.TypeArguments.Select(typeArg =>
{
var mapper = options.TypeMapperProvider.GetTypeMapper(typeArg);
return mapper.MapTo(typeArg, options);
});

return $"{typeSymbol.Name}<{string.Join(", ", mappedGenericTypeArguments)}>";
}

return $"{typeSymbol.Name}{genericTypeParameters}";
return typeSymbol.Name;
}

public static (ITypeSymbol TypeSymbol, bool IsNullable) GetMemberTypeSymbol(ISymbol symbol, ITranspilationOptions options)
Expand Down
90 changes: 79 additions & 11 deletions tests/Tapper.Tests.Analyzer/AnalyzerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,32 +58,100 @@ unsafe class NotNamedType
}

[Fact]
public async Task Test_TAPP003()
public async Task Test_TAPP004()
{
var code = @"using Tapper;
using System;
[TranspilationSource]
class Generics<T>
class NotSupport
{
public T Value { get; set; }
}";
public Func<int, int>? GetPointer;
}
";

await VerifyAsync(code, "TAPP003", 4, 7, 4, 15);
await VerifyAsync(code, "TAPP004", 7, 28, 7, 38);
}

[Fact]
public async Task Test_TAPP004()
public async Task Test_GenericType0()
{
var code = @"using Tapper;
var code = """
using Tapper;
using System;

public class GenericClass1<T>
{
public string StringProperty { get; set; }
public T GenericProperty { get; set; }
}

[TranspilationSource]
class NotSupport
public class NestedGenericClass<T1, T2>
{
public Func<int, int>? GetPointer;
public string StringProperty { get; set; }
public T1 GenericProperty { get; set; }
public GenericClass1<T1> GenericClass1Property { get; set; }
}
";
""";

await VerifyAsync(code, "TAPP004", 7, 28, 7, 38);
await VerifyAsync(code, "TAPP001", 15, 30, 15, 51);
}

[Fact]
public async Task Test_GenericType1()
{
var code = """
using Tapper;
using System;

public class MyType
{
}

[TranspilationSource]
public class GenericClass1<T>
{
public MyType MyType { get; set; }
public T GenericProperty { get; set; }
}
""";

await VerifyAsync(code, "TAPP001", 11, 19, 11, 25);
}

[Fact]
public async Task Test_GenericType2()
{
var code = """
using Tapper;
using System;

[TranspilationSource]
public class GenericClass1<T>
{
public string StringProperty { get; set; }
public T GenericProperty { get; set; }
}

public class NestedGenericClass<T1, T2>
{
public string StringProperty { get; set; }
public T1 GenericProperty { get; set; }
public GenericClass1<T1> GenericClass1Property { get; set; }
}

[TranspilationSource]
public class DeeplyNestedGenericClass<A, B, C>
{
public string StringProperty { get; set; }
public A GenericPropertyA { get; set; }
public B GenericPropertyB { get; set; }
public GenericClass1<A> GenericClass1Property { get; set; }
public NestedGenericClass<string, B> NestedGenericClassProperty { get; set; }
}
""";

await VerifyAsync(code, "TAPP001", 25, 42, 25, 68);
}
}
2 changes: 1 addition & 1 deletion tests/Tapper.Tests.Analyzer/Tapper.Tests.Analyzer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<LangVersion>11</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit d7b987e

Please sign in to comment.