From 0ad89b0d6b9b13c28d300db40c4efae325c814d4 Mon Sep 17 00:00:00 2001 From: Mohammadreza Taikandi Date: Fri, 2 Jul 2021 09:12:11 +0100 Subject: [PATCH] Use fully qualified name of the source type instead of adding usings if necessary. --- src/MapTo/MappingContext.cs | 32 ++++-- src/MapTo/Models.cs | 2 +- test/MapTo.Tests/MappedClassesTests.cs | 136 +++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 8 deletions(-) diff --git a/src/MapTo/MappingContext.cs b/src/MapTo/MappingContext.cs index 259cddc..9981bd9 100644 --- a/src/MapTo/MappingContext.cs +++ b/src/MapTo/MappingContext.cs @@ -11,8 +11,11 @@ namespace MapTo { internal abstract class MappingContext { + private readonly List _ignoredNamespaces; + protected MappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax) { + _ignoredNamespaces = new(); Diagnostics = ImmutableArray.Empty; Usings = ImmutableArray.Create("System", Constants.RootNamespace); SourceGenerationOptions = sourceGenerationOptions; @@ -77,7 +80,10 @@ namespace MapTo protected void AddUsingIfRequired(bool condition, string? ns) { - if (condition && ns is not null && ns != TypeSyntax.GetNamespace() && !Usings.Contains(ns)) + if (condition && ns is not null && + ns != TypeSyntax.GetNamespace() && + !_ignoredNamespaces.Contains(ns) && + !Usings.Contains(ns)) { Usings = Usings.Add(ns); } @@ -145,7 +151,6 @@ namespace MapTo } AddUsingIfRequired(propertyType); - AddUsingIfRequired(sourceTypeSymbol); AddUsingIfRequired(enumerableTypeArgumentType); AddUsingIfRequired(mappedSourcePropertyType); @@ -155,8 +160,8 @@ namespace MapTo converterFullyQualifiedName, converterParameters.ToImmutableArray(), sourceProperty.Name, - mappedSourcePropertyType?.Name, - enumerableTypeArgumentType?.Name); + ToQualifiedDisplayName(mappedSourcePropertyType), + ToQualifiedDisplayName(enumerableTypeArgumentType)); } protected bool TryGetMapTypeConverter(ISymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName, @@ -171,7 +176,7 @@ namespace MapTo } var typeConverterAttribute = property.GetAttribute(MapTypeConverterAttributeTypeSymbol); - if (!(typeConverterAttribute?.ConstructorArguments.First().Value is INamedTypeSymbol converterTypeSymbol)) + if (typeConverterAttribute?.ConstructorArguments.First().Value is not INamedTypeSymbol converterTypeSymbol) { return false; } @@ -248,6 +253,9 @@ namespace MapTo return null; } + var sourceTypeNamespace = sourceTypeSymbol.ContainingNamespace.ToDisplayString(); + _ignoredNamespaces.Add(sourceTypeNamespace); + var typeIdentifierName = TypeSyntax.GetIdentifierName(); var sourceTypeIdentifierName = sourceTypeSymbol.Name; var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel); @@ -260,7 +268,6 @@ namespace MapTo return null; } - AddUsingIfRequired(sourceTypeSymbol); AddUsingIfRequired(mappedProperties.Any(p => p.IsEnumerable), "System.Linq"); return new MappingModel( @@ -269,7 +276,7 @@ namespace MapTo TypeSyntax.Modifiers, TypeSyntax.Keyword.Text, typeIdentifierName, - sourceTypeSymbol.ContainingNamespace.ToString(), + sourceTypeNamespace, sourceTypeIdentifierName, sourceTypeSymbol.ToDisplayString(), mappedProperties, @@ -316,5 +323,16 @@ namespace MapTo return false; } + + private string? ToQualifiedDisplayName(ISymbol? typeSymbol) + { + if (typeSymbol is null) + { + return null; + } + + var typeNamespace = typeSymbol.ContainingNamespace.ToDisplayString(); + return _ignoredNamespaces.Contains(typeNamespace) ? typeSymbol.ToDisplayString() : typeSymbol.Name; + } } } \ No newline at end of file diff --git a/src/MapTo/Models.cs b/src/MapTo/Models.cs index e925a11..1ec8e0d 100644 --- a/src/MapTo/Models.cs +++ b/src/MapTo/Models.cs @@ -34,7 +34,7 @@ namespace MapTo bool GenerateSecondaryConstructor ) { - public string SourceType => SourceTypeIdentifierName == TypeIdentifierName ? SourceTypeFullName : SourceTypeIdentifierName; + public string SourceType => SourceTypeFullName; } internal record SourceGenerationOptions( diff --git a/test/MapTo.Tests/MappedClassesTests.cs b/test/MapTo.Tests/MappedClassesTests.cs index 08b2400..a4e4313 100644 --- a/test/MapTo.Tests/MappedClassesTests.cs +++ b/test/MapTo.Tests/MappedClassesTests.cs @@ -172,6 +172,142 @@ namespace SaleModel diagnostics.ShouldBeSuccessful(); } + [Theory] + [MemberData(nameof(SameSourceAndDestinationTypeNameData))] + public void When_SourceAndDestinationNamesAreTheSame_Should_MapAccordingly(string source, LanguageVersion languageVersion) + { + // Arrange + source = source.Trim(); + + // Act + var (_, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: languageVersion); + + // Assert + diagnostics.ShouldBeSuccessful(); + } + + public static IEnumerable SameSourceAndDestinationTypeNameData => new List + { + new object[] + { + @" +namespace Test +{ + public class TypeName { public int Prop2 { get; set; } } +} + +namespace Test2 +{ + using MapTo; + + [MapFrom(typeof(Test.TypeName))] + public partial class TypeName + { + [MapProperty(SourcePropertyName=""Prop2"")] + public int Prop1 { get; set; } + } +}", + LanguageVersion.CSharp7_3 + }, + new object[] + { + @" +namespace Test +{ + public record TypeName(int Prop2); +} + +namespace Test2 +{ + using MapTo; + + [MapFrom(typeof(Test.TypeName))] + public partial record TypeName([MapProperty(SourcePropertyName=""Prop2"")] int Prop1); +}", + LanguageVersion.CSharp9 + }, + new object[] + { + @" +namespace Test +{ + using System.Collections.Generic; + + public class SourceType2 { public int Id { get; set; } } + public class SourceType + { + public int Id { get; set; } + public List Prop1 { get; set; } + } +} + +namespace Test2 +{ + using MapTo; + using System.Collections.Generic; + + [MapFrom(typeof(Test.SourceType2))] + public partial class SourceType2 { public int Id { get; set; } } + + [MapFrom(typeof(Test.SourceType))] + public partial class SourceType + { + public int Id { get; set; } + public IReadOnlyList Prop1 { get; set; } + } +}", + LanguageVersion.CSharp7_3 + }, + new object[] + { + @" +namespace Test +{ + using System.Collections.Generic; + + public record SourceType(int Id, List Prop1); + public record SourceType2(int Id); +} + +namespace Test2 +{ + using MapTo; + using System.Collections.Generic; + + [MapFrom(typeof(Test.SourceType2))] + public partial record SourceType2(int Id); + + [MapFrom(typeof(Test.SourceType))] + public partial record SourceType(int Id, IReadOnlyList Prop1); +}", + LanguageVersion.CSharp9 + }, + new object[] + { + @" +namespace Test +{ + using System.Collections.Generic; + + public record SourceType1(int Id); + public record SourceType2(int Id, List Prop1); +} + +namespace Test +{ + using MapTo; + using System.Collections.Generic; + + [MapFrom(typeof(Test.SourceType1))] + public partial record SourceType3(int Id); + + [MapFrom(typeof(Test.SourceType2))] + public partial record SourceType4(int Id, IReadOnlyList Prop1); +}", + LanguageVersion.CSharp9 + } + }; + [Theory] [MemberData(nameof(VerifyMappedTypesData))] public void VerifyMappedTypes(string[] sources, LanguageVersion languageVersion)