Added [KnownAsImmutable] assembly attribute
This commit is contained in:
Родитель
d380fdb536
Коммит
eb748cf07e
|
@ -361,3 +361,17 @@ public class MyImmutable
|
|||
> (`[GeneratedImmutable(GenerateEquality = false)]`)
|
||||
> won't have any effect in inherited class if the generation is active on the
|
||||
> 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 =
|
||||
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)
|
||||
{
|
||||
|
@ -92,7 +92,7 @@ namespace Uno.Helpers
|
|||
|
||||
if (t is IArrayTypeSymbol arrayType)
|
||||
{
|
||||
return arrayType.ElementType?.IsImmutable(asImmutable) ?? false;
|
||||
return arrayType.ElementType?.IsImmutable(asImmutable, knownAsImmutable) ?? false;
|
||||
}
|
||||
|
||||
var definitionType = t.GetDefinitionType();
|
||||
|
@ -127,7 +127,7 @@ namespace Uno.Helpers
|
|||
case "System.Collections.Immutable.ImmutableStack<T>":
|
||||
{
|
||||
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.ImmutableDictionary<TKey, TValue>":
|
||||
|
@ -135,11 +135,16 @@ namespace Uno.Helpers
|
|||
{
|
||||
var keyTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||
var valueTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.Skip(1).FirstOrDefault();
|
||||
return (keyTypeParameter == null || keyTypeParameter.IsImmutable(asImmutable))
|
||||
&& (valueTypeParameter == null || valueTypeParameter.IsImmutable(asImmutable));
|
||||
return (keyTypeParameter == null || keyTypeParameter.IsImmutable(asImmutable, knownAsImmutable))
|
||||
&& (valueTypeParameter == null || valueTypeParameter.IsImmutable(asImmutable, knownAsImmutable));
|
||||
}
|
||||
}
|
||||
|
||||
if (knownAsImmutable.Contains(definitionType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (definitionType.GetType().Name)
|
||||
{
|
||||
case "TupleTypeSymbol":
|
||||
|
@ -154,13 +159,13 @@ namespace Uno.Helpers
|
|||
return asImmutable;
|
||||
}
|
||||
|
||||
return IsImmutableByRequirements(t, treatArrayAsImmutable);
|
||||
return IsImmutableByRequirements(t, treatArrayAsImmutable, knownAsImmutable);
|
||||
}
|
||||
|
||||
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
|
||||
// 1) all instance fields are readonly
|
||||
|
@ -169,11 +174,11 @@ namespace Uno.Helpers
|
|||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -183,7 +188,7 @@ namespace Uno.Helpers
|
|||
return type.BaseType == null
|
||||
|| type.BaseType.SpecialType == SpecialType.System_Object
|
||||
|| type.BaseType.SpecialType == SpecialType.System_ValueType
|
||||
|| type.BaseType.IsImmutable(treatArrayAsImmutable);
|
||||
|| type.BaseType.IsImmutable(treatArrayAsImmutable, knownAsImmutable);
|
||||
}
|
||||
|
||||
public static ITypeSymbol GetDefinitionType(this ITypeSymbol type)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -46,6 +47,7 @@ namespace Uno
|
|||
private INamedTypeSymbol _immutableBuilderAttributeSymbol;
|
||||
private INamedTypeSymbol _immutableAttributeCopyIgnoreAttributeSymbol;
|
||||
private INamedTypeSymbol _immutableGenerationOptionsAttributeSymbol;
|
||||
private INamedTypeSymbol _immutableKnownAsImmutableAttributeSymbol;
|
||||
|
||||
private (bool generateOptionCode, bool treatArrayAsImmutable, bool generateEqualityByDefault, bool generateJsonNet) _generationOptions;
|
||||
private bool _generateOptionCode = true;
|
||||
|
@ -53,6 +55,8 @@ namespace Uno
|
|||
|
||||
private Regex[] _copyIgnoreAttributeRegexes;
|
||||
|
||||
private IReadOnlyList<ITypeSymbol> _knownAsImmutableTypes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Execute(SourceGeneratorContext context)
|
||||
{
|
||||
|
@ -63,6 +67,7 @@ namespace Uno
|
|||
_immutableBuilderAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableBuilderAttribute");
|
||||
_immutableAttributeCopyIgnoreAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableAttributeCopyIgnoreAttribute");
|
||||
_immutableGenerationOptionsAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.ImmutableGenerationOptionsAttribute");
|
||||
_immutableKnownAsImmutableAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.KnownAsImmutableAttribute");
|
||||
|
||||
var generationData = EnumerateImmutableGeneratedEntities()
|
||||
.OrderBy(x => x.symbol.Name)
|
||||
|
@ -70,6 +75,13 @@ namespace Uno
|
|||
|
||||
var immutableEntitiesToGenerate = generationData.Select(x => x.Item1).ToArray();
|
||||
|
||||
if (immutableEntitiesToGenerate.Length == 0)
|
||||
{
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
_knownAsImmutableTypes = EnumerateKnownAsImmutables();
|
||||
|
||||
_copyIgnoreAttributeRegexes =
|
||||
ExtractCopyIgnoreAttributes(context.Compilation.Assembly)
|
||||
.Concat(new[] {new Regex(@"^Uno\.Immutable"), new Regex(@"^Uno\.Equality")})
|
||||
|
@ -849,7 +861,7 @@ $@"public sealed class {symbolName}BuilderJsonConverterTo{symbolName}{genericArg
|
|||
builder.AppendLineInvariant(
|
||||
$"#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)
|
||||
{
|
||||
|
@ -950,5 +962,22 @@ $@"public sealed class {symbolName}BuilderJsonConverterTo{symbolName}{genericArg
|
|||
where moduleAttribute != null
|
||||
//where (bool) moduleAttribute.ConstructorArguments[0].Value
|
||||
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>
|
||||
/// Global settings for [GenerateImmutable] generator.
|
||||
/// </summary>
|
||||
[System.AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
|
||||
[System.AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
||||
public sealed class ImmutableGenerationOptionsAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче