public NonexclusiveInterfaceDescription(LazinatorCompilation fileSet, INamedTypeSymbol t, NullableContext nullableContextSetting, ObjectDescription container) { string typeName = LazinatorCompilation.TypeSymbolToString(t); NullableContextSetting = nullableContextSetting; if (!fileSet.NonexclusiveInterfaces.Contains(typeName)) { throw new LazinatorCodeGenException("NonexclusiveLazinator must be applied to a nonexclusive interface."); } Container = container; CloneNonexclusiveLazinatorAttribute nonexclusiveLazinatorAttribute = fileSet.GetFirstAttributeOfType <CloneNonexclusiveLazinatorAttribute>(t); if (nonexclusiveLazinatorAttribute == null) { throw new LazinatorCodeGenException("Expected nonexclusive self-serialize attribute."); } if (fileSet.PropertiesForType.ContainsKey(typeName)) { Properties = fileSet.PropertiesForType[typeName].Select(x => new PropertyDescription(x.Property, container, NullableContextSetting, null, null, false)).ToList(); } }
public async Task CanParseSupportedCollections() { LazinatorCompilation lazinatorFiles = await GetMiniRoslynFileSet(typeof(DotNetList_Lazinator)); string interfaceName = "IDotNetList_Lazinator"; var interfaceSymbol = lazinatorFiles.LookupSymbol(interfaceName); var properties = lazinatorFiles.PropertiesForType[LazinatorCompilation.TypeSymbolToString(interfaceSymbol)]; var property = properties.First().Property; property.Type.Name.Should().Be("List"); (property.Type is INamedTypeSymbol).Should().BeTrue(); var namedType = property.Type as INamedTypeSymbol; namedType.TypeArguments.Count().Should().Be(1); var typeArgument = namedType.TypeArguments[0]; typeArgument.Name.Should().Be("ExampleChild"); INamedTypeSymbol exampleChildClass = lazinatorFiles.LookupSymbol(typeArgument.Name); exampleChildClass.IsReferenceType.Should().BeTrue(); }
private void SetPropertiesIncludingInherited(INamedTypeSymbol interfaceSymbol) { List <PropertyWithDefinitionInfo> propertiesWithLevel = Container.Compilation.PropertiesForType[LazinatorCompilation.TypeSymbolToString(interfaceSymbol)]; foreach (var unofficialProperty in GetUnofficialProperties(interfaceSymbol)) { if (!propertiesWithLevel.Any(x => x.Property.MetadataName == unofficialProperty.PropertySymbol.MetadataName)) { propertiesWithLevel.Add(new PropertyWithDefinitionInfo(unofficialProperty.PropertySymbol, PropertyWithDefinitionInfo.Level.IsDefinedThisLevel) { PropertyAccessibility = unofficialProperty.PropertyAccessibility }); } } foreach (var baseType in Container.GetBaseLazinatorObjects()) { List <IPropertySymbol> additionalProperties; bool baseTypeIsIndexed = Container.Compilation.TypeToExclusiveInterface.ContainsKey(LazinatorCompilation.TypeSymbolToString(baseType.ILazinatorTypeSymbol)); if (baseTypeIsIndexed) { var baseExclusiveInterface = Container.Compilation.TypeToExclusiveInterface[LazinatorCompilation.TypeSymbolToString(baseType.ILazinatorTypeSymbol)]; additionalProperties = Container.Compilation.PropertiesForType[baseExclusiveInterface].Select(x => x.Property).ToList(); } else { additionalProperties = new List <IPropertySymbol>(); } foreach (var baseProperty in additionalProperties) { if (!propertiesWithLevel.Any(x => x.Property.MetadataName == baseProperty.MetadataName)) { propertiesWithLevel.Add(new PropertyWithDefinitionInfo(baseProperty, PropertyWithDefinitionInfo.Level.IsDefinedLowerLevelButNotInInterface) { DerivationKeyword = "override " }); } } // now, unofficial properties var baseUnofficialProperties = GetUnofficialProperties(baseType.InterfaceTypeSymbol); foreach (var unofficialProperty in baseUnofficialProperties) { if (!propertiesWithLevel.Any(x => x.Property.MetadataName == unofficialProperty.PropertySymbol.MetadataName)) { propertiesWithLevel.Add(new PropertyWithDefinitionInfo(unofficialProperty.PropertySymbol, PropertyWithDefinitionInfo.Level.IsDefinedLowerLevelButNotInInterface) { DerivationKeyword = "override ", PropertyAccessibility = unofficialProperty.PropertyAccessibility }); } } } var firstProblem = propertiesWithLevel.FirstOrDefault(x => !RoslynHelpers.ParentAndChildShareNullabilityContext(NullableContextSetting, x.Property.GetNullableContextForSymbol(Compilation, true))); if (firstProblem != null) { throw new LazinatorCodeGenException($"Lazinator requires properties of an interface to have the same nullability context as the interface itself. {NamedTypeSymbol} has nullability context {NullableContextSetting} while {firstProblem.Property} has nullability context {firstProblem.Property.GetNullableContextForSymbol(Compilation, true)}"); } var orderedPropertiesWithLevel = propertiesWithLevel.Select(x => new { propertyWithLevel = x, description = new PropertyDescription(x.Property, Container, NullableContextSetting, x.DerivationKeyword, x.PropertyAccessibility, false) }) .OrderByDescending(x => x.description.PropertyType == LazinatorPropertyType.PrimitiveType || x.description.PropertyType == LazinatorPropertyType.PrimitiveTypeNullable) // primitive properties are always first (but will only be redefined if defined abstractly below) .ThenBy(x => x.propertyWithLevel.LevelInfo == PropertyWithDefinitionInfo.Level.IsDefinedThisLevel) .ThenBy(x => x.description.RelativeOrder) .ThenBy(x => x.description.PropertyName).ToList(); var last = orderedPropertiesWithLevel.LastOrDefault(); if (last != null) { last.description.IsLast = true; } // A property that ends with "_Dirty" is designed to track dirtiness of another property. We will thus treat it specially. var dirtyPropertiesWithLevel = propertiesWithLevel.Where(x => x.Property.Name.EndsWith("_Dirty")).ToList(); if (dirtyPropertiesWithLevel.Any(x => (x.Property.Type as INamedTypeSymbol)?.Name != "Boolean")) { throw new LazinatorCodeGenException($"Property ending with _Dirty must be of type bool."); } PropertiesIncludingInherited = new List <PropertyDescription>(); PropertiesToDefineThisLevel = new List <PropertyDescription>(); PropertiesInherited = new List <PropertyDescription>(); foreach (var orderedProperty in orderedPropertiesWithLevel) { if (orderedProperty.propertyWithLevel.LevelInfo == PropertyWithDefinitionInfo.Level.IsDefinedInLowerLevelInterface) { orderedProperty.description.IsDefinedInLowerLevelInterface = true; orderedProperty.description.DerivationKeyword = "override "; } if (!dirtyPropertiesWithLevel.Any(x => x.Property.Name == orderedProperty.propertyWithLevel.Property.Name)) { // this is not itself a "_Dirty" property, though it may have a corresponding _Dirty property. PropertiesIncludingInherited.Add(orderedProperty.description); if (orderedProperty.propertyWithLevel.LevelInfo == PropertyWithDefinitionInfo.Level.IsDefinedThisLevel || ( !Container.IsAbstract // if we have two consecutive abstract classes, we don't want to repeat the abstract properties && !Container.GetBaseLazinatorObjects().Any(x => !x.IsAbstract && x.PropertiesToDefineThisLevel.Any(y => y.PropertyName == orderedProperty.description.PropertyName))) ) { PropertiesToDefineThisLevel.Add(orderedProperty.description); } else { PropertiesInherited.Add(orderedProperty.description); } } } // for each _dirty property, set TrackDirtinessNonSerialized on the corresponding property. foreach (var dirtyWithLevel in dirtyPropertiesWithLevel) { var match = PropertiesIncludingInherited.SingleOrDefault(x => x.PropertyName + "_Dirty" == dirtyWithLevel.Property.Name); if (match == null) { throw new LazinatorCodeGenException( $"Property ending with _Dirty must have a corresponding property without the ending."); } if (!match.IsNonLazinatorType) { throw new LazinatorCodeGenException( $"Property ending with _Dirty must correspond to a nonserialized property without the ending (not to a Lazinator or primitive type)."); } match.TrackDirtinessNonSerialized = true; match = PropertiesToDefineThisLevel.SingleOrDefault(x => x.PropertyName + "_Dirty" == dirtyWithLevel.Property.Name); if (match != null) { match.TrackDirtinessNonSerialized = true; } } }