diff --git a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.Designer.cs b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.Designer.cs index 082143d..6982fc2 100644 --- a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.Designer.cs +++ b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.Designer.cs @@ -81,10 +81,10 @@ internal EmptyInitializationBlockTestCases() { /// var firstName = "Cezary"; /// var lastName = "Piatek"; /// - /// var x = new UserDTO() + /// var x = new UserDTO /// [|{ /// - /// [rest of string was truncated]";. + /// } [rest of string was truncated]";. /// internal static string _001_CompleteInitializationBlockWithLocals { get { @@ -113,9 +113,9 @@ internal static string _001_CompleteInitializationBlockWithLocals { /// var firstName = "Cezary"; /// var lastName = "Piatek"; /// - /// var x = new UserDTO() + /// var x = new UserDTO /// { - /// [rest of string was truncated]";. + /// F [rest of string was truncated]";. /// internal static string _001_CompleteInitializationBlockWithLocals_FIXED { get { @@ -195,14 +195,14 @@ internal static string _002_CompleteInitializationBlockWithLambdaParameter_FIXED /// { /// public IList<UserDTO> Map(IList<UserEntity> entities) /// { - /// return entities.Select((UserEntity x) => new UserDTO() [|{ }|]).ToList(); + /// return entities.Select((UserEntity x) => new UserDTO [|{ }|]).ToList(); /// } /// } /// /// public class UserDTO /// { /// public string FirstName { get; set; } - /// public string LastName { get; se [rest of string was truncated]";. + /// public string LastName { get; set; [rest of string was truncated]";. /// internal static string _003_CompleteInitializationBlockWithSompleLambdaParameter { get { @@ -222,13 +222,13 @@ internal static string _003_CompleteInitializationBlockWithSompleLambdaParameter /// { /// public IList<UserDTO> Map(IList<UserEntity> entities) /// { - /// return entities.Select((UserEntity x) => new UserDTO() + /// return entities.Select((UserEntity x) => new UserDTO /// { /// FirstName = x.FirstName, /// LastName = x.LastName, /// Age = x.Age /// }).ToList(); - /// [rest of string was truncated]";. + /// } [rest of string was truncated]";. /// internal static string _003_CompleteInitializationBlockWithSompleLambdaParameter_FIXED { get { @@ -280,10 +280,10 @@ internal static string _004_CompleteInitializationBlockWithSampleDataRecursiveTy /// return new Tree /// { /// Name = "lorem ipsum", - /// Left = new Tree() + /// Left = new Tree /// { /// Name = "lorem ipsum", - /// Left = null /* Stop recursive mappin [rest of string was truncated]";. + /// Left = null /* Stop recursive mapping [rest of string was truncated]";. /// internal static string _004_CompleteInitializationBlockWithSampleDataRecursiveType_FIXED { get { @@ -365,7 +365,7 @@ internal static string _005_CompleteInitializationBlockWithSampleDatComplexType_ /// var users = new List<User>(); /// var query = from p in users /// where p.FirstName !="" - /// select new UserDTO() + /// select new UserDTO /// { /// [||] /// }; @@ -376,7 +376,7 @@ internal static string _005_CompleteInitializationBlockWithSampleDatComplexType_ /// class User /// { /// public string FirstName { get; set; } - /// public st [rest of string was truncated]";. + /// public stri [rest of string was truncated]";. /// internal static string _006_SelectInSimpleLinq { get { @@ -398,7 +398,7 @@ internal static string _006_SelectInSimpleLinq { /// var users = new List<User>(); /// var query = from p in users /// where p.FirstName !="" - /// select new UserDTO() + /// select new UserDTO /// { /// FirstName = p.FirstName, /// LastName = p.LastName @@ -409,7 +409,7 @@ internal static string _006_SelectInSimpleLinq { /// /// class User /// { - /// [rest of string was truncated]";. + /// [rest of string was truncated]";. /// internal static string _006_SelectInSimpleLinqFixed { get { @@ -718,10 +718,10 @@ internal static string _010_GroupByInLinq_txt_FIXED { /// var firstName = "Cezary"; /// var lastName = "Piatek"; /// - /// var user = new UserDTO() + /// var user = new UserDTO /// [|{ /// - /// }|] [rest of string was truncated]";. + /// }|]; /// [rest of string was truncated]";. /// internal static string _011_CompleteInitializationBlockWithLocals { get { @@ -750,14 +750,64 @@ internal static string _011_CompleteInitializationBlockWithLocals { /// var firstName = "Cezary"; /// var lastName = "Piatek"; /// - /// var user = new UserDTO() + /// var user = new UserDTO /// { - /// Fir [rest of string was truncated]";. + /// First [rest of string was truncated]";. /// internal static string _011_CompleteInitializationBlockWithLocals_FIXED { get { return ResourceManager.GetString("_011_CompleteInitializationBlockWithLocals_FIXED", resourceCulture); } } + + /// + /// Looks up a localized string similar to #nullable enable + ///using System; + ///using System.Collections.Generic; + ///using System.Text; + ///using System.Collections.ObjectModel; + ///using System.Collections.Immutable; + ///using System.Linq; + /// + ///namespace MappingGenerator.Test.MappingGenerator.TestCaseData + ///{ + /// public class TestMapper + /// { + /// public static UserDTO Map(UserEntity? entity) + /// { + /// return entity != null ? new UserDTO + /// [|{ + /// }|] : throw new ArgumentNullException(nameof(entity), "The value of 'entity' sho [rest of string was truncated]";. + /// + internal static string _012_ScaffoldingWithNonNullable { + get { + return ResourceManager.GetString("_012_ScaffoldingWithNonNullable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to #nullable enable + ///using System; + ///using System.Collections.Generic; + ///using System.Text; + ///using System.Collections.ObjectModel; + ///using System.Collections.Immutable; + ///using System.Linq; + /// + ///namespace MappingGenerator.Test.MappingGenerator.TestCaseData + ///{ + /// public class TestMapper + /// { + /// public static UserDTO Map(UserEntity? entity) + /// { + /// return entity != null ? new UserDTO + /// [|{ + /// }|] : throw new ArgumentNullException(nameof(entity), "The value of 'entity' sho [rest of string was truncated]";. + /// + internal static string _012_ScaffoldingWithNonNullable_FIXED { + get { + return ResourceManager.GetString("_012_ScaffoldingWithNonNullable_FIXED", resourceCulture); + } + } } } diff --git a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.resx b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.resx index be9e4e8..fdcf732 100644 --- a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.resx +++ b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTestCases.resx @@ -184,4 +184,10 @@ TestCasesData\011_CompleteInitializationBlockWithLocals_FIXED.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + TestCasesData\012_ScaffoldingWithNonNullable.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + TestCasesData\012_ScaffoldingWithNonNullable_FIXED.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + \ No newline at end of file diff --git a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTests.cs b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTests.cs index cbc20a6..f912c2f 100644 --- a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTests.cs +++ b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/EmptyInitializationBlockTests.cs @@ -98,6 +98,15 @@ public void should_not_initialized_using_itself() var fixedCode = EmptyInitializationBlockTestCases._011_CompleteInitializationBlockWithLocals_FIXED; TestCodeRefactoring(test, fixedCode, 0); } + + + [Test] + public void should_scaffold_init_block_with_non_nullable_references() + { + var test = EmptyInitializationBlockTestCases._012_ScaffoldingWithNonNullable; + var fixedCode = EmptyInitializationBlockTestCases._012_ScaffoldingWithNonNullable_FIXED; + TestCodeRefactoring(test, fixedCode, 1); + } protected override string LanguageName => LanguageNames.CSharp; protected override CodeRefactoringProvider CreateProvider() diff --git a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/TestCasesData/012_ScaffoldingWithNonNullable.txt b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/TestCasesData/012_ScaffoldingWithNonNullable.txt new file mode 100644 index 0000000..e204b80 --- /dev/null +++ b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/TestCasesData/012_ScaffoldingWithNonNullable.txt @@ -0,0 +1,132 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections.ObjectModel; +using System.Linq; + +namespace MappingGenerator.Test.MappingGenerator.TestCaseData +{ + public class TestMapper + { + public static UserDTO Map(UserEntity? entity) + { + return entity != null ? new UserDTO + [|{ + }|] : throw new ArgumentNullException(nameof(entity), "The value of 'entity' should not be null"); + } + } + + public class UserDTO + { + public int Id {get; set;} + public string FirstName { get; set; } + public string? MiddleName { get; set; } + public string LastName { get; set; } + public int Age { get; set; } + public AccountDTO? Account { get; set; } + public AccountDTO Account2 { get; set; } + public List? Debs { get; set; } + public List Debs2 { get; set; } + public string AddressCity { get; set; } + public string AddressZipCode { get; set; } + public string AddressStreet { get; set; } + public string? AddressFlatNo { get; set; } + public string AddressBuildingNo { get; set; } + public AddressDTO Address1 { get; set; } + public AddressDTO? Address2 { get; set; } + public List Address3 { get; set; } + public List Address4 { get; set; } + public List Address5 { get; set; } + public List Address6 { get; set; } + public int Total { get; set; } + public int UnitId { get; set; } + public UserSourceDTO Source { get; set; } + } + + public class AccountDTO + { + public string BankName { get; set; } + public string Number { get; set; } + } + + public class AddressDTO + { + public string City { get; set; } + public string ZipCode { get; set; } + public string Street { get; set; } + public string FlatNo { get; set; } + public string BuildingNo { get; set; } + + public AddressDTO(AddressEntity entity) + { + } + } + + public class UserSourceDTO + { + public string ProviderName { get; set; } + public string ProviderAddress { get; set; } + public bool IsActive {get; set;} + + public UserSourceDTO(string providerName, string providerAddress, bool isActive) + { + ProviderName = providerName; + ProviderAddress = providerAddress; + IsActive = isActive; + } + } + + //---- Entities + + public class UserEntity + { + public int Id {get; set;} + public string FirstName { get; set; } + public string LastName { get; set; } + public string? MiddleName { get; set; } + public int Age { get; set; } + public AccountEntity? Account { get; set; } + public AccountEntity? Account2 { get; set; } + public List? Debs { get; set; } + public List? Debs2 { get; set; } + public AddressEntity? Address { get; set; } + public AddressEntity? Address1 { get; set; } + public AddressEntity? Address2 { get; set; } + public List Address3 { get; set; } + public List Address4 { get; set; } + public List? Address5 { get; set; } + public List? Address6 { get; set; } + public int? GetTotal() => throw new Exception(); + public UnitEntity? Unit { get; set; } + public UserSourceEntity Source { get; set; } + } + + public class AccountEntity + { + public string BankName { get; set; } + public string Number { get; set; } + } + + public class AddressEntity + { + public string City { get; set; } + public string ZipCode { get; set; } + public string Street { get; set; } + public string FlatNo { get; set; } + public string BuildingNo { get; set; } + } + + public class UnitEntity + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class UserSourceEntity + { + public string ProviderName { get; set; } + public string? ProviderAddress { get; set; } + public bool? IsActive {get; set;} + } +} \ No newline at end of file diff --git a/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/TestCasesData/012_ScaffoldingWithNonNullable_FIXED.txt b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/TestCasesData/012_ScaffoldingWithNonNullable_FIXED.txt new file mode 100644 index 0000000..4b74c69 --- /dev/null +++ b/MappingGenerator/MappingGenerator/MappingGenerator.Test/EmptyInitializationBlock/TestCasesData/012_ScaffoldingWithNonNullable_FIXED.txt @@ -0,0 +1,278 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections.ObjectModel; +using System.Linq; + +namespace MappingGenerator.Test.MappingGenerator.TestCaseData +{ + public class TestMapper + { + public static UserDTO Map(UserEntity? entity) + { + return entity != null ? new UserDTO + { + Id = 32, + FirstName = "lorem ipsum", + MiddleName = "lorem ipsum", + LastName = "lorem ipsum", + Age = 32, + Account = new AccountDTO + { + BankName = "lorem ipsum", + Number = "lorem ipsum" + }, + Account2 = new AccountDTO + { + BankName = "lorem ipsum", + Number = "lorem ipsum" + }, + Debs = new List() + { + new AccountDTO + { + BankName = "lorem ipsum", + Number = "lorem ipsum" + } + }, + Debs2 = new List() + { + new AccountDTO + { + BankName = "lorem ipsum", + Number = "lorem ipsum" + } + }, + AddressCity = "lorem ipsum", + AddressZipCode = "lorem ipsum", + AddressStreet = "lorem ipsum", + AddressFlatNo = "lorem ipsum", + AddressBuildingNo = "lorem ipsum", + Address1 = new AddressDTO(new AddressEntity + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }) + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }, + Address2 = new AddressDTO(new AddressEntity + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }) + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }, + Address3 = new List() + { + new AddressDTO(new AddressEntity + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }) + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + } + }, + Address4 = new List() + { + new AddressDTO(new AddressEntity + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }) + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + } + }, + Address5 = new List() + { + new AddressDTO(new AddressEntity + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }) + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + } + }, + Address6 = new List() + { + new AddressDTO(new AddressEntity + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + }) + { + City = "lorem ipsum", + ZipCode = "lorem ipsum", + Street = "lorem ipsum", + FlatNo = "lorem ipsum", + BuildingNo = "lorem ipsum" + } + }, + Total = 32, + UnitId = 32, + Source = new UserSourceDTO("lorem ipsum", "lorem ipsum", true) + { + ProviderName = "lorem ipsum", + ProviderAddress = "lorem ipsum", + IsActive = true + } + } : throw new ArgumentNullException(nameof(entity), "The value of 'entity' should not be null"); + } + } + + public class UserDTO + { + public int Id {get; set;} + public string FirstName { get; set; } + public string? MiddleName { get; set; } + public string LastName { get; set; } + public int Age { get; set; } + public AccountDTO? Account { get; set; } + public AccountDTO Account2 { get; set; } + public List? Debs { get; set; } + public List Debs2 { get; set; } + public string AddressCity { get; set; } + public string AddressZipCode { get; set; } + public string AddressStreet { get; set; } + public string? AddressFlatNo { get; set; } + public string AddressBuildingNo { get; set; } + public AddressDTO Address1 { get; set; } + public AddressDTO? Address2 { get; set; } + public List Address3 { get; set; } + public List Address4 { get; set; } + public List Address5 { get; set; } + public List Address6 { get; set; } + public int Total { get; set; } + public int UnitId { get; set; } + public UserSourceDTO Source { get; set; } + } + + public class AccountDTO + { + public string BankName { get; set; } + public string Number { get; set; } + } + + public class AddressDTO + { + public string City { get; set; } + public string ZipCode { get; set; } + public string Street { get; set; } + public string FlatNo { get; set; } + public string BuildingNo { get; set; } + + public AddressDTO(AddressEntity entity) + { + } + } + + public class UserSourceDTO + { + public string ProviderName { get; set; } + public string ProviderAddress { get; set; } + public bool IsActive {get; set;} + + public UserSourceDTO(string providerName, string providerAddress, bool isActive) + { + ProviderName = providerName; + ProviderAddress = providerAddress; + IsActive = isActive; + } + } + + //---- Entities + + public class UserEntity + { + public int Id {get; set;} + public string FirstName { get; set; } + public string LastName { get; set; } + public string? MiddleName { get; set; } + public int Age { get; set; } + public AccountEntity? Account { get; set; } + public AccountEntity? Account2 { get; set; } + public List? Debs { get; set; } + public List? Debs2 { get; set; } + public AddressEntity? Address { get; set; } + public AddressEntity? Address1 { get; set; } + public AddressEntity? Address2 { get; set; } + public List Address3 { get; set; } + public List Address4 { get; set; } + public List? Address5 { get; set; } + public List? Address6 { get; set; } + public int? GetTotal() => throw new Exception(); + public UnitEntity? Unit { get; set; } + public UserSourceEntity Source { get; set; } + } + + public class AccountEntity + { + public string BankName { get; set; } + public string Number { get; set; } + } + + public class AddressEntity + { + public string City { get; set; } + public string ZipCode { get; set; } + public string Street { get; set; } + public string FlatNo { get; set; } + public string BuildingNo { get; set; } + } + + public class UnitEntity + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class UserSourceEntity + { + public string ProviderName { get; set; } + public string? ProviderAddress { get; set; } + public bool? IsActive {get; set;} + } +} \ No newline at end of file diff --git a/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/MappingImplementors/MultiParameterPureMappingMethodImplementor.cs b/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/MappingImplementors/MultiParameterPureMappingMethodImplementor.cs index 359fb4b..831ef63 100644 --- a/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/MappingImplementors/MultiParameterPureMappingMethodImplementor.cs +++ b/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/MappingImplementors/MultiParameterPureMappingMethodImplementor.cs @@ -21,7 +21,7 @@ public IEnumerable GenerateImplementation(IMethodSymbol methodSymbol var mappingEngine = new MappingEngine(semanticModel, generator); var targetType = methodSymbol.ReturnType; var sourceFinder = new LocalScopeMappingSourceFinder(semanticModel, methodSymbol); - var objectCreationExpressionSyntax = (ObjectCreationExpressionSyntax)generator.ObjectCreationExpression(targetType); + var objectCreationExpressionSyntax = (ObjectCreationExpressionSyntax)generator.ObjectCreationExpression(targetType.StripNullability()); var newExpression = mappingEngine.AddInitializerWithMapping(objectCreationExpressionSyntax, new SingleSourceMatcher(sourceFinder), targetType, mappingContext); return new[] { generator.ReturnStatement(newExpression).WithAdditionalAnnotations(Formatter.Annotation) }; } diff --git a/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/SourceFinders/ScaffoldingSourceFinder.cs b/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/SourceFinders/ScaffoldingSourceFinder.cs index 1bdf1c6..f206989 100644 --- a/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/SourceFinders/ScaffoldingSourceFinder.cs +++ b/MappingGenerator/MappingGenerator/MappingGenerator/Mappings/SourceFinders/ScaffoldingSourceFinder.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using MappingGenerator.RoslynHelpers; @@ -34,6 +36,8 @@ private MappingElement FindMappingSource(AnnotatedType targetType, MappingContex }; } + private ConcurrentDictionary cache = new ConcurrentDictionary(); + internal SyntaxNode GetDefaultExpression(ITypeSymbol type, MappingContext mappingContext, MappingPath mappingPath) { if (mappingPath.AddToMapped(type) == false) @@ -42,132 +46,145 @@ internal SyntaxNode GetDefaultExpression(ITypeSymbol type, MappingContext mappin .WithTrailingTrivia(SyntaxFactory.Comment(" /* Stop recursive mapping */")); } - if (SymbolHelper.IsNullable(type, out var underlyingType)) + return cache.GetOrAdd(type.ToDisplayString().TrimEnd('?'), _ => { - type = underlyingType; - } - - if (type.TypeKind == TypeKind.Enum && type is INamedTypeSymbol namedTypeSymbol) - { - var enumOptions = namedTypeSymbol.MemberNames.Where(x=>x!="value__" && x!=".ctor").OrderBy(x=>x).ToList(); - if (enumOptions.Count > 0) + if (SymbolHelper.IsNullable(type, out var underlyingType)) { - return syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(namedTypeSymbol.Name), syntaxGenerator.IdentifierName(enumOptions[0])); + type = underlyingType; } - return syntaxGenerator.DefaultExpression(type); - } - - if (type.SpecialType == SpecialType.None) - { - var objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(type); - - if (MappingHelper.IsCollection(type)) + if (type.TypeKind == TypeKind.Enum && type is INamedTypeSymbol namedTypeSymbol) { - var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(type); - - if (type is IArrayTypeSymbol) + var enumOptions = namedTypeSymbol.MemberNames.Where(x => x != "value__" && x != ".ctor").OrderBy(x => x).ToList(); + if (enumOptions.Count > 0) { - objectCreationExpression = SyntaxFactory.ObjectCreationExpression((TypeSyntax)syntaxGenerator.TypeExpression(type)); + return SyntaxFactoryExtensions.CreateMemberAccessExpression(SyntaxFactory.IdentifierName(namedTypeSymbol.Name), false, enumOptions[0]); } - else if (type.TypeKind == TypeKind.Interface || isReadonlyCollection) + return syntaxGenerator.DefaultExpression(type); + } + + if (type.SpecialType == SpecialType.None) + { + + + ObjectCreationExpressionSyntax objectCreationExpression = null; + + if (MappingHelper.IsCollection(type)) { - var namedType = type as INamedTypeSymbol; - if (namedType.IsGenericType) + var isReadonlyCollection = ObjectHelper.IsReadonlyCollection(type); + + if (type is IArrayTypeSymbol) { - var typeArgumentListSyntax = SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(namedType.TypeArguments.Select(x=> syntaxGenerator.TypeExpression(x)))); - var newType = SyntaxFactory.GenericName(SyntaxFactory.Identifier("List"), typeArgumentListSyntax); - objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); + objectCreationExpression = CreateObject(type); } - else + else if (type.TypeKind == TypeKind.Interface || isReadonlyCollection) { - var newType = SyntaxFactory. ParseTypeName("ArrayList"); - objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); + if (type is INamedTypeSymbol namedType && namedType.IsGenericType) + { + var typeArgumentListSyntax = SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(namedType.TypeArguments.Select(x => syntaxGenerator.TypeExpression(x)))); + var newType = SyntaxFactory.GenericName(SyntaxFactory.Identifier("List"), typeArgumentListSyntax); + objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); + } + else + { + var newType = SyntaxFactory.ParseTypeName("ArrayList"); + objectCreationExpression = SyntaxFactory.ObjectCreationExpression(newType, SyntaxFactory.ArgumentList(), default(InitializerExpressionSyntax)); + } } - } - var subType = MappingHelper.GetElementType(type); - var initializationBlockExpressions = new SeparatedSyntaxList(); - var subTypeDefault = (ExpressionSyntax)GetDefaultExpression(subType.Type, mappingContext, mappingPath.Clone()); - if (subTypeDefault != null) - { - initializationBlockExpressions = initializationBlockExpressions.Add(subTypeDefault); - } + objectCreationExpression ??= CreateObject(type, Array.Empty()); - var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializationBlockExpressions).FixInitializerExpressionFormatting(objectCreationExpression); - return objectCreationExpression - .WithInitializer(initializerExpressionSyntax) - .WrapInReadonlyCollectionIfNecessary(isReadonlyCollection, syntaxGenerator); - } - - { - var nt = type as INamedTypeSymbol; - - if (nt == null) - { - var genericTypeConstraints = type.UnwrapGeneric().ToList(); - if (genericTypeConstraints.Any() == false) + var subType = MappingHelper.GetElementType(type); + var initializationBlockExpressions = new SeparatedSyntaxList(); + var subTypeDefault = (ExpressionSyntax)GetDefaultExpression(subType.Type, mappingContext, mappingPath.Clone()); + if (subTypeDefault != null) { - return GetDefaultForUnknown(type, ObjectType); + initializationBlockExpressions = initializationBlockExpressions.Add(subTypeDefault); } - nt = genericTypeConstraints.FirstOrDefault(x=>x.TypeKind == TypeKind.Class) as INamedTypeSymbol ?? - genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Interface) as INamedTypeSymbol; - } - if (nt == null) - { - return GetDefaultForUnknownType(type); + var initializerExpressionSyntax = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializationBlockExpressions).FixInitializerExpressionFormatting(objectCreationExpression); + return objectCreationExpression + .WithInitializer(initializerExpressionSyntax) + .WrapInReadonlyCollectionIfNecessary(isReadonlyCollection, syntaxGenerator); } - if (nt.TypeKind == TypeKind.Interface) { - var implementations = SymbolFinder.FindImplementationsAsync(type, this._document.Project.Solution).Result; - var firstImplementation = implementations.FirstOrDefault(); - if (firstImplementation is INamedTypeSymbol == false) + var nt = type as INamedTypeSymbol; + if (nt == null) + { + var genericTypeConstraints = type.UnwrapGeneric().ToList(); + if (genericTypeConstraints.Any() == false) + { + return GetDefaultForUnknown(type, ObjectType); + } + nt = genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Class) as INamedTypeSymbol ?? + genericTypeConstraints.FirstOrDefault(x => x.TypeKind == TypeKind.Interface) as INamedTypeSymbol; + } + + if (nt == null) { return GetDefaultForUnknownType(type); } - nt = firstImplementation as INamedTypeSymbol; - objectCreationExpression = (ObjectCreationExpressionSyntax) syntaxGenerator.ObjectCreationExpression(nt); + if (nt.TypeKind == TypeKind.Interface) + { + var implementations = SymbolFinder.FindImplementationsAsync(type, this._document.Project.Solution).Result; + var firstImplementation = implementations.FirstOrDefault(); + if (firstImplementation is INamedTypeSymbol == false) + { + return GetDefaultForUnknownType(type); + } + + nt = firstImplementation as INamedTypeSymbol; + objectCreationExpression = CreateObject(nt); - }else if (nt.TypeKind == TypeKind.Class && nt.IsAbstract) - { - var randomDerived = SymbolFinder.FindDerivedClassesAsync(nt, this._document.Project.Solution).Result - .FirstOrDefault(x => x.IsAbstract == false); - - if (randomDerived != null) + } + else if (nt.TypeKind == TypeKind.Class && nt.IsAbstract) { - nt = randomDerived; - objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt); + var randomDerived = SymbolFinder.FindDerivedClassesAsync(nt, this._document.Project.Solution).Result + .FirstOrDefault(x => x.IsAbstract == false); + + if (randomDerived != null) + { + nt = randomDerived; + objectCreationExpression = CreateObject(nt); + } } - } - var publicConstructors = nt.Constructors.Where(x => mappingContext.AccessibilityHelper.IsSymbolAccessible(x, nt)).ToList(); - var hasDefaultConstructor = publicConstructors.Any(x => x.Parameters.Length == 0); - if (hasDefaultConstructor == false && publicConstructors.Count > 0) - { - var randomConstructor = publicConstructors.First(); - var constructorArguments = randomConstructor.Parameters.Select(p => GetDefaultExpression(p.Type, mappingContext, mappingPath.Clone())).ToList(); - objectCreationExpression = (ObjectCreationExpressionSyntax)syntaxGenerator.ObjectCreationExpression(nt, constructorArguments); - } + var publicConstructors = nt.Constructors.Where(x => mappingContext.AccessibilityHelper.IsSymbolAccessible(x, nt)).ToList(); + var hasDefaultConstructor = publicConstructors.Any(x => x.Parameters.Length == 0); + if (hasDefaultConstructor == false && publicConstructors.Count > 0) + { + var randomConstructor = publicConstructors.First(); + var constructorArguments = randomConstructor.Parameters.Select(p => GetDefaultExpression(p.Type, mappingContext, mappingPath.Clone())).Select(x=> SyntaxFactory.Argument((ExpressionSyntax) x)) .ToList(); + objectCreationExpression = CreateObject(nt, constructorArguments); + } - var fields = MappingTargetHelper.GetFieldsThaCanBeSetPublicly(nt, mappingContext); - var assignments = fields.Select(x => - { - var identifier = (ExpressionSyntax)(SyntaxFactory.IdentifierName(x.Name)); - var mappingSource = this.FindMappingSource(x.Type, mappingContext, mappingPath.Clone()); - return (ExpressionSyntax)syntaxGenerator.AssignmentStatement(identifier, mappingSource.Expression); - }).ToList(); + var fields = MappingTargetHelper.GetFieldsThaCanBeSetPublicly(nt, mappingContext); + var assignments = fields.Select(x => + { + var identifier = (ExpressionSyntax)(SyntaxFactory.IdentifierName(x.Name)); + var mappingSource = this.FindMappingSource(x.Type, mappingContext, mappingPath.Clone()); + return (ExpressionSyntax)syntaxGenerator.AssignmentStatement(identifier, mappingSource.Expression); + }).ToList(); + if (objectCreationExpression == null) + { + objectCreationExpression = CreateObject(type); + } - return SyntaxFactoryExtensions.WithMembersInitialization(objectCreationExpression, assignments); + return SyntaxFactoryExtensions.WithMembersInitialization(objectCreationExpression, assignments); + } } - } - + return GetDefaultForSpecialType(type); + }); + } - return GetDefaultForSpecialType(type); + private ObjectCreationExpressionSyntax CreateObject(ITypeSymbol type, IReadOnlyList constructorArguments = null) + { + var argumentListSyntax = constructorArguments == null ? null : SyntaxFactory.ArgumentList(new SeparatedSyntaxList().AddRange(constructorArguments)); + return SyntaxFactory.ObjectCreationExpression((TypeSyntax)syntaxGenerator.TypeExpression(type.StripNullability())).WithArgumentList(argumentListSyntax); }