From 8bf626977293fd5f7aa3a2d99596e1429c5bdbae Mon Sep 17 00:00:00 2001 From: Beakona Date: Mon, 8 Apr 2024 14:29:28 +0200 Subject: [PATCH] missing parameter attributes for set/add/remove.. --- AutoInterfaceSample/Program.cs | 54 +++++++++++-- AutoInterfaceSampleNetStandard/TestRecord.cs | 53 ++++++++++++- .../CSharpCodeTextWriter.cs | 77 +++++++++++++------ .../IDictionaryExtensions.cs | 15 ++++ 4 files changed, 166 insertions(+), 33 deletions(-) create mode 100644 BeaKona.AutoInterfaceGenerator/IDictionaryExtensions.cs diff --git a/AutoInterfaceSample/Program.cs b/AutoInterfaceSample/Program.cs index cc7137d..26b3f85 100644 --- a/AutoInterfaceSample/Program.cs +++ b/AutoInterfaceSample/Program.cs @@ -1,4 +1,5 @@ -using TestInterfacesNetStandard; +#nullable enable +using System.Diagnostics.CodeAnalysis; namespace AutoInterfaceSample.Test { @@ -10,14 +11,55 @@ public static void Main() } } - partial record TestRecord + public class MyDb : IDb { - //[BeaKona.AutoInterface(IncludeBaseInterfaces = true)] - public ITestable2? Testable { get; set; } + public string ConnectionString { get; [param: AllowNull] set; } = default!; + + public string this[int a, [AllowNull] string b] + { + get => b ?? ""; + [param: AllowNull] + set + { + } + } + } + + partial record TestDb([property: BeaKona.AutoInterface(typeof(IDb), IncludeBaseInterfaces = true)] IDb Inner) //: IDb + { + } + + //partial record TecProgDbConnection + //{ + //[AllowNull] + //[DisallowNull] + //string IDb.ConnectionString + //{ + // get => (this.Inner as System.Data.IDbConnection)!.ConnectionString; + // //[param:MaybeNull] + // set => (this.Inner as System.Data.IDbConnection)!.ConnectionString = value; + //} + //} + + public interface IDb + { + string ConnectionString + { + get; + [param: AllowNull] + set; + } + + string this[int a, [AllowNull] string b] + { + get; + [param: AllowNull] + set; + } } } -namespace System.Diagnostics.CodeAnalysis +/*namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] @@ -25,4 +67,4 @@ internal sealed class NotNullWhenAttribute(bool returnValue) : Attribute { public bool ReturnValue { get; } = returnValue; } -} +}*/ diff --git a/AutoInterfaceSampleNetStandard/TestRecord.cs b/AutoInterfaceSampleNetStandard/TestRecord.cs index 832c83b..a31800e 100644 --- a/AutoInterfaceSampleNetStandard/TestRecord.cs +++ b/AutoInterfaceSampleNetStandard/TestRecord.cs @@ -1,12 +1,49 @@ -using TestInterfacesNetStandard; +using System.Diagnostics.CodeAnalysis; namespace AutoInterfaceSampleNetStandard { - partial class TestRecord + partial class TestDb { - [BeaKona.AutoInterface(IncludeBaseInterfaces = true)] - public ITestable2? Testable { get; set; } + [property: BeaKona.AutoInterface(typeof(IDb), IncludeBaseInterfaces = true)] + public IDb Inner { get; set; } = default!; } + + public class MyDb : IDb + { + public string ConnectionString { get; [param: AllowNull] set; } = default!; + + public string this[int a, [AllowNull] string b] + { + get => b ?? ""; + [param: AllowNull] + set + { + } + } + } + + public interface IDb + { + string ConnectionString + { + get; + [param: AllowNull] + set; + } + + string this[int a, [AllowNull] string b] + { + get; + [param: AllowNull] + set; + } + } + + //partial class TestRecord + //{ + // [BeaKona.AutoInterface(IncludeBaseInterfaces = true)] + // public ITestable2? Testable { get; set; } + //} } namespace System.Diagnostics.CodeAnalysis @@ -22,4 +59,12 @@ public NotNullWhenAttribute(bool returnValue) public bool ReturnValue { get; } } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute + { + public AllowNullAttribute() + { + } + } } diff --git a/BeaKona.AutoInterfaceGenerator/CSharpCodeTextWriter.cs b/BeaKona.AutoInterfaceGenerator/CSharpCodeTextWriter.cs index dafa0c9..9dc8d3e 100644 --- a/BeaKona.AutoInterfaceGenerator/CSharpCodeTextWriter.cs +++ b/BeaKona.AutoInterfaceGenerator/CSharpCodeTextWriter.cs @@ -214,7 +214,7 @@ public void WriteParameterAttributes(SourceBuilder builder, ScopeInfo scope, IPa foreach (var attribute in GetParameterAttributes(parameter)) { - this.WriteAttribute(builder, scope, attribute, false); + this.WriteAttribute(builder, scope, attribute, false, null); any = true; } @@ -288,7 +288,7 @@ private void WriteForwardAttributes(SourceBuilder builder, ScopeInfo scope, ISym foreach (var attribute in this.GetForwardAttributes(member)) { builder.AppendIndentation(); - this.WriteAttribute(builder, scope, attribute, true); + this.WriteAttribute(builder, scope, attribute, true, null); builder.AppendLine(); } } @@ -300,7 +300,20 @@ private void WriteReturnAttributes(SourceBuilder builder, ScopeInfo scope, IEnum foreach (var attribute in attributes) { builder.AppendIndentation(); - this.WriteAttribute(builder, scope, attribute, false, true); + this.WriteAttribute(builder, scope, attribute, false, "return"); + builder.AppendLine(); + } + } + } + + private void WriteParamAttributes(SourceBuilder builder, ScopeInfo scope, IEnumerable attributes) + { + if (attributes != null) + { + foreach (var attribute in attributes) + { + builder.AppendIndentation(); + this.WriteAttribute(builder, scope, attribute, false, "param"); builder.AppendLine(); } } @@ -371,12 +384,13 @@ private void WriteAttributeReference(SourceBuilder builder, ScopeInfo scope, Att } } - private void WriteAttribute(SourceBuilder builder, ScopeInfo scope, AttributeData attribute, bool strict, bool isReturn = false) + private void WriteAttribute(SourceBuilder builder, ScopeInfo scope, AttributeData attribute, bool strict, string? type) { builder.Append('['); - if (isReturn) + if (type != null && string.IsNullOrEmpty(type) == false) { - builder.Append("return: "); + builder.Append(type); + builder.Append(": "); } this.WriteAttributeReference(builder, scope, attribute, strict); builder.Append(']'); @@ -431,29 +445,37 @@ private IEnumerable GetReturnAttributes(IMethodSymbol method) } } - private bool HasAttributes(params ISymbol?[] members) + private IEnumerable GetParamAttributes(IMethodSymbol? method) { - if (members != null) + if (method != null && method.Parameters.Length > 0) { - foreach (var member in members) + foreach (var attribute in method.Parameters.Last().GetAttributes()) { - if (member != null) + if (attribute.AttributeClass is INamedTypeSymbol attributeClass) { - if (this.GetForwardAttributes(member).Any()) - { - return true; - } - - if (member is IMethodSymbol method) + if (this.forwardAttributeNamespaces.Contains(attributeClass.ContainingNamespace.ToDisplayString())) { - if (this.GetReturnAttributes(method).Any()) - { - return true; - } + yield return attribute; } } } } + } + + private bool HasAttributes(IMethodSymbol? method) + { + if (method != null) + { + if (this.GetForwardAttributes(method).Any()) + { + return true; + } + + if (this.GetReturnAttributes(method).Any()) + { + return true; + } + } return false; } @@ -628,6 +650,10 @@ public void WritePropertyDefinition(SourceBuilder builder, IPropertySymbol prope this.WriteTypeReference(builder, @interface, scope); builder.Append('.'); + var setParamAttributes = this.GetParamAttributes(property.SetMethod); + var hasAttributes = setParamAttributes.Any() || this.HasAttributes(property.GetMethod) || this.HasAttributes(property.SetMethod); + var noAttributes = hasAttributes == false; + if (property.IsIndexer) { builder.Append("this["); @@ -639,8 +665,6 @@ public void WritePropertyDefinition(SourceBuilder builder, IPropertySymbol prope this.WriteIdentifier(builder, property); } - bool noAttributes = this.HasAttributes(property.GetMethod, property.SetMethod) == false; - if (property.SetMethod == null && getterTemplate == null && noAttributes) { builder.Append(" => "); @@ -672,6 +696,7 @@ public void WritePropertyDefinition(SourceBuilder builder, IPropertySymbol prope if (property.SetMethod is not null) { this.WriteForwardAttributes(builder, scope, property.SetMethod); + this.WriteParamAttributes(builder, scope, setParamAttributes); builder.AppendIndentation(); builder.Append("set => "); @@ -728,6 +753,7 @@ public void WritePropertyDefinition(SourceBuilder builder, IPropertySymbol prope if (property.SetMethod is not null) { this.WriteForwardAttributes(builder, scope, property.SetMethod); + this.WriteParamAttributes(builder, scope, setParamAttributes); builder.AppendIndentation(); builder.AppendLine("set"); @@ -802,7 +828,10 @@ public void WriteEventDefinition(SourceBuilder builder, IEventSymbol @event, Sco builder.IncrementIndentation(); try { - bool noAttributes = this.HasAttributes(@event.AddMethod, @event.RemoveMethod) == false; + var addParamAttributes = this.GetParamAttributes(@event.AddMethod); + var removeParamAttributes = this.GetParamAttributes(@event.RemoveMethod); + var hasAttributes = addParamAttributes.Any() || removeParamAttributes.Any() || this.HasAttributes(@event.AddMethod) || this.HasAttributes(@event.RemoveMethod); + var noAttributes = hasAttributes == false; if (references.Count() == 1 && adderTemplate == null && removerTemplate == null && noAttributes) { @@ -825,6 +854,7 @@ public void WriteEventDefinition(SourceBuilder builder, IEventSymbol @event, Sco if (@event.AddMethod != null) { this.WriteForwardAttributes(builder, scope, @event.AddMethod); + this.WriteParamAttributes(builder, scope, addParamAttributes); builder.AppendIndentation(); builder.AppendLine("add"); @@ -869,6 +899,7 @@ public void WriteEventDefinition(SourceBuilder builder, IEventSymbol @event, Sco if (@event.RemoveMethod != null) { this.WriteForwardAttributes(builder, scope, @event.RemoveMethod); + this.WriteParamAttributes(builder, scope, removeParamAttributes); builder.AppendIndentation(); builder.AppendLine("remove"); diff --git a/BeaKona.AutoInterfaceGenerator/IDictionaryExtensions.cs b/BeaKona.AutoInterfaceGenerator/IDictionaryExtensions.cs new file mode 100644 index 0000000..d9b3d06 --- /dev/null +++ b/BeaKona.AutoInterfaceGenerator/IDictionaryExtensions.cs @@ -0,0 +1,15 @@ +namespace BeaKona.AutoInterfaceGenerator; + +internal static class IDictionaryExtensions +{ + public static TValue GetOrCreate(this IDictionary @this, TKey key) where TKey : notnull where TValue : new() + { + if (@this.TryGetValue(key, out var value) == false) + { + value = new(); + @this.Add(key, value); + } + + return value; + } +} \ No newline at end of file