예제 #1
0
        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();
            }
        }
예제 #2
0
        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();
        }
예제 #3
0
        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;
                }
            }
        }