Added [KnownAsImmutable] assembly attribute
This commit is contained in:
Родитель
d380fdb536
Коммит
eb748cf07e
|
@ -361,3 +361,17 @@ public class MyImmutable
|
||||||
> (`[GeneratedImmutable(GenerateEquality = false)]`)
|
> (`[GeneratedImmutable(GenerateEquality = false)]`)
|
||||||
> won't have any effect in inherited class if the generation is active on the
|
> won't have any effect in inherited class if the generation is active on the
|
||||||
> base class.
|
> base class.
|
||||||
|
|
||||||
|
## I want to reference external classes from my entities.
|
||||||
|
|
||||||
|
I'm getting this error:
|
||||||
|
|
||||||
|
``` csharp
|
||||||
|
#error: 'ImmutableGenerator: Property MyClass.SomeField type ExternalClass.SuperClass is not immutable. It cannot be used in an immutable entity.'
|
||||||
|
```
|
||||||
|
|
||||||
|
To fix this, put this attribute on your assembly:
|
||||||
|
|
||||||
|
``` csharp
|
||||||
|
[assembly: Uno.KnownAsImmutable(typeof(ExternalClass.SuperClass))]
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// ******************************************************************
|
||||||
|
// 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 System.Net.Mail;
|
||||||
|
using Uno;
|
||||||
|
|
||||||
|
[assembly: KnownAsImmutable(typeof(SmtpException))]
|
||||||
|
|
||||||
|
namespace Uno.CodeGen.Tests
|
||||||
|
{
|
||||||
|
partial class Given_ImmutableEntity
|
||||||
|
{
|
||||||
|
internal static readonly MyImmutableWithExternalKnownAsImmutable KnownAsImmutable =
|
||||||
|
MyImmutableWithExternalKnownAsImmutable.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedImmutable]
|
||||||
|
internal partial class MyImmutableWithExternalKnownAsImmutable
|
||||||
|
{
|
||||||
|
private SmtpException Exception { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ namespace Uno.Helpers
|
||||||
private static ImmutableDictionary<(ITypeSymbol, bool), bool> _isImmutable =
|
private static ImmutableDictionary<(ITypeSymbol, bool), bool> _isImmutable =
|
||||||
ImmutableDictionary<(ITypeSymbol, bool), bool>.Empty;
|
ImmutableDictionary<(ITypeSymbol, bool), bool>.Empty;
|
||||||
|
|
||||||
public static bool IsImmutable(this ITypeSymbol type, bool treatArrayAsImmutable)
|
public static bool IsImmutable(this ITypeSymbol type, bool treatArrayAsImmutable, IReadOnlyList<ITypeSymbol> knownAsImmutable)
|
||||||
{
|
{
|
||||||
bool GetIsImmutable((ITypeSymbol type, bool treatArrayAsImmutable) x)
|
bool GetIsImmutable((ITypeSymbol type, bool treatArrayAsImmutable) x)
|
||||||
{
|
{
|
||||||
|
@ -92,7 +92,7 @@ namespace Uno.Helpers
|
||||||
|
|
||||||
if (t is IArrayTypeSymbol arrayType)
|
if (t is IArrayTypeSymbol arrayType)
|
||||||
{
|
{
|
||||||
return arrayType.ElementType?.IsImmutable(asImmutable) ?? false;
|
return arrayType.ElementType?.IsImmutable(asImmutable, knownAsImmutable) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var definitionType = t.GetDefinitionType();
|
var definitionType = t.GetDefinitionType();
|
||||||
|
@ -127,7 +127,7 @@ namespace Uno.Helpers
|
||||||
case "System.Collections.Immutable.ImmutableStack<T>":
|
case "System.Collections.Immutable.ImmutableStack<T>":
|
||||||
{
|
{
|
||||||
var argumentParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
var argumentParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||||
return argumentParameter == null || argumentParameter.IsImmutable(asImmutable);
|
return argumentParameter == null || argumentParameter.IsImmutable(asImmutable, knownAsImmutable);
|
||||||
}
|
}
|
||||||
case "System.Collections.Immutable.IImmutableDictionary<TKey, TValue>":
|
case "System.Collections.Immutable.IImmutableDictionary<TKey, TValue>":
|
||||||
case "System.Collections.Immutable.ImmutableDictionary<TKey, TValue>":
|
case "System.Collections.Immutable.ImmutableDictionary<TKey, TValue>":
|
||||||
|
@ -135,11 +135,16 @@ namespace Uno.Helpers
|
||||||
{
|
{
|
||||||
var keyTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
var keyTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||||
var valueTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.Skip(1).FirstOrDefault();
|
var valueTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.Skip(1).FirstOrDefault();
|
||||||
return (keyTypeParameter == null || keyTypeParameter.IsImmutable(asImmutable))
|
return (keyTypeParameter == null || keyTypeParameter.IsImmutable(asImmutable, knownAsImmutable))
|
||||||
&& (valueTypeParameter == null || valueTypeParameter.IsImmutable(asImmutable));
|
&& (valueTypeParameter == null || valueTypeParameter.IsImmutable(asImmutable, knownAsImmutable));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (knownAsImmutable.Contains(definitionType))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (definitionType.GetType().Name)
|
switch (definitionType.GetType().Name)
|
||||||
{
|
{
|
||||||
case "TupleTypeSymbol":
|
case "TupleTypeSymbol":
|
||||||
|
@ -154,13 +159,13 @@ namespace Uno.Helpers
|
||||||
return asImmutable;
|
return asImmutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
return IsImmutableByRequirements(t, treatArrayAsImmutable);
|
return IsImmutableByRequirements(t, treatArrayAsImmutable, knownAsImmutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImmutableInterlocked.GetOrAdd(ref _isImmutable, (type, treatArrayAsImmutable), GetIsImmutable);
|
return ImmutableInterlocked.GetOrAdd(ref _isImmutable, (type, treatArrayAsImmutable), GetIsImmutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsImmutableByRequirements(ITypeSymbol type, bool treatArrayAsImmutable)
|
private static bool IsImmutableByRequirements(ITypeSymbol type, bool treatArrayAsImmutable, IReadOnlyList<ITypeSymbol> knownAsImmutable)
|
||||||
{
|
{
|
||||||
// Check if type is complying to immutable requirements
|
// Check if type is complying to immutable requirements
|
||||||
// 1) all instance fields are readonly
|
// 1) all instance fields are readonly
|
||||||
|
@ -169,11 +174,11 @@ namespace Uno.Helpers
|
||||||
|
|
||||||
foreach (var member in type.GetMembers())
|
foreach (var member in type.GetMembers())
|
||||||
{
|
{
|
||||||
if (member is IFieldSymbol f && !(f.IsStatic || f.IsReadOnly || f.IsImplicitlyDeclared) && f.Type.IsImmutable(treatArrayAsImmutable))
|
if (member is IFieldSymbol f && !(f.IsStatic || f.IsReadOnly || f.IsImplicitlyDeclared) && f.Type.IsImmutable(treatArrayAsImmutable, knownAsImmutable))
|
||||||
{
|
{
|
||||||
return false; // there's a non-readonly non-static field
|
return false; // there's a non-readonly non-static field
|
||||||
}
|
}
|
||||||
if (member is IPropertySymbol p && !(p.IsStatic || p.IsReadOnly || p.IsImplicitlyDeclared) && p.Type.IsImmutable(treatArrayAsImmutable))
|
if (member is IPropertySymbol p && !(p.IsStatic || p.IsReadOnly || p.IsImplicitlyDeclared) && p.Type.IsImmutable(treatArrayAsImmutable, knownAsImmutable))
|
||||||
{
|
{
|
||||||
return false; // there's a non-readonly non-static property
|
return false; // there's a non-readonly non-static property
|
||||||
}
|
}
|
||||||
|
@ -183,7 +188,7 @@ namespace Uno.Helpers
|
||||||
return type.BaseType == null
|
return type.BaseType == null
|
||||||
|| type.BaseType.SpecialType == SpecialType.System_Object
|
|| type.BaseType.SpecialType == SpecialType.System_Object
|
||||||
|| type.BaseType.SpecialType == SpecialType.System_ValueType
|
|| type.BaseType.SpecialType == SpecialType.System_ValueType
|
||||||
|| type.BaseType.IsImmutable(treatArrayAsImmutable);
|
|| type.BaseType.IsImmutable(treatArrayAsImmutable, knownAsImmutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ITypeSymbol GetDefinitionType(this ITypeSymbol type)
|
public static ITypeSymbol GetDefinitionType(this ITypeSymbol type)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
@ -46,6 +47,7 @@ namespace Uno
|
||||||
private INamedTypeSymbol _immutableBuilderAttributeSymbol;
|
private INamedTypeSymbol _immutableBuilderAttributeSymbol;
|
||||||
private INamedTypeSymbol _immutableAttributeCopyIgnoreAttributeSymbol;
|
private INamedTypeSymbol _immutableAttributeCopyIgnoreAttributeSymbol;
|
||||||
private INamedTypeSymbol _immutableGenerationOptionsAttributeSymbol;
|
private INamedTypeSymbol _immutableGenerationOptionsAttributeSymbol;
|
||||||
|
private INamedTypeSymbol _immutableKnownAsImmutableAttributeSymbol;
|
||||||
|
|
||||||
private (bool generateOptionCode, bool treatArrayAsImmutable, bool generateEqualityByDefault, bool generateJsonNet) _generationOptions;
|
private (bool generateOptionCode, bool treatArrayAsImmutable, bool generateEqualityByDefault, bool generateJsonNet) _generationOptions;
|
||||||
private bool _generateOptionCode = true;
|
private bool _generateOptionCode = true;
|
||||||
|
@ -53,6 +55,8 @@ namespace Uno
|
||||||
|
|
||||||
private Regex[] _copyIgnoreAttributeRegexes;
|
private Regex[] _copyIgnoreAttributeRegexes;
|
||||||
|
|
||||||
|
private IReadOnlyList<ITypeSymbol> _knownAsImmutableTypes;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Execute(SourceGeneratorContext context)
|
public override void Execute(SourceGeneratorContext context)
|
||||||
{
|
{
|
||||||
|
@ -63,6 +67,7 @@ namespace Uno
|
||||||
_immutableBuilderAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableBuilderAttribute");
|
_immutableBuilderAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableBuilderAttribute");
|
||||||
_immutableAttributeCopyIgnoreAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableAttributeCopyIgnoreAttribute");
|
_immutableAttributeCopyIgnoreAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableAttributeCopyIgnoreAttribute");
|
||||||
_immutableGenerationOptionsAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableGenerationOptionsAttribute");
|
_immutableGenerationOptionsAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableGenerationOptionsAttribute");
|
||||||
|
_immutableKnownAsImmutableAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.KnownAsImmutableAttribute");
|
||||||
|
|
||||||
var generationData = EnumerateImmutableGeneratedEntities()
|
var generationData = EnumerateImmutableGeneratedEntities()
|
||||||
.OrderBy(x => x.symbol.Name)
|
.OrderBy(x => x.symbol.Name)
|
||||||
|
@ -70,6 +75,13 @@ namespace Uno
|
||||||
|
|
||||||
var immutableEntitiesToGenerate = generationData.Select(x => x.Item1).ToArray();
|
var immutableEntitiesToGenerate = generationData.Select(x => x.Item1).ToArray();
|
||||||
|
|
||||||
|
if (immutableEntitiesToGenerate.Length == 0)
|
||||||
|
{
|
||||||
|
return; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
_knownAsImmutableTypes = EnumerateKnownAsImmutables();
|
||||||
|
|
||||||
_copyIgnoreAttributeRegexes =
|
_copyIgnoreAttributeRegexes =
|
||||||
ExtractCopyIgnoreAttributes(context.Compilation.Assembly)
|
ExtractCopyIgnoreAttributes(context.Compilation.Assembly)
|
||||||
.Concat(new[] {new Regex(@"^Uno\.Immutable"), new Regex(@"^Uno\.Equality")})
|
.Concat(new[] {new Regex(@"^Uno\.Immutable"), new Regex(@"^Uno\.Equality")})
|
||||||
|
@ -849,7 +861,7 @@ $@"public sealed class {symbolName}BuilderJsonConverterTo{symbolName}{genericArg
|
||||||
builder.AppendLineInvariant(
|
builder.AppendLineInvariant(
|
||||||
$"#error {nameof(ImmutableGenerator)}: {typeSource} type {type} IS A BUILDER! It cannot be used in an immutable entity.");
|
$"#error {nameof(ImmutableGenerator)}: {typeSource} type {type} IS A BUILDER! It cannot be used in an immutable entity.");
|
||||||
}
|
}
|
||||||
else if (!type.IsImmutable(_generationOptions.treatArrayAsImmutable))
|
else if (!type.IsImmutable(_generationOptions.treatArrayAsImmutable, _knownAsImmutableTypes))
|
||||||
{
|
{
|
||||||
if (type is IArrayTypeSymbol)
|
if (type is IArrayTypeSymbol)
|
||||||
{
|
{
|
||||||
|
@ -950,5 +962,22 @@ $@"public sealed class {symbolName}BuilderJsonConverterTo{symbolName}{genericArg
|
||||||
where moduleAttribute != null
|
where moduleAttribute != null
|
||||||
//where (bool) moduleAttribute.ConstructorArguments[0].Value
|
//where (bool) moduleAttribute.ConstructorArguments[0].Value
|
||||||
select (type, moduleAttribute);
|
select (type, moduleAttribute);
|
||||||
|
|
||||||
|
private IReadOnlyList<ITypeSymbol> EnumerateKnownAsImmutables()
|
||||||
|
{
|
||||||
|
var currentModuleAttributes = _context.Compilation.Assembly.GetAttributes();
|
||||||
|
var referencedAssembliesAttributes =
|
||||||
|
_context.Compilation.SourceModule.ReferencedAssemblySymbols.SelectMany(a => a.GetAttributes());
|
||||||
|
|
||||||
|
var knownTypeAttributes = currentModuleAttributes
|
||||||
|
.Concat(referencedAssembliesAttributes)
|
||||||
|
.Where(a => a.AttributeClass.Equals(_immutableKnownAsImmutableAttributeSymbol))
|
||||||
|
.Select(a => a.ConstructorArguments[0].Value)
|
||||||
|
.Cast<ITypeSymbol>()
|
||||||
|
.Distinct()
|
||||||
|
.ToImmutableArray();
|
||||||
|
|
||||||
|
return knownTypeAttributes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace Uno
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Global settings for [GenerateImmutable] generator.
|
/// Global settings for [GenerateImmutable] generator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[System.AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
|
[System.AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
||||||
public sealed class ImmutableGenerationOptionsAttribute : Attribute
|
public sealed class ImmutableGenerationOptionsAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -58,4 +58,4 @@ namespace Uno
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public bool GenerateNewtownsoftJsonNetConverters { get; set; } = true;
|
public bool GenerateNewtownsoftJsonNetConverters { get; set; } = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
// ******************************************************************
|
||||||
|
// 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 System;
|
||||||
|
|
||||||
|
namespace Uno
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Define a type (usually external) as immutable
|
||||||
|
/// </summary>
|
||||||
|
[System.AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||||
|
public sealed class KnownAsImmutableAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type known to be immutable
|
||||||
|
/// </summary>
|
||||||
|
public Type Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// .ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
public KnownAsImmutableAttribute(Type type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче