Fixed problem with inheritance from external assembly

This commit is contained in:
Carl de Billy 2018-02-22 11:31:18 -05:00
Родитель c2e1ced64a
Коммит e8e153880d
23 изменённых файлов: 420 добавлений и 86 удалений

Просмотреть файл

@ -0,0 +1,9 @@
namespace Uno.CodeGen.Tests.ExternalClass
{
[GeneratedImmutable]
public abstract partial class AbstractExternalClass
{
[EqualityHash]
public string Id { get; }
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace Uno.CodeGen.Tests.ExternalClass
{
[GeneratedImmutable]
public abstract partial class AbstractExternalGenericClass<T>
{
[EqualityHash]
public T Id { get; }
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace Uno.CodeGen.Tests.ExternalClass
{
[GeneratedImmutable]
public partial class ConcreteExternalClass
{
[EqualityHash]
public string Id { get; }
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0;netstandard1.6</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<SourceGenerator Include="..\Uno.CodeGen\bin\$(Configuration)\net46\Uno.CodeGen.dll" />
</ItemGroup>
<Import Project="..\Uno.CodeGen\build\Uno.CodeGen.props" />
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.1" />
<PackageReference Include="Uno.SourceGenerationTasks" Version="1.20.0-dev.17" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Uno.Equality\Uno.Equality.csproj" />
<ProjectReference Include="..\Uno.Immutables\Uno.Immutables.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -18,6 +18,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Equality;
namespace Uno.CodeGen.Tests
{

Просмотреть файл

@ -16,6 +16,7 @@
// ******************************************************************
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Equality;
namespace Uno.CodeGen.Tests
{
@ -39,6 +40,7 @@ namespace Uno.CodeGen.Tests
e1.Equals(e2).Should().BeFalse();
e1.KeyEquals(e2).Should().BeTrue();
}
[TestMethod]
public void KeyEquality_WhenUsingEqualityMode()
{
@ -58,6 +60,25 @@ namespace Uno.CodeGen.Tests
e1.KeyEquals(e2).Should().BeFalse();
e1.IdField.KeyEquals(e2.IdField).Should().BeTrue();
}
//[TestMethod]
//public void KeyEquality_WhenUsingKeyEqualityMode_OnAbstractClass()
//{
// var e1 = new KeyEqualityConcreteWrapper.Builder
// {
// IdField = new KeyEqualityId.Builder { Id = "a", Name = "n" },
// NonKeyField = "nkf1"
// }.ToImmutable();
// var e2 = new KeyEqualityWrapper.Builder
// {
// IdField = new KeyEqualityConcreteWrapper.Builder { Id = "a", Name = "n-bis" },
// NonKeyField = "nkf2"
// }.ToImmutable();
// e1.Equals(e2).Should().BeFalse();
// e1.KeyEquals(e2).Should().BeTrue();
//}
}
[GeneratedImmutable]
@ -86,4 +107,20 @@ namespace Uno.CodeGen.Tests
public string Name { get; }
}
[GeneratedImmutable]
public abstract partial class KeyEqualityAbstractWrapper<T>
where T:IKeyEquatable<T>
{
[EqualityKey]
public T IdField { get; }
public string NonKeyField { get; }
}
[GeneratedImmutable]
public abstract partial class KeyEqualityConcreteWrapper : KeyEqualityAbstractWrapper<KeyEqualityId>
{
public string NonKeyField2 { get; }
}
}

Просмотреть файл

@ -16,6 +16,7 @@
// ******************************************************************
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Equality;
namespace Uno.CodeGen.Tests
{

Просмотреть файл

@ -0,0 +1,89 @@
// ******************************************************************
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ******************************************************************
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.CodeGen.Tests.ExternalClass;
using Uno.Equality;
namespace Uno.CodeGen.Tests
{
partial class Given_ImmutableEntity
{
[TestMethod]
public void Immutable_When_Abstracted_Base_Class()
{
// var sut1 = InheritanceDerivedClass.Default.WithKeyValue(null);
}
}
//public interface IImmutable<out TBuilder>
//{
// TBuilder GetBuilder();
//}
[GeneratedImmutable]
public abstract partial class InheritanceAbstractBaseClass<T> // : IImmutable<InheritanceAbstractBaseClass<T>.Builder>
where T : IKeyEquatable<T>
{
[EqualityKey]
public T KeyValue { get; }
//public abstract TBuilder GetBuilder<TBuilder>() where TBuilder : Builder;
//Builder IImmutable<Builder>.GetBuilder()
//{
// throw new System.NotImplementedException();
//}
}
//[GeneratedImmutable]
//public abstract partial class InheritanceAbstractBaseClass2<T> : InheritanceAbstractBaseClass<T>
// where T : IKeyEquatable<T>
//{
// //Builder IImmutable<InheritanceAbstractBaseClass2<T>.Builder>.GetBuilder()
// //{
// // throw new System.NotImplementedException();
// //}
//}
[GeneratedImmutable]
public partial class InheritanceHashedClass
{
[EqualityKey]
public string Id { get; }
}
[GeneratedImmutable]
public partial class InheritanceDerivedClass : InheritanceAbstractBaseClass<InheritanceHashedClass>
{
//public override TBuilder GetBuilder<TBuilder>() where TBuilder : Builder
//{
// throw new System.NotImplementedException();
//}
}
public partial class InheritanceDerivedClassFromExternal : AbstractExternalClass
{
}
public partial class InheritanceDerivedClassFromExternalGeneric : AbstractExternalGenericClass<string>
{
}
}

Просмотреть файл

@ -29,8 +29,13 @@
<ItemGroup>
<ProjectReference Include="..\Uno.ClassLifecycle\Uno.ClassLifecycle.csproj" />
<ProjectReference Include="..\Uno.CodeGen.Tests.ExternalClass\Uno.CodeGen.Tests.ExternalClass.csproj" />
<ProjectReference Include="..\Uno.Equality\Uno.Equality.csproj" />
<ProjectReference Include="..\Uno.Immutables\Uno.Immutables.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

Просмотреть файл

@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.ClassLifecycle", "Uno.C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.CodeGen.ClassLifecycle", "Uno.CodeGen.ClassLifecycle\Uno.CodeGen.ClassLifecycle.csproj", "{3F7D2A75-2FBE-4B2C-AB93-1FBF7EFFE3C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uno.CodeGen.Tests.ExternalClass", "Uno.CodeGen.Tests.ExternalClass\Uno.CodeGen.Tests.ExternalClass.csproj", "{907C30BD-0984-4D75-8A43-107C98235F65}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -56,6 +58,10 @@ Global
{3F7D2A75-2FBE-4B2C-AB93-1FBF7EFFE3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F7D2A75-2FBE-4B2C-AB93-1FBF7EFFE3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F7D2A75-2FBE-4B2C-AB93-1FBF7EFFE3C9}.Release|Any CPU.Build.0 = Release|Any CPU
{907C30BD-0984-4D75-8A43-107C98235F65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{907C30BD-0984-4D75-8A43-107C98235F65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{907C30BD-0984-4D75-8A43-107C98235F65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{907C30BD-0984-4D75-8A43-107C98235F65}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -0,0 +1,38 @@
using System;
using System.Linq;
using System.Text;
using Uno.SourceGeneration;
namespace Uno
{
/// <summary>
/// Simple generator outputing a list of all references used to compile the project.
/// </summary>
/// <remarks>
/// Generates only one file containing only comments.
/// </remarks>
public class CompilationReferencesListingGenerator : SourceGenerator
{
/// <inheritdoc />
public override void Execute(SourceGeneratorContext context)
{
var output = new StringBuilder();
output.AppendLine("// This is the list of all external references used to compile this project:");
var lines = context
.Compilation
.References
.OrderBy(r => r.Display, StringComparer.InvariantCultureIgnoreCase)
.Select((r, i) => $"// #{i}: {r.Display}");
foreach (var line in lines)
{
output.AppendLine(line);
}
var filename = $"{nameof(CompilationReferencesListingGenerator)}.cs";
context.AddCompilationUnit(filename, output.ToString());
}
}
}

Просмотреть файл

@ -22,6 +22,7 @@ using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Uno.Equality;
using Uno.Helpers;
using Uno.RoslynHelpers;
using Uno.SourceGeneration;
@ -95,6 +96,7 @@ namespace Uno
249449, // 22001st prime number
};
/// <inheritdoc />
public override void Execute(SourceGeneratorContext context)
{
_context = context;

Просмотреть файл

@ -15,24 +15,28 @@
//
// ******************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
namespace Uno.Helpers
{
public static class NamedTypeSymbolExtensions
{
public static SymbolNames GetSymbolNames(this INamedTypeSymbol typeSymbol)
public static SymbolNames GetSymbolNames(this INamedTypeSymbol typeSymbol, INamedTypeSymbol typeToUseForSubstitutions = null)
{
var substitutions = typeToUseForSubstitutions.GetSubstitutionTypes();
var symbolName = typeSymbol.Name;
if (typeSymbol.TypeArguments.Length == 0) // not a generic type
{
return new SymbolNames(typeSymbol, symbolName, "", symbolName, symbolName, symbolName, symbolName, "");
}
var argumentNames = typeSymbol.GetTypeArgumentNames();
var argumentNames = typeSymbol.GetTypeArgumentNames(substitutions);
var genericArguments = string.Join(", ", argumentNames);
// symbolNameWithGenerics: MyType<T1, T2>
@ -56,9 +60,43 @@ namespace Uno.Helpers
return new SymbolNames(typeSymbol, symbolName, $"<{genericArguments}>", symbolNameWithGenerics, symbolForXml, symbolNameDefinition, symbolFilename, genericConstraints);
}
public static string[] GetTypeArgumentNames(this ITypeSymbol typeSymbol)
public static string[] GetTypeArgumentNames(
this ITypeSymbol typeSymbol,
(string argumentName, string type)[] substitutions)
{
return (typeSymbol as INamedTypeSymbol)?.TypeArguments.Select(ta => ta.ToString()).ToArray() ?? new string[0];
if (typeSymbol is INamedTypeSymbol namedSymbol)
{
var dict = substitutions.ToDictionary(x => x.argumentName, x => x.type);
return namedSymbol.TypeArguments
.Select(ta => ta.ToString())
.Select(t=> dict.ContainsKey(t) ? dict[t] : t)
.ToArray();
}
return new string[0];
}
public static (string argumentName, string type)[] GetSubstitutionTypes(this INamedTypeSymbol type)
{
if (type == null || type.TypeArguments.Length == 0)
{
return new(string, string)[] { };
}
var argumentParameters = type.TypeParameters;
var argumentTypes = type.TypeArguments;
var result = new (string, string)[type.TypeArguments.Length];
for (var i = 0; i < argumentParameters.Length; i++)
{
var parameterType = argumentTypes[i] as INamedTypeSymbol;
var parameterNames = parameterType.GetSymbolNames();
result[i]= (argumentParameters[i].Name, parameterNames.SymbolNameWithGenerics);
}
return result;
}
}
@ -93,6 +131,24 @@ namespace Uno.Helpers
public string SymbolFilename { get; }
public string GenericConstraints { get; }
public string GetContainingTypeFullName(INamedTypeSymbol typeForSubstitutions = null)
{
if (Symbol.ContainingType != null)
{
return Symbol
.ContainingType
.GetSymbolNames(typeForSubstitutions)
.GetSymbolFullNameWithGenerics();
}
return Symbol.ContainingNamespace.ToString();
}
public string GetSymbolFullNameWithGenerics(INamedTypeSymbol typeForSubstitutions = null)
{
return $"{GetContainingTypeFullName(typeForSubstitutions)}.{SymbolNameWithGenerics}";
}
public void Deconstruct(
out string symbolName,
out string genericArguments,

Просмотреть файл

@ -14,11 +14,17 @@
// limitations under the License.
//
// ******************************************************************
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Uno.Helpers
{
@ -325,5 +331,55 @@ namespace Uno.Helpers
(keyType, valueType, isReadonlyDictionary) = ImmutableInterlocked.GetOrAdd(ref _isDictionary, type, GetIsACollection);
return keyType != null;
}
private static MethodInfo _isAutoPropertyGetter;
public static bool IsAutoProperty(this IPropertySymbol symbol)
{
if (symbol.IsWithEvents || symbol.IsIndexer || !symbol.IsReadOnly)
{
return false;
}
while (!Equals(symbol.OriginalDefinition, symbol))
{
// In some cases we're dealing with a derived type of `WrappedPropertySymbol`.
// This code needs to deal with the SourcePropertySymbol from Roslyn,
// the type containing the `IsAutoProperty` internal member.
symbol = symbol.OriginalDefinition;
}
var type = symbol.GetType();
switch (type.FullName)
{
case "Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol":
return symbol.IsReadOnly; // It's from compiled code. We assume it's an auto-property when "read only"
case "Microsoft.CodeAnalysis.CSharp.Symbols.SourcePropertySymbol":
break; // ok
default:
throw new InvalidOperationException(
"Unable to find the internal property `IsAutoProperty` on implementation of `IPropertySymbol`. " +
"Should be on internal class `PropertySymbol`. Maybe you are using an incompatible version of Roslyn.");
}
if (_isAutoPropertyGetter == null)
{
_isAutoPropertyGetter = type
.GetProperty("IsAutoProperty", BindingFlags.Instance | BindingFlags.NonPublic)
.GetMethod;
}
var isAuto = _isAutoPropertyGetter.Invoke(symbol, new object[] { });
return (bool) isAuto;
}
public static bool IsFromPartialDeclaration(this ISymbol symbol)
{
return symbol
.DeclaringSyntaxReferences
.Select(reference => reference.GetSyntax(CancellationToken.None))
.OfType<ClassDeclarationSyntax>()
.Any(node => node.Modifiers.Any(SyntaxKind.PartialKeyword));
}
}
}

Просмотреть файл

@ -26,6 +26,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Uno.Helpers;
using Uno.RoslynHelpers;
using Uno.SourceGeneration;
using TypeSymbolExtensions = Uno.Helpers.TypeSymbolExtensions;
namespace Uno
{
@ -51,6 +52,7 @@ namespace Uno
private Regex[] _copyIgnoreAttributeRegexes;
/// <inheritdoc />
public override void Execute(SourceGeneratorContext context)
{
_context = context;
@ -141,24 +143,28 @@ namespace Uno
return shouldGenerateEquality ?? _generationOptions.generateEqualityByDefault;
}
private (bool isBaseType, string baseType, string builderBaseType, bool isImmutablePresent) GetTypeInfo(
private (bool isBaseTypePresent, string baseType, string builderBaseType, bool isImmutablePresent) GetTypeInfo(
SourceGeneratorContext context,
INamedTypeSymbol type, INamedTypeSymbol[] immutableEntitiesToGenerate)
{
// Check if [Immutable] is present on the non-generated partial
var isImmutablePresent = type.FindAttributeFlattened(_immutableAttributeSymbol) != null;
var baseType = type.BaseType;
if (baseType == null || baseType.Equals(_systemObject))
{
return (false, null, null, false); // no base type
return (false, null, null, isImmutablePresent); // no base type
}
// Check if [Immutable] is present on the non-generated partial
var isImmutablePresent = baseType.FindAttributeFlattened(_immutableAttributeSymbol) != null;
// Is Builder already compiled ? (in another project/assembly)
var builderAttribute = baseType.FindAttributeFlattened(_immutableBuilderAttributeSymbol);
var builderAttribute = baseType.FindAttribute(_immutableBuilderAttributeSymbol);
if (builderAttribute != null)
{
return (true, null, null, true); // no relevant basetype
var baseTypeBuilder = builderAttribute.ConstructorArguments[0].Value as INamedTypeSymbol;
var baseTypeNames = baseType.GetSymbolNames();
var baseTypeBuilderNames = baseTypeBuilder.GetSymbolNames(baseType);
return (true, baseTypeNames.GetSymbolFullNameWithGenerics(baseType), baseTypeBuilderNames.GetSymbolFullNameWithGenerics(baseType), isImmutablePresent);
}
var baseTypeDefinition = baseType.ConstructedFrom ?? baseType;
@ -175,11 +181,11 @@ namespace Uno
return (true, baseTypeName, builderBaseType, isImmutablePresent);
}
return (true, null, null, false); // no relevant basetype
return (false, null, null, isImmutablePresent); // no relevant basetype
}
private void GenerateImmutable(INamedTypeSymbol typeSymbol,
(bool isBaseType, string baseType, string builderBaseType, bool isImmutablePresent) baseTypeInfo,
(bool isBaseTypePresent, string baseType, string builderBaseType, bool isImmutablePresent) baseTypeInfo,
bool generateEquality)
{
var defaultMemberName = "Default";
@ -193,22 +199,22 @@ namespace Uno
var typeProperties = typeSymbol.GetProperties().ToArray();
if (baseTypeInfo.isBaseType)
if (baseTypeInfo.isBaseTypePresent)
{
var baseProperties = typeSymbol.BaseType.GetProperties()
.Where(x => x.IsReadOnly && IsAutoProperty(x))
.Where(x => x.IsReadOnly && x.IsAutoProperty())
.Select(x => x.Name)
.ToArray();
properties = typeProperties
.Where(x => x.IsReadOnly && IsAutoProperty(x))
.Where(x => x.IsReadOnly && x.IsAutoProperty())
.Select(x => (x, baseProperties.Contains(x.Name))) // remove properties already present in base class
.ToArray();
}
else
{
properties = typeProperties
.Where(x => x.IsReadOnly && IsAutoProperty(x))
.Where(x => x.IsReadOnly && x.IsAutoProperty())
.Select(x => (x, false))
.ToArray();
}
@ -220,14 +226,14 @@ namespace Uno
ValidateType(builder, typeSymbol, baseTypeInfo, symbolNames, typeProperties);
var newModifier = baseTypeInfo.isBaseType ? "new " : "";
var newModifier = baseTypeInfo.isBaseTypePresent ? "new " : "";
builder.AppendLineInvariant("using System;");
builder.AppendLine();
builder.AppendLineInvariant("// <auto-generated>");
builder.AppendLineInvariant("// *****************************************************************************************************************");
builder.AppendLineInvariant("// This has been generated by Uno.CodeGen (ImmutableGenerator), available at https://github.com/nventive/Uno.CodeGen");
builder.AppendLineInvariant("// *****************************************************************************************************************");
builder.AppendLineInvariant("// **********************************************************************************************************************");
builder.AppendLineInvariant("// This file has been generated by Uno.CodeGen (ImmutableGenerator), available at https://github.com/nventive/Uno.CodeGen");
builder.AppendLineInvariant("// **********************************************************************************************************************");
builder.AppendLineInvariant("// </auto-generated>");
builder.AppendLine();
@ -236,11 +242,11 @@ namespace Uno
string builderTypeNameAndBaseClass;
if (typeSymbol.IsAbstract)
{
builderTypeNameAndBaseClass = baseTypeInfo.isBaseType ? $"Builder : {baseTypeInfo.builderBaseType}" : $"Builder";
builderTypeNameAndBaseClass = baseTypeInfo.isBaseTypePresent ? $"Builder : {baseTypeInfo.builderBaseType}" : $"Builder";
}
else
{
builderTypeNameAndBaseClass = baseTypeInfo.isBaseType
builderTypeNameAndBaseClass = baseTypeInfo.isBaseTypePresent
? $"Builder : {baseTypeInfo.builderBaseType}, global::Uno.IImmutableBuilder<{symbolNameWithGenerics}>"
: $"Builder : global::Uno.IImmutableBuilder<{symbolNameWithGenerics}>";
}
@ -277,8 +283,22 @@ namespace Uno
builder.AppendLineInvariant($"/// </summary>");
builder.AppendLineInvariant($"public static readonly {newModifier}{symbolNameWithGenerics} {defaultMemberName} = new {symbolNameWithGenerics}();");
//builder.AppendLine();
//builder.AppendLineInvariant($"/// <summary>");
//builder.AppendLineInvariant($"/// {defaultMemberName} instance with only property initializer set.");
//builder.AppendLineInvariant($"/// </summary>");
//using (builder.BlockInvariant($"internal {newModifier}{symbolNameWithGenerics} {defaultMemberName}({symbolNameWithGenerics} original = default({symbolNameWithGenerics}));"))
//{
//}
builder.AppendLine();
}
else
{
}
var prop1Name = properties.Select(p => p.property.Name).FirstOrDefault() ?? symbolName + "Property";
builder.AppendLineInvariant($"/// <summary>");
@ -301,14 +321,14 @@ namespace Uno
using (builder.BlockInvariant(
$"{typeSymbol.GetAccessibilityAsCSharpCodeString()} {newModifier}partial class {builderTypeNameAndBaseClass}"))
{
if (!baseTypeInfo.isBaseType)
if (!baseTypeInfo.isBaseTypePresent)
{
// _isDirty only on base builder (will be reused in derived builders)
builder.AppendLineInvariant("// Dirty means there's a difference from `_original`.");
builder.AppendLineInvariant("protected bool _isDirty = false;");
builder.AppendLine();
builder.AppendLineInvariant("// This is the original entity, if any (could be null))");
builder.AppendLineInvariant("{0}", $"internal readonly {symbolNameWithGenerics} _original;");
builder.AppendLineInvariant("{0}", $"protected internal readonly {symbolNameWithGenerics} _original;");
builder.AppendLine();
builder.AppendLineInvariant("// Cached version of generated entity (flushed when the builder is updated)");
builder.AppendLineInvariant($"protected {symbolNameWithGenerics} _cachedResult = default({symbolNameWithGenerics});");
@ -448,6 +468,13 @@ return ({symbolNameWithGenerics})(_cachedResult = _original);");
builder.AppendLine();
}
else
{
builder.AppendLineInvariant($"// Since {symbolNameWithGenerics} is abstract, there is no .ToImmutable():");
builder.AppendLineInvariant($"// you should call it on a derived class, which should implement it.");
builder.AppendLine();
}
if (properties.Any())
{
@ -578,7 +605,7 @@ public static implicit operator global::Uno.Option<{symbolNameWithGenerics}>(Bui
builder.AppendLineInvariant("/// </remarks>");
builder.AppendLineInvariant($"/// <param name=\"builder\">The builder for {symbolNameForXml}.</param>");
var baseConstructorChaining = baseTypeInfo.isBaseType
var baseConstructorChaining = baseTypeInfo.isBaseTypePresent
? " : base(builder)"
: "";
@ -753,7 +780,7 @@ $@"public sealed class {symbolName}BuilderJsonConverterTo{symbolName}{genericArg
(bool isBaseType, string baseType, string builderBaseType, bool isImmutablePresent) baseTypeInfo,
SymbolNames symbolNames, IPropertySymbol[] typeProperties)
{
if (!IsFromPartialDeclaration(typeSymbol))
if (!typeSymbol.IsFromPartialDeclaration())
{
builder.AppendLineInvariant(
$"#warning {nameof(ImmutableGenerator)}: you should add the partial modifier to the class {symbolNames.SymbolNameWithGenerics}.");
@ -892,55 +919,11 @@ $@"public sealed class {symbolName}BuilderJsonConverterTo{symbolName}{genericArg
return string.Join("", Enumerate());
}
private static MethodInfo _isAutoPropertyGetter;
private static bool IsAutoProperty(IPropertySymbol symbol)
{
if (symbol.IsWithEvents || symbol.IsIndexer || !symbol.IsReadOnly)
{
return false;
}
while (!Equals(symbol.OriginalDefinition, symbol))
{
// In some cases we're dealing with a derived type of `WrappedPropertySymbol`.
// This code needs to deal with the SourcePropertySymbol from Roslyn,
// the type containing the `IsAutoProperty` internal member.
symbol = symbol.OriginalDefinition;
}
if (_isAutoPropertyGetter == null)
{
var type = symbol.GetType();
var propertyInfo = type.GetProperty("IsAutoProperty", BindingFlags.Instance | BindingFlags.NonPublic);
if (propertyInfo == null)
{
throw new InvalidOperationException(
"Unable to find the internal property `IsAutoProperty` on implementation of `IPropertySymbol`. " +
"Should be on internal class `PropertySymbol`. Maybe you are using an incompatible version of Roslyn.");
}
_isAutoPropertyGetter = propertyInfo?.GetMethod;
}
var isAuto = _isAutoPropertyGetter.Invoke(symbol, new object[] { });
return (bool) isAuto;
}
private IEnumerable<(INamedTypeSymbol symbol, AttributeData attribute)> EnumerateImmutableGeneratedEntities()
=> from type in _context.Compilation.SourceModule.GlobalNamespace.GetNamespaceTypes()
let moduleAttribute = type.FindAttributeFlattened(_generatedImmutableAttributeSymbol)
where moduleAttribute != null
//where (bool) moduleAttribute.ConstructorArguments[0].Value
select (type, moduleAttribute);
private static bool IsFromPartialDeclaration(ISymbol symbol)
{
return symbol
.DeclaringSyntaxReferences
.Select(reference => reference.GetSyntax(CancellationToken.None))
.OfType<ClassDeclarationSyntax>()
.Any(node => node.Modifiers.Any(SyntaxKind.PartialKeyword));
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"profiles": {
"Uno.CodeGen": {
"commandName": "Executable",
"executablePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\MSBuild\\15.0\\Bin\\MSBuild.exe",
"commandLineArgs": "..\\Uno.CodeGen.Tests\\Uno.CodeGen.Tests.csproj /p:Configuration=Debug"
}
}
}

Просмотреть файл

@ -37,7 +37,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Uno.ClassLifecycle.Generator\Uno.CodeGen.ClassLifecycle.csproj" />
<ProjectReference Include="..\Uno.Equality\Uno.Equality.csproj" />
<ProjectReference Include="..\Uno.Immutables\Uno.Immutables.csproj" />
</ItemGroup>

Просмотреть файл

@ -14,7 +14,7 @@
// limitations under the License.
//
// ******************************************************************
namespace Uno
namespace Uno.Equality
{
/// <summary>
/// Use to qualify the collection mode in <see cref="EqualityComparerOptionsAttribute"/> attribute.

Просмотреть файл

@ -16,7 +16,7 @@
// ******************************************************************
using System;
namespace Uno
namespace Uno.Equality
{
/// <summary>
/// Attribute to put on a collection field/property to specify the kind

Просмотреть файл

@ -15,6 +15,7 @@
//
// ******************************************************************
using System;
using Uno.Equality;
namespace Uno
{

Просмотреть файл

@ -14,7 +14,7 @@
// limitations under the License.
//
// ******************************************************************
namespace Uno
namespace Uno.Equality
{
/// <summary>
/// Mode to use for <see cref="EqualityKeyAttribute"/>

Просмотреть файл

@ -16,7 +16,7 @@
// ******************************************************************
using System;
namespace Uno
namespace Uno.Equality
{
/// <summary>
/// Use to qualify the string mode in <see cref="EqualityComparerOptionsAttribute"/> attribute.

Просмотреть файл

@ -12,7 +12,7 @@
<Company>nventive</Company>
<Description>This package provides attributes for Equality source code generation.
This package is part of the Uno.CodeGen to generate equality members in your project.</Description>
<RootNamespace>Uno</RootNamespace>
<RootNamespace>Uno.Equality</RootNamespace>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<Copyright>Copyright (C) 2015-2018 nventive inc. - all rights reserved</Copyright>
<PackageProjectUrl>https://github.com/nventive/Uno.CodeGen</PackageProjectUrl>