From fc9759ff368ddccfea57453c69033587d31d4555 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Tue, 14 Dec 2021 18:27:15 -0600 Subject: [PATCH] Config generation now uses Nullable annotations --- BSIPA.sln | 5 +- Common.props | 8 + Common.targets | 4 - IPA.Loader/Config/Data/List.cs | 30 ++-- IPA.Loader/Config/Data/Map.cs | 37 ++--- IPA.Loader/Config/Data/Primitives.cs | 90 ++++++++++- IPA.Loader/Config/Data/Value.cs | 34 ++-- IPA.Loader/Config/SelfConfig.cs | 2 +- IPA.Loader/Config/Stores/Attributes.cs | 20 ++- .../Config/Stores/CollectionConverter.cs | 33 ++-- IPA.Loader/Config/Stores/Converters.cs | 149 +++++++++--------- .../Config/Stores/CustomObjectConverter.cs | 31 ++-- .../GeneratedStoreImpl/ConversionDelegates.cs | 9 +- .../Stores/GeneratedStoreImpl/Correction.cs | 3 +- .../GeneratedStoreImpl/Deserialization.cs | 7 +- .../GeneratedStoreImpl/GeneratedStoreImpl.cs | 26 +-- .../GeneratedStoreImpl/IGeneratedStore.cs | 41 ++--- .../Stores/GeneratedStoreImpl/MakeCreator.cs | 9 +- .../GeneratedStoreImpl/ObjectStructure.cs | 35 ++-- .../GeneratedStoreImpl/Serialization.cs | 3 +- .../Stores/GeneratedStorePublicInterface.cs | 3 +- IPA.Loader/Config/Stores/ValueConverter.cs | 15 +- IPA.Loader/Utilities/AlmostVersion.cs | 12 +- IPA/_Attributes.cs | 10 -- 24 files changed, 367 insertions(+), 249 deletions(-) delete mode 100644 IPA/_Attributes.cs diff --git a/BSIPA.sln b/BSIPA.sln index 93a8e3aa..3e5a2bda 100644 --- a/BSIPA.sln +++ b/BSIPA.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28729.10 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.31911.260 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IPA", "IPA\IPA.csproj", "{14092533-98BB-40A4-9AFC-27BB75672A70}" ProjectSection(ProjectDependencies) = postProject @@ -24,7 +24,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\docs.yml = .github\workflows\docs.yml README.md = README.md .github\release_draft.yml = .github\release_draft.yml - System.Diagnostics.CodeAnalysis.cs = System.Diagnostics.CodeAnalysis.cs .github\workflows\tag_docs.yml = .github\workflows\tag_docs.yml EndProjectSection EndProject diff --git a/Common.props b/Common.props index d76b2ec5..f33175a4 100644 --- a/Common.props +++ b/Common.props @@ -14,6 +14,14 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers + \ No newline at end of file diff --git a/Common.targets b/Common.targets index 56750be5..f6c26ae1 100644 --- a/Common.targets +++ b/Common.targets @@ -1,8 +1,4 @@  - - - - \ No newline at end of file diff --git a/IPA.Loader/Config/Data/List.cs b/IPA.Loader/Config/Data/List.cs index d4f48397..764c619b 100644 --- a/IPA.Loader/Config/Data/List.cs +++ b/IPA.Loader/Config/Data/List.cs @@ -1,4 +1,5 @@ -using IPA.Utilities; +#nullable enable +using IPA.Utilities; using System; using System.Collections; using System.Collections.Generic; @@ -10,9 +11,9 @@ namespace IPA.Config.Data /// A list of s for serialization by an . /// Use or to create. /// - public sealed class List : Value, IList + public sealed class List : Value, IList { - private readonly List values = new List(); + private readonly List values = new(); internal List() { } @@ -22,7 +23,7 @@ namespace IPA.Config.Data /// the index to retrieve the at /// the at /// - public Value this[int index] { get => values[index]; set => values[index] = value; } + public Value? this[int index] { get => values[index]; set => values[index] = value; } /// /// Gets the number of elements in the . @@ -30,21 +31,22 @@ namespace IPA.Config.Data /// public int Count => values.Count; - bool ICollection.IsReadOnly => ((IList)values).IsReadOnly; + bool ICollection.IsReadOnly => ((IList)values).IsReadOnly; /// /// Adds a to the end of this . /// /// the to add /// - public void Add(Value item) => values.Add(item); + public void Add(Value? item) => values.Add(item); /// /// Adds a range of s to the end of this . /// /// the range of s to add - public void AddRange(IEnumerable vals) + public void AddRange(IEnumerable vals) { + if (vals is null) throw new ArgumentNullException(nameof(vals)); foreach (var val in vals) Add(val); } @@ -60,7 +62,7 @@ namespace IPA.Config.Data /// the to check for /// if the item was founc, otherwise /// - public bool Contains(Value item) => values.Contains(item); + public bool Contains(Value? item) => values.Contains(item); /// /// Copies the s in the to the in . @@ -68,14 +70,14 @@ namespace IPA.Config.Data /// the to copy to /// the starting index to copy to /// - public void CopyTo(Value[] array, int arrayIndex) => values.CopyTo(array, arrayIndex); + public void CopyTo(Value?[] array, int arrayIndex) => values.CopyTo(array, arrayIndex); /// /// Gets an enumerator to enumerate the . /// /// an for this /// - public IEnumerator GetEnumerator() => ((IList)values).GetEnumerator(); + public IEnumerator GetEnumerator() => ((IList)values).GetEnumerator(); /// /// Gets the index that a given is in the . @@ -83,7 +85,7 @@ namespace IPA.Config.Data /// the to search for /// the index that the was at, or -1. /// - public int IndexOf(Value item) => values.IndexOf(item); + public int IndexOf(Value? item) => values.IndexOf(item); /// /// Inserts a at an index. @@ -91,7 +93,7 @@ namespace IPA.Config.Data /// the index to insert at /// the to insert /// - public void Insert(int index, Value item) => values.Insert(index, item); + public void Insert(int index, Value? item) => values.Insert(index, item); /// /// Removes a from the . @@ -99,7 +101,7 @@ namespace IPA.Config.Data /// the to remove /// if the item was removed, otherwise /// - public bool Remove(Value item) => values.Remove(item); + public bool Remove(Value? item) => values.Remove(item); /// /// Removes a at an index. @@ -115,7 +117,7 @@ namespace IPA.Config.Data public override string ToString() => $"[{string.Join(",",this.Select(v => v?.ToString() ?? "null").StrJP())}]"; - IEnumerator IEnumerable.GetEnumerator() => ((IList)values).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/IPA.Loader/Config/Data/Map.cs b/IPA.Loader/Config/Data/Map.cs index 21be20b4..8ed8f17c 100644 --- a/IPA.Loader/Config/Data/Map.cs +++ b/IPA.Loader/Config/Data/Map.cs @@ -1,4 +1,5 @@ -using IPA.Utilities; +#nullable enable +using IPA.Utilities; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -10,10 +11,10 @@ namespace IPA.Config.Data /// A ordered map of to for serialization by an . /// Use or to create. /// - public sealed class Map : Value, IDictionary + public sealed class Map : Value, IDictionary { - private readonly Dictionary values = new Dictionary(); - private readonly List keyOrder = new List(); + private readonly Dictionary values = new(); + private readonly List keyOrder = new(); internal Map() { } @@ -23,7 +24,7 @@ namespace IPA.Config.Data /// the key to get the value associated with /// the value associated with the /// - public Value this[string key] { get => values[key]; set => values[key] = value; } + public Value? this[string key] { get => values[key]; set => values[key] = value; } /// /// Gets a collection of the keys for the . @@ -39,7 +40,7 @@ namespace IPA.Config.Data /// guarantee that order is maintained. /// /// - public ICollection Values => values.Values; + public ICollection Values => values.Values; /// /// Gets the number of key-value pairs in this . @@ -47,7 +48,7 @@ namespace IPA.Config.Data /// public int Count => values.Count; - bool ICollection>.IsReadOnly => ((IDictionary)values).IsReadOnly; + bool ICollection>.IsReadOnly => ((IDictionary)values).IsReadOnly; /// /// Adds a new with a given key. @@ -55,13 +56,13 @@ namespace IPA.Config.Data /// the key to put the value at /// the to add /// - public void Add(string key, Value value) + public void Add(string key, Value? value) { values.Add(key, value); keyOrder.Add(key); } - void ICollection>.Add(KeyValuePair item) + void ICollection>.Add(KeyValuePair item) => Add(item.Key, item.Value); /// @@ -74,8 +75,8 @@ namespace IPA.Config.Data keyOrder.Clear(); } - bool ICollection>.Contains(KeyValuePair item) - => ((IDictionary)values).Contains(item); + bool ICollection>.Contains(KeyValuePair item) + => ((IDictionary)values).Contains(item); /// /// Checks if the contains a given . @@ -85,18 +86,18 @@ namespace IPA.Config.Data /// public bool ContainsKey(string key) => values.ContainsKey(key); - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - => ((IDictionary)values).CopyTo(array, arrayIndex); + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + => ((IDictionary)values).CopyTo(array, arrayIndex); /// /// Enumerates the 's key-value pairs. /// /// an of key-value pairs in this /// - public IEnumerator> GetEnumerator() + public IEnumerator> GetEnumerator() { foreach (var key in keyOrder) - yield return new KeyValuePair(key, this[key]); + yield return new KeyValuePair(key, this[key]); } /// @@ -107,8 +108,8 @@ namespace IPA.Config.Data /// public bool Remove(string key) => values.Remove(key) && keyOrder.Remove(key); - bool ICollection>.Remove(KeyValuePair item) - => ((IDictionary)values).Remove(item) && (keyOrder.Remove(item.Key) || true); + bool ICollection>.Remove(KeyValuePair item) + => ((IDictionary)values).Remove(item) && (keyOrder.Remove(item.Key) || true); /// /// Gets the value associated with the specified key. @@ -117,7 +118,7 @@ namespace IPA.Config.Data /// the target location of the retrieved object /// if the key was found and set, otherwise /// - public bool TryGetValue(string key, out Value value) => values.TryGetValue(key, out value); + public bool TryGetValue(string key, out Value? value) => values.TryGetValue(key, out value); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/IPA.Loader/Config/Data/Primitives.cs b/IPA.Loader/Config/Data/Primitives.cs index c03b3c90..5abc6d88 100644 --- a/IPA.Loader/Config/Data/Primitives.cs +++ b/IPA.Loader/Config/Data/Primitives.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,10 +13,28 @@ namespace IPA.Config.Data /// public sealed class Text : Value { + /// + /// Constructs an empty object. + /// + [Obsolete("Use the String constructor.")] + public Text() + { + Value = null!; + } + + /// + /// Constructs a object containing the provided value. + /// + /// The value to construct with. + public Text(string value) + { + Value = value; + } + /// /// The actual value of this object. /// - public string Value { get; set; } + public string Value { get; init; } /// /// Converts this into a human-readable format. @@ -30,6 +49,24 @@ namespace IPA.Config.Data /// public sealed class Integer : Value { + /// + /// Constructs an empty object. + /// + [Obsolete("Use the long constructor.")] + public Integer() + { + Value = 0; + } + + /// + /// Constructs a object containing the provided value. + /// + /// The value to construct with. + public Integer(long value) + { + Value = value; + } + /// /// The actual value of the object. /// @@ -50,10 +87,28 @@ namespace IPA.Config.Data /// /// A representing a floating point value. This may hold a - /// 's worth of data. + /// 's worth of data. /// public sealed class FloatingPoint : Value { + /// + /// Constructs an empty object. + /// + [Obsolete("Use the long constructor.")] + public FloatingPoint() + { + Value = 0; + } + + /// + /// Constructs a object containing the provided value. + /// + /// The value to construct with. + public FloatingPoint(decimal value) + { + Value = value; + } + /// /// The actual value fo this object. /// @@ -76,16 +131,37 @@ namespace IPA.Config.Data /// A representing a boolean value. /// public sealed class Boolean : Value - { + { + /// + /// Constructs an empty object. + /// + [Obsolete("Use the long constructor.")] + public Boolean() + { + Value = false; + } + + /// + /// Constructs a object containing the provided value. + /// + /// The value to construct with. + public Boolean(bool value) + { + Value = value; + } + /// /// The actual value fo this object. /// - public bool Value { get; set; } - + public bool Value { get; set; } + + /// /// Converts this into a human-readable format. /// /// the result of Value.ToString().ToLower() - public override string ToString() => Value.ToString().ToLower(); + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", + Justification = "ToLower is the desired display value.")] + public override string ToString() => Value.ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture); } } diff --git a/IPA.Loader/Config/Data/Value.cs b/IPA.Loader/Config/Data/Value.cs index 2e0e4eb7..0d61cebd 100644 --- a/IPA.Loader/Config/Data/Value.cs +++ b/IPA.Loader/Config/Data/Value.cs @@ -1,6 +1,8 @@ -using System; +#nullable enable +using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -25,21 +27,21 @@ namespace IPA.Config.Data /// Creates a Null . /// /// - public static Value Null() => null; + public static Value? Null() => null; /// /// Creates an empty . /// /// an empty /// - public static List List() => new List(); + public static List List() => new(); /// /// Creates an empty . /// /// an empty /// /// - public static Map Map() => new Map(); + public static Map Map() => new(); /// /// Creates a new representing a . @@ -47,14 +49,16 @@ namespace IPA.Config.Data /// the value to wrap /// a wrapping /// - public static Text From(string val) => Text(val); + [return: NotNullIfNotNull("val")] + public static Text? From(string? val) => Text(val); /// /// Creates a new object wrapping a . /// /// the value to wrap /// a wrapping /// - public static Text Text(string val) => val == null ? null : new Text { Value = val }; + [return: NotNullIfNotNull("val")] + public static Text? Text(string? val) => val == null ? null : new(val); /// /// Creates a new wrapping a . @@ -69,7 +73,7 @@ namespace IPA.Config.Data /// the value to wrap /// a wrapping /// - public static Integer Integer(long val) => new Integer { Value = val }; + public static Integer Integer(long val) => new(val); /// /// Creates a new wrapping a . @@ -84,7 +88,7 @@ namespace IPA.Config.Data /// the value to wrap /// a wrapping /// - public static FloatingPoint Float(decimal val) => new FloatingPoint { Value = val }; + public static FloatingPoint Float(decimal val) => new(val); /// /// Creates a new wrapping a . @@ -99,7 +103,7 @@ namespace IPA.Config.Data /// the value to wrap /// a wrapping /// - public static Boolean Bool(bool val) => new Boolean { Value = val }; + public static Boolean Bool(bool val) => new(val); /// /// Creates a new holding the content of an @@ -108,9 +112,10 @@ namespace IPA.Config.Data /// the s to initialize the with /// a containing the content of /// - public static List From(IEnumerable vals) + [return: NotNullIfNotNull("vals")] + public static List? From(IEnumerable? vals) { - if (vals == null) return null; + if (vals is null) return null; var l = List(); l.AddRange(vals); return l; @@ -124,7 +129,7 @@ namespace IPA.Config.Data /// a containing the content of /// /// - public static Map From(IDictionary vals) => From(vals as IEnumerable>); + public static Map From(IDictionary vals) => From(vals as IEnumerable>); /// /// Creates a new holding the content of an @@ -134,9 +139,10 @@ namespace IPA.Config.Data /// a containing the content of /// /// - public static Map From(IEnumerable> vals) + [return: NotNullIfNotNull("vals")] + public static Map? From(IEnumerable>? vals) { - if (vals == null) return null; + if (vals is null) return null; var m = Map(); foreach (var v in vals) m.Add(v.Key, v.Value); return m; diff --git a/IPA.Loader/Config/SelfConfig.cs b/IPA.Loader/Config/SelfConfig.cs index 1bc877b8..bc0d2b04 100644 --- a/IPA.Loader/Config/SelfConfig.cs +++ b/IPA.Loader/Config/SelfConfig.cs @@ -175,7 +175,7 @@ namespace IPA.Config && CommandLineValues.YeetMods; // LINE: ignore - [NonNullable, UseConverter(typeof(CollectionConverter>))] + [NonNullable, UseConverter(typeof(CollectionConverter>))] public virtual HashSet GameAssemblies { get; set; } = new HashSet { // LINE: ignore 5 diff --git a/IPA.Loader/Config/Stores/Attributes.cs b/IPA.Loader/Config/Stores/Attributes.cs index 574dfd80..66c63e16 100644 --- a/IPA.Loader/Config/Stores/Attributes.cs +++ b/IPA.Loader/Config/Stores/Attributes.cs @@ -1,6 +1,8 @@ -using IPA.Config.Stores.Converters; +#nullable enable +using IPA.Config.Stores.Converters; using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace IPA.Config.Stores.Attributes @@ -37,23 +39,25 @@ namespace IPA.Config.Stores.Attributes /// /// Gets whether or not to use the default converter for the member type instead of the specified type. /// + [MemberNotNullWhen(false, nameof(ConverterType))] public bool UseDefaultConverterForType { get; } /// /// Gets the type of the converter to use. /// - public Type ConverterType { get; } + public Type? ConverterType { get; } /// /// Gets the target type of the converter if it is avaliable at instantiation time, otherwise /// . /// - public Type ConverterTargetType { get; } + public Type? ConverterTargetType { get; } /// /// Gets whether or not this converter is a generic . /// - public bool IsGenericConverter => ConverterTargetType != null; + [MemberNotNullWhen(true, nameof(ConverterTargetType))] + public bool IsGenericConverter => ConverterTargetType is not null; /// /// Creates a new specifying to use the default converter type for the target member. @@ -67,11 +71,17 @@ namespace IPA.Config.Stores.Attributes /// the type to assign to public UseConverterAttribute(Type converterType) { + if (converterType is null) + throw new ArgumentNullException(nameof(converterType)); + UseDefaultConverterForType = false; ConverterType = converterType; + if (converterType.IsValueType) + throw new ArgumentException("Type is not a value converter!"); + var baseT = ConverterType.BaseType; - while (baseT != null && baseT != typeof(object) && + while (baseT != typeof(object) && (!baseT.IsGenericType || baseT.GetGenericTypeDefinition() != typeof(ValueConverter<>))) baseT = baseT.BaseType; if (baseT == typeof(object)) ConverterTargetType = null; diff --git a/IPA.Loader/Config/Stores/CollectionConverter.cs b/IPA.Loader/Config/Stores/CollectionConverter.cs index 635ba49c..16093540 100644 --- a/IPA.Loader/Config/Stores/CollectionConverter.cs +++ b/IPA.Loader/Config/Stores/CollectionConverter.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,7 +15,7 @@ namespace IPA.Config.Stores.Converters /// the type of the items in the collection /// the instantiated type of collection public class CollectionConverter : ValueConverter - where TCollection : ICollection + where TCollection : ICollection { /// /// Creates a using the default converter for the @@ -54,7 +55,7 @@ namespace IPA.Config.Stores.Converters /// protected void PopulateFromValue(TCollection col, List list, object parent) { - //Logger.log.Debug($"CollectionConverter<{typeof(T)}, {typeof(TCollection)}>({BaseConverter.GetType()}).PopulateFromValue([object], {list}, {parent.GetType()})"); + if (list is null) throw new ArgumentNullException(nameof(list)); foreach (var it in list) col.Add(BaseConverter.FromValue(it, parent)); } @@ -66,9 +67,9 @@ namespace IPA.Config.Stores.Converters /// the object that will own the resulting /// a new holding the deserialized content of /// - public override TCollection FromValue(Value value, object parent) + public override TCollection FromValue(Value? value, object parent) { - if (!(value is List list)) throw new ArgumentException("Argument not a List", nameof(value)); + if (value is not List list) throw new ArgumentException("Argument not a List", nameof(value)); var col = Create(list.Count, parent); PopulateFromValue(col, list, parent); @@ -81,7 +82,7 @@ namespace IPA.Config.Stores.Converters /// the object owning /// the that was serialized into /// - public override Value ToValue(TCollection obj, object parent) + public override Value? ToValue(TCollection? obj, object parent) => Value.From(obj.Select(t => BaseConverter.ToValue(t, parent))); } /// @@ -92,7 +93,7 @@ namespace IPA.Config.Stores.Converters /// the type of the converter to use for /// public sealed class CollectionConverter : CollectionConverter - where TCollection : ICollection + where TCollection : ICollection where TConverter : ValueConverter, new() { /// @@ -110,7 +111,7 @@ namespace IPA.Config.Stores.Converters /// /// the element type of the /// - public class ISetConverter : CollectionConverter> + public class ISetConverter : CollectionConverter> { /// /// Creates an using the default converter for . @@ -128,8 +129,8 @@ namespace IPA.Config.Stores.Converters /// the size to initialize it to /// the object that will own the new object /// the new - protected override ISet Create(int size, object parent) - => new HashSet(); + protected override ISet Create(int size, object parent) + => new HashSet(); } /// /// An which default constructs a converter for use as the value converter. @@ -155,7 +156,7 @@ namespace IPA.Config.Stores.Converters /// /// the element type of the /// - public class ListConverter : CollectionConverter> + public class ListConverter : CollectionConverter> { /// /// Creates an using the default converter for . @@ -173,8 +174,8 @@ namespace IPA.Config.Stores.Converters /// the size to initialize it to /// the object that will own the new object /// the new - protected override List Create(int size, object parent) - => new List(size); + protected override List Create(int size, object parent) + => new(size); } /// /// A which default constructs a converter for use as the value converter. @@ -199,7 +200,7 @@ namespace IPA.Config.Stores.Converters /// /// the element type of the /// - public class IListConverter : CollectionConverter> + public class IListConverter : CollectionConverter> { /// /// Creates an using the default converter for . @@ -217,8 +218,8 @@ namespace IPA.Config.Stores.Converters /// the size to initialize it to /// the object that will own the new object /// the new - protected override IList Create(int size, object parent) - => new List(size); + protected override IList Create(int size, object parent) + => new List(size); } /// /// An which default constructs a converter for use as the value converter. diff --git a/IPA.Loader/Config/Stores/Converters.cs b/IPA.Loader/Config/Stores/Converters.cs index 7821b90a..12e222f1 100644 --- a/IPA.Loader/Config/Stores/Converters.cs +++ b/IPA.Loader/Config/Stores/Converters.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Config.Stores.Attributes; using IPA.Logging; using System; @@ -22,7 +23,7 @@ namespace IPA.Config.Stores.Converters /// /// the to get the integral value of /// the integral value of , or - public static long? IntValue(Value val) + public static long? IntValue(Value? val) => val is Integer inte ? inte.Value : val is FloatingPoint fp ? fp.AsInteger()?.Value : null; @@ -32,7 +33,7 @@ namespace IPA.Config.Stores.Converters /// /// the to get the floaing point value of /// the floaing point value of , or - public static decimal? FloatValue(Value val) + public static decimal? FloatValue(Value? val) => val is FloatingPoint fp ? fp.Value : val is Integer inte ? inte.AsFloat()?.Value : null; @@ -83,7 +84,7 @@ namespace IPA.Config.Stores.Converters } //Logger.log.Debug($"gives converter for value type {t}"); - var valConv = Activator.CreateInstance(typeof(ValConv<>).MakeGenericType(t)) as IValConv; + var valConv = (IValConv)Activator.CreateInstance(typeof(ValConv<>).MakeGenericType(t)); return valConv.Get(); } @@ -117,7 +118,7 @@ namespace IPA.Config.Stores.Converters IValConv, IValConv, IValConv { - internal static readonly ValConvImpls Impl = new ValConvImpls(); + internal static readonly ValConvImpls Impl = new(); Type IValConv.Get() => typeof(CharConverter); Type IValConv.Get() => typeof(LongConverter); Type IValConv.Get() => typeof(ULongConverter); @@ -145,7 +146,7 @@ namespace IPA.Config.Stores.Converters /// the type of the that this works on public static class Converter { - private static ValueConverter defaultConverter = null; + private static ValueConverter? defaultConverter; /// /// Gets the default for the current type. /// @@ -158,7 +159,7 @@ namespace IPA.Config.Stores.Converters //Logger.log.Debug($"Converter<{t}>.MakeDefault()"); static ValueConverter MakeInstOf(Type ty) - => Activator.CreateInstance(ty) as ValueConverter; + => (ValueConverter)Activator.CreateInstance(ty); return MakeInstOf(Converter.GetDefaultConverterType(t)); } @@ -193,16 +194,16 @@ namespace IPA.Config.Stores.Converters /// the tree to convert /// the object which will own the created object /// the object represented by - public override T? FromValue(Value value, object parent) - => value == null ? null : new T?(baseConverter.FromValue(value, parent)); + public override T? FromValue(Value? value, object parent) + => value is null ? null : new T?(baseConverter.FromValue(value, parent)); /// /// Converts a nullable to a tree. /// /// the value to serialize /// the object which owns /// a tree representing . - public override Value ToValue(T? obj, object parent) - => obj == null ? null : baseConverter.ToValue(obj.Value, parent); + public override Value? ToValue(T? obj, object parent) + => obj is null ? null : baseConverter.ToValue(obj.Value, parent); } /// @@ -237,7 +238,7 @@ namespace IPA.Config.Stores.Converters /// the object which will own the created object /// the deserialized enum value /// if is not a node - public override T FromValue(Value value, object parent) + public override T FromValue(Value? value, object parent) => value is Text t ? (T)Enum.Parse(typeof(T), t.Value) : throw new ArgumentException("Value not a string", nameof(value)); @@ -248,8 +249,8 @@ namespace IPA.Config.Stores.Converters /// the value to serialize /// the object which owns /// a node representing - public override Value ToValue(T obj, object parent) - => Value.Text(obj.ToString()); + public override Value? ToValue(T? obj, object parent) + => Value.Text(obj?.ToString()); } /// @@ -267,7 +268,7 @@ namespace IPA.Config.Stores.Converters /// the object which will own the created object /// the deserialized enum value /// if is not a node - public override T FromValue(Value value, object parent) + public override T FromValue(Value? value, object parent) => value is Text t ? (T)Enum.Parse(typeof(T), t.Value, true) : throw new ArgumentException("Value not a string", nameof(value)); @@ -278,8 +279,8 @@ namespace IPA.Config.Stores.Converters /// the value to serialize /// the object which owns /// a node representing - public override Value ToValue(T obj, object parent) - => Value.Text(obj.ToString()); + public override Value? ToValue(T? obj, object parent) + => Value.Text(obj?.ToString()); } /// @@ -296,7 +297,7 @@ namespace IPA.Config.Stores.Converters /// the object which will own the created object /// the deserialized enum value /// if is not a numeric node - public override T FromValue(Value value, object parent) + public override T FromValue(Value? value, object parent) => (T)Enum.ToObject(typeof(T), Converter.IntValue(value) ?? throw new ArgumentException("Value not a numeric node", nameof(value))); @@ -306,7 +307,7 @@ namespace IPA.Config.Stores.Converters /// the value to serialize /// the object which owns /// an node representing - public override Value ToValue(T obj, object parent) + public override Value ToValue(T? obj, object parent) => Value.Integer(Convert.ToInt64(obj)); } @@ -314,7 +315,7 @@ namespace IPA.Config.Stores.Converters /// A converter for instances of . /// /// the value type of the dictionary - public class IDictionaryConverter : ValueConverter> + public class IDictionaryConverter : ValueConverter> { /// /// Gets the converter for the dictionary's value type. @@ -338,9 +339,9 @@ namespace IPA.Config.Stores.Converters /// the to convert /// the parent that will own the resulting object /// the deserialized dictionary - public override IDictionary FromValue(Value value, object parent) - => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) - ?.ToDictionary(p => p.Key, p => p.val) + public override IDictionary FromValue(Value? value, object parent) + => ((value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) + ?.ToDictionary(p => p.Key, p => p.val)) ?? throw new ArgumentException("Value not a map", nameof(value)); /// @@ -349,8 +350,8 @@ namespace IPA.Config.Stores.Converters /// the dictionary to serialize /// the object that owns the dictionary /// the dictionary serialized as a - public override Value ToValue(IDictionary obj, object parent) - => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); + public override Value? ToValue(IDictionary? obj, object parent) + => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); } /// @@ -373,7 +374,7 @@ namespace IPA.Config.Stores.Converters /// A converter for instances of . /// /// the value type of the dictionary - public class DictionaryConverter : ValueConverter> + public class DictionaryConverter : ValueConverter> { /// /// Gets the converter for the dictionary's value type. @@ -397,7 +398,7 @@ namespace IPA.Config.Stores.Converters /// the to convert /// the parent that will own the resulting object /// the deserialized dictionary - public override Dictionary FromValue(Value value, object parent) + public override Dictionary FromValue(Value? value, object parent) => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) ?.ToDictionary(p => p.Key, p => p.val) ?? throw new ArgumentException("Value not a map", nameof(value)); @@ -408,8 +409,8 @@ namespace IPA.Config.Stores.Converters /// the dictionary to serialize /// the object that owns the dictionary /// the dictionary serialized as a - public override Value ToValue(Dictionary obj, object parent) - => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); + public override Value? ToValue(Dictionary? obj, object parent) + => Value.From(obj?.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); } /// @@ -433,7 +434,7 @@ namespace IPA.Config.Stores.Converters /// A converter for instances of . /// /// the value type of the dictionary - public class IReadOnlyDictionaryConverter : ValueConverter> + public class IReadOnlyDictionaryConverter : ValueConverter> { /// /// Gets the converter for the dictionary's value type. @@ -457,7 +458,7 @@ namespace IPA.Config.Stores.Converters /// the to convert /// the parent that will own the resulting object /// the deserialized dictionary - public override IReadOnlyDictionary FromValue(Value value, object parent) + public override IReadOnlyDictionary FromValue(Value? value, object parent) => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent))) ?.ToDictionary(p => p.Key, p => p.val) ?? throw new ArgumentException("Value not a map", nameof(value)); @@ -468,8 +469,8 @@ namespace IPA.Config.Stores.Converters /// the dictionary to serialize /// the object that owns the dictionary /// the dictionary serialized as a - public override Value ToValue(IReadOnlyDictionary obj, object parent) - => Value.From(obj.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); + public override Value? ToValue(IReadOnlyDictionary? obj, object parent) + => Value.From(obj?.Select(p => new KeyValuePair(p.Key, BaseConverter.ToValue(p.Value, parent)))); } /// @@ -500,7 +501,7 @@ namespace IPA.Config.Stores.Converters /// the object which will own the created object /// the deserialized Color object /// if is not a node or couldn't be parsed into a Color object - public override Color FromValue(Value value, object parent) + public override Color FromValue(Value? value, object parent) { if (value is Text t) { @@ -526,146 +527,146 @@ namespace IPA.Config.Stores.Converters internal class StringConverter : ValueConverter { - public override string FromValue(Value value, object parent) + public override string? FromValue(Value? value, object parent) => (value as Text)?.Value; - public override Value ToValue(string obj, object parent) + public override Value? ToValue(string? obj, object parent) => Value.From(obj); } internal class CharConverter : ValueConverter { - public override char FromValue(Value value, object parent) + public override char FromValue(Value? value, object parent) => (value as Text)?.Value[0] ?? throw new ArgumentException("Value not a text node", nameof(value)); // can throw nullptr - public override Value ToValue(char obj, object parent) + public override Value? ToValue(char obj, object parent) => Value.From(char.ToString(obj)); } internal class LongConverter : ValueConverter { - public override long FromValue(Value value, object parent) + public override long FromValue(Value? value, object parent) => Converter.IntValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value)); - public override Value ToValue(long obj, object parent) + public override Value? ToValue(long obj, object parent) => Value.From(obj); } internal class ULongConverter : ValueConverter { - public override ulong FromValue(Value value, object parent) + public override ulong FromValue(Value? value, object parent) => (ulong)(Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value))); - public override Value ToValue(ulong obj, object parent) + public override Value? ToValue(ulong obj, object parent) => Value.From(obj); } internal class IntPtrConverter : ValueConverter { - public override IntPtr FromValue(Value value, object parent) + public override IntPtr FromValue(Value? value, object parent) => (IntPtr)Converter.Default.FromValue(value, parent); - public override Value ToValue(IntPtr obj, object parent) + public override Value? ToValue(IntPtr obj, object parent) => Value.From((long)obj); } internal class UIntPtrConverter : ValueConverter { - public override UIntPtr FromValue(Value value, object parent) + public override UIntPtr FromValue(Value? value, object parent) => (UIntPtr)Converter.Default.FromValue(value, parent); - public override Value ToValue(UIntPtr obj, object parent) + public override Value? ToValue(UIntPtr obj, object parent) => Value.From((decimal)obj); } internal class IntConverter : ValueConverter { - public override int FromValue(Value value, object parent) + public override int FromValue(Value? value, object parent) => (int)Converter.Default.FromValue(value, parent); - public override Value ToValue(int obj, object parent) + public override Value? ToValue(int obj, object parent) => Value.From(obj); } internal class UIntConverter : ValueConverter { - public override uint FromValue(Value value, object parent) + public override uint FromValue(Value? value, object parent) => (uint)Converter.Default.FromValue(value, parent); - public override Value ToValue(uint obj, object parent) + public override Value? ToValue(uint obj, object parent) => Value.From(obj); } internal class ShortConverter : ValueConverter { - public override short FromValue(Value value, object parent) + public override short FromValue(Value? value, object parent) => (short)Converter.Default.FromValue(value, parent); - public override Value ToValue(short obj, object parent) + public override Value? ToValue(short obj, object parent) => Value.From(obj); } internal class UShortConverter : ValueConverter { - public override ushort FromValue(Value value, object parent) + public override ushort FromValue(Value? value, object parent) => (ushort)Converter.Default.FromValue(value, parent); - public override Value ToValue(ushort obj, object parent) + public override Value? ToValue(ushort obj, object parent) => Value.From(obj); } internal class ByteConverter : ValueConverter { - public override byte FromValue(Value value, object parent) + public override byte FromValue(Value? value, object parent) => (byte)Converter.Default.FromValue(value, parent); - public override Value ToValue(byte obj, object parent) + public override Value? ToValue(byte obj, object parent) => Value.From(obj); } internal class SByteConverter : ValueConverter { - public override sbyte FromValue(Value value, object parent) + public override sbyte FromValue(Value? value, object parent) => (sbyte)Converter.Default.FromValue(value, parent); - public override Value ToValue(sbyte obj, object parent) + public override Value? ToValue(sbyte obj, object parent) => Value.From(obj); } internal class DecimalConverter : ValueConverter { - public override decimal FromValue(Value value, object parent) + public override decimal FromValue(Value? value, object parent) => Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value)); - public override Value ToValue(decimal obj, object parent) + public override Value? ToValue(decimal obj, object parent) => Value.From(obj); } internal class FloatConverter : ValueConverter { - public override float FromValue(Value value, object parent) + public override float FromValue(Value? value, object parent) => (float)Converter.Default.FromValue(value, parent); - public override Value ToValue(float obj, object parent) + public override Value? ToValue(float obj, object parent) => Value.From((decimal)obj); } internal class DoubleConverter : ValueConverter { - public override double FromValue(Value value, object parent) + public override double FromValue(Value? value, object parent) => (double)Converter.Default.FromValue(value, parent); - public override Value ToValue(double obj, object parent) + public override Value? ToValue(double obj, object parent) => Value.From((decimal)obj); } internal class BooleanConverter : ValueConverter { - public override bool FromValue(Value value, object parent) + public override bool FromValue(Value? value, object parent) => (value as Boolean)?.Value ?? throw new ArgumentException("Value not a Boolean", nameof(value)); - public override Value ToValue(bool obj, object parent) + public override Value? ToValue(bool obj, object parent) => Value.From(obj); } internal class DateTimeConverter : ValueConverter { - public override DateTime FromValue(Value value, object parent) + public override DateTime FromValue(Value? value, object parent) { - if (!(value is Text text)) + if (value is not Text text) { throw new ArgumentException("Value is not of type Text", nameof(value)); } @@ -678,14 +679,14 @@ namespace IPA.Config.Stores.Converters throw new ArgumentException($"Parsing failed, {text.Value}"); } - public override Value ToValue(DateTime obj, object parent) => Value.Text(obj.ToString("O")); + public override Value? ToValue(DateTime obj, object parent) => Value.Text(obj.ToString("O")); } internal class DateTimeOffsetConverter : ValueConverter { - public override DateTimeOffset FromValue(Value value, object parent) + public override DateTimeOffset FromValue(Value? value, object parent) { - if (!(value is Text text)) + if (value is not Text text) { throw new ArgumentException("Value is not of type Text", nameof(value)); } @@ -703,9 +704,9 @@ namespace IPA.Config.Stores.Converters internal class TimeSpanConverter : ValueConverter { - public override TimeSpan FromValue(Value value, object parent) + public override TimeSpan FromValue(Value? value, object parent) { - if (!(value is Text text)) + if (value is not Text text) { throw new ArgumentException("Value is not of type Text", nameof(value)); } @@ -718,6 +719,6 @@ namespace IPA.Config.Stores.Converters throw new ArgumentException($"Parsing failed, {text.Value}"); } - public override Value ToValue(TimeSpan obj, object parent) => Value.Text(obj.ToString()); + public override Value? ToValue(TimeSpan obj, object parent) => Value.Text(obj.ToString()); } } diff --git a/IPA.Loader/Config/Stores/CustomObjectConverter.cs b/IPA.Loader/Config/Stores/CustomObjectConverter.cs index be881cc7..10787398 100644 --- a/IPA.Loader/Config/Stores/CustomObjectConverter.cs +++ b/IPA.Loader/Config/Stores/CustomObjectConverter.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using System; namespace IPA.Config.Stores.Converters @@ -12,24 +13,26 @@ namespace IPA.Config.Stores.Converters { private interface IImpl { - T FromValue(Value value, object parent); - Value ToValue(T obj, object parent); + T? FromValue(Value? value, object parent); + Value? ToValue(T? obj, object parent); } private class Impl : IImpl where U : class, GeneratedStoreImpl.IGeneratedStore, T { private static readonly GeneratedStoreImpl.GeneratedStoreCreator creator = GeneratedStoreImpl.GetCreator(typeof(T)); - private static U Create(GeneratedStoreImpl.IGeneratedStore parent) - => creator(parent) as U; + private static U Create(GeneratedStoreImpl.IGeneratedStore? parent) + => (U)creator(parent); - public T FromValue(Value value, object parent) + public T? FromValue(Value? value, object parent) { // lots of casting here, but it works i promise (probably) (parent can be a non-IGeneratedStore, however it won't necessarily behave then) + if (value is null) return null; var obj = Create(parent as GeneratedStoreImpl.IGeneratedStore); obj.Deserialize(value); return obj; } - public Value ToValue(T obj, object parent) + public Value? ToValue(T? obj, object parent) { + if (obj is null) return null; if (obj is GeneratedStoreImpl.IGeneratedStore store) return store.Serialize(); else @@ -51,7 +54,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that will own the deserialized value /// the deserialized value /// - public static T Deserialize(Value value, object parent) + public static T? Deserialize(Value? value, object parent) => impl.FromValue(value, parent); /// @@ -61,7 +64,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that owns /// the tree that represents /// - public static Value Serialize(T obj, object parent) + public static Value? Serialize(T? obj, object parent) => impl.ToValue(obj, parent); /// @@ -71,7 +74,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that will own the deserialized value /// the deserialized value /// - public override T FromValue(Value value, object parent) + public override T? FromValue(Value? value, object parent) => Deserialize(value, parent); /// @@ -81,7 +84,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that owns /// the tree that represents /// - public override Value ToValue(T obj, object parent) + public override Value? ToValue(T? obj, object parent) => Serialize(obj, parent); } @@ -104,7 +107,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that will own the deserialized value /// the deserialized value /// - public static T Deserialize(Value value, object parent) + public static T Deserialize(Value? value, object parent) => deserialize(value, parent); /// @@ -123,7 +126,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that will own the deserialized value /// the deserialized value /// - public override T FromValue(Value value, object parent) + public override T FromValue(Value? value, object parent) => Deserialize(value, parent); /// @@ -133,7 +136,7 @@ namespace IPA.Config.Stores.Converters /// the parent object that owns /// the tree that represents /// - public override Value ToValue(T obj, object parent) + public override Value? ToValue(T obj, object parent) => Serialize(obj); } diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs index 1ad9fd12..dc7f5ea9 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using System; using System.Collections.Generic; using System.Linq; @@ -13,12 +14,12 @@ namespace IPA.Config.Stores { internal delegate Value SerializeObject(T obj); - internal delegate T DeserializeObject(Value val, object parent); + internal delegate T DeserializeObject(Value? val, object parent); private static class DelegateStore { - public static SerializeObject Serialize; - public static DeserializeObject Deserialize; + public static SerializeObject? Serialize; + public static DeserializeObject? Deserialize; } internal static SerializeObject GetSerializerDelegate() diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs index 15d54c97..b59bf7d0 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Config.Stores.Attributes; using IPA.Logging; using System; diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs index 7f0bdffe..ab424717 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Logging; using System; using System.Collections; @@ -43,6 +44,7 @@ namespace IPA.Config.Stores private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, LocalAllocator GetLocal, Action thisarg, Action parentobj) { + if (!member.IsNullable) throw new InvalidOperationException("EmitDeserializeNullable called for non-nullable!"); thisarg ??= il => il.Emit(OpCodes.Ldarg_0); parentobj ??= thisarg; EmitDeserializeValue(il, member, member.NullableWrappedType, expected, GetLocal, thisarg, parentobj); @@ -161,6 +163,9 @@ namespace IPA.Config.Stores private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, Label nextLabel, LocalAllocator GetLocal, Action thisobj, Action parentobj) { + if (!member.HasConverter) + throw new InvalidOperationException("EmitDeserializeConverter called for member without converter"); + using var stlocal = GetLocal.Allocate(typeof(Value)); using var valLocal = GetLocal.Allocate(member.Type); diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs index c069905f..7efa987a 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Config.Stores.Attributes; using IPA.Logging; using System; @@ -36,13 +37,12 @@ namespace IPA.Config.Stores private static readonly MethodInfo CreateGParent = typeof(GeneratedStoreImpl).GetMethod(nameof(Create), BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.Any, new[] { typeof(IGeneratedStore) }, Array.Empty()); - internal static T Create(IGeneratedStore parent) where T : class => (T)Create(typeof(T), parent); + internal static T Create(IGeneratedStore? parent) where T : class => (T)Create(typeof(T), parent); - private static IConfigStore Create(Type type, IGeneratedStore parent) + private static IConfigStore Create(Type type, IGeneratedStore? parent) => GetCreator(type)(parent); - private static readonly SingleCreationValueCache generatedCreators - = new SingleCreationValueCache(); + private static readonly SingleCreationValueCache generatedCreators = new(); private static (GeneratedStoreCreator ctor, Type type) GetCreatorAndGeneratedType(Type t) => generatedCreators.GetOrAdd(t, MakeCreator); @@ -55,7 +55,7 @@ namespace IPA.Config.Stores internal const string GeneratedAssemblyName = "IPA.Config.Generated"; - private static AssemblyBuilder assembly = null; + private static AssemblyBuilder? assembly; private static AssemblyBuilder Assembly { get @@ -75,7 +75,7 @@ namespace IPA.Config.Stores Assembly.Save(file); } - private static ModuleBuilder module = null; + private static ModuleBuilder? module; private static ModuleBuilder Module { get @@ -88,7 +88,7 @@ namespace IPA.Config.Stores } // TODO: does this need to be a SingleCreationValueCache or similar? - private static readonly Dictionary> TypeRequiredConverters = new Dictionary>(); + private static readonly Dictionary> TypeRequiredConverters = new(); private static void CreateAndInitializeConvertersFor(Type type, IEnumerable structure) { if (!TypeRequiredConverters.TryGetValue(type, out var converters)) @@ -96,7 +96,8 @@ namespace IPA.Config.Stores var converterFieldType = Module.DefineType($"{type.FullName}", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.AnsiClass); // a static class - var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray(); + var uniqueConverterTypes = structure.Where(m => m.HasConverter) + .Select(m => m.Converter).NonNull().Distinct().ToArray(); converters = new Dictionary(uniqueConverterTypes.Length); foreach (var convType in uniqueConverterTypes) @@ -122,11 +123,14 @@ namespace IPA.Config.Stores TypeRequiredConverters.Add(type, converters); - converterFieldType.CreateType(); + _ = converterFieldType.CreateType(); } - foreach (var member in structure.Where(m => m.HasConverter)) + foreach (var member in structure) + { + if (!member.HasConverter) continue; member.ConverterField = converters[member.Converter]; + } } } } diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs index 350ef742..dc323833 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs @@ -1,4 +1,5 @@ -using IPA.Logging; +#nullable enable +using IPA.Logging; using System; using System.Collections.Generic; using System.Reflection; @@ -42,27 +43,27 @@ namespace IPA.Config.Stores internal class Impl : IConfigStore { private readonly IGeneratedStore generated; - private long enteredTransactions = 0; + private long enteredTransactions; internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); public Impl(IGeneratedStore store) => generated = store; - private readonly AutoResetEvent resetEvent = new AutoResetEvent(false); + private readonly AutoResetEvent resetEvent = new(false); public WaitHandle SyncObject => resetEvent; - public static WaitHandle ImplGetSyncObject(IGeneratedStore s) => FindImpl(s).SyncObject; + public static WaitHandle? ImplGetSyncObject(IGeneratedStore s) => FindImpl(s)?.SyncObject; internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject)); - public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim(); - public static ReaderWriterLockSlim ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject; + public ReaderWriterLockSlim WriteSyncObject { get; } = new(); + public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject; internal static MethodInfo ImplGetWriteSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetWriteSyncObject)); internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged)); - public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged(); + public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s)?.SignalChanged(); public void SignalChanged() { try { - resetEvent.Set(); + _ = resetEvent.Set(); } catch (ObjectDisposedException e) { @@ -72,11 +73,11 @@ namespace IPA.Config.Stores } internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged)); - public static void ImplInvokeChanged(IGeneratedStore s) => FindImpl(s).InvokeChanged(); + public static void ImplInvokeChanged(IGeneratedStore s) => FindImpl(s)?.InvokeChanged(); public void InvokeChanged() => generated.Changed(); internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead)); - public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead(); + public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s)?.TakeRead(); public void TakeRead() { if (!WriteSyncObject.IsWriteLockHeld) @@ -84,7 +85,7 @@ namespace IPA.Config.Stores } internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead)); - public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead(); + public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s)?.ReleaseRead(); public void ReleaseRead() { if (!WriteSyncObject.IsWriteLockHeld) @@ -92,24 +93,24 @@ namespace IPA.Config.Stores } internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite)); - public static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite(); + public static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s)?.TakeWrite(); public void TakeWrite() => WriteSyncObject.EnterWriteLock(); internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite)); - public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite(); + public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s)?.ReleaseWrite(); public void ReleaseWrite() => WriteSyncObject.ExitWriteLock(); internal static MethodInfo ImplChangeTransactionMethod = typeof(Impl).GetMethod(nameof(ImplChangeTransaction)); - public static IDisposable ImplChangeTransaction(IGeneratedStore s, IDisposable nest) => FindImpl(s).ChangeTransaction(nest); + public static IDisposable? ImplChangeTransaction(IGeneratedStore s, IDisposable nest) => FindImpl(s)?.ChangeTransaction(nest); // TODO: improve trasactionals so they don't always save in every case public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true) => GetFreeTransaction().InitWith(this, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld); - private ChangeTransactionObj GetFreeTransaction() + private static ChangeTransactionObj GetFreeTransaction() => freeTransactionObjs.Count > 0 ? freeTransactionObjs.Pop() : new ChangeTransactionObj(); // TODO: maybe sometimes clean this? - private static readonly Stack freeTransactionObjs = new Stack(); + private static readonly Stack freeTransactionObjs = new(); private sealed class ChangeTransactionObj : IDisposable { @@ -148,7 +149,7 @@ namespace IPA.Config.Stores try { if (data.ownsWrite) - data.impl.ReleaseWrite(); + data.impl?.ReleaseWrite(); } catch { @@ -163,14 +164,14 @@ namespace IPA.Config.Stores ~ChangeTransactionObj() => Dispose(false); } - public static Impl FindImpl(IGeneratedStore store) + public static Impl? FindImpl(IGeneratedStore store) { while (store?.Parent != null) store = store.Parent; // walk to the top of the tree return store?.Impl; } internal static MethodInfo ImplReadFromMethod = typeof(Impl).GetMethod(nameof(ImplReadFrom)); - public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).ReadFrom(provider); + public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s)?.ReadFrom(provider); public void ReadFrom(ConfigProvider provider) { Logger.config.Debug($"Generated impl ReadFrom {generated.GetType()}"); @@ -183,7 +184,7 @@ namespace IPA.Config.Stores } internal static MethodInfo ImplWriteToMethod = typeof(Impl).GetMethod(nameof(ImplWriteTo)); - public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).WriteTo(provider); + public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s)?.WriteTo(provider); public void WriteTo(ConfigProvider provider) { Logger.config.Debug($"Generated impl WriteTo {generated.GetType()}"); diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs index d9248c21..c80ef1ea 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Config.Stores.Attributes; using IPA.Logging; using System; @@ -21,7 +22,7 @@ namespace IPA.Config.Stores internal static partial class GeneratedStoreImpl { - internal delegate IConfigStore GeneratedStoreCreator(IGeneratedStore parent); + internal delegate IConfigStore GeneratedStoreCreator(IGeneratedStore? parent); private static void GetMethodThis(ILGenerator il) => il.Emit(OpCodes.Ldarg_0); private static (GeneratedStoreCreator ctor, Type type) MakeCreator(Type type) @@ -109,7 +110,7 @@ namespace IPA.Config.Stores const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final; #region INotifyPropertyChanged - MethodBuilder notifyChanged = null; + MethodBuilder? notifyChanged = null; if (isINotifyPropertyChanged || hasNotifyAttribute) { // we don't actually want to notify if the base class implements it @@ -639,7 +640,7 @@ namespace IPA.Config.Stores #region Members foreach (var member in structure.Where(m => m.IsVirtual)) { // IsVirtual implies !IsField - var prop = member.Member as PropertyInfo; + var prop = (PropertyInfo)member.Member; var get = prop.GetGetMethod(true); var set = prop.GetSetMethod(true); diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs index 86384804..904ee261 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs @@ -1,10 +1,12 @@ -using IPA.Config.Stores.Attributes; +#nullable enable +using IPA.Config.Stores.Attributes; using IPA.Config.Stores.Converters; using IPA.Logging; using IPA.Utilities; using IPA.Utilities.Async; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text; @@ -20,23 +22,25 @@ namespace IPA.Config.Stores { private class SerializedMemberInfo { - public string Name; - public MemberInfo Member; - public Type Type; + public string Name = null!; + public MemberInfo Member = null!; + public Type Type = null!; public bool AllowNull; public bool IsVirtual; public bool IsField; - public bool IsNullable; // signifies whether this is a Nullable + [MemberNotNullWhen(true, nameof(NullableWrappedType))] + public bool IsNullable { get; set; } // signifies whether this is a Nullable - public bool HasConverter; - public bool IsGenericConverter; // used so we can call directly to the generic version if it is - public Type Converter; - public Type ConverterBase; - public Type ConverterTarget; - public FieldInfo ConverterField; + [MemberNotNullWhen(true, nameof(Converter), nameof(ConverterBase))] + public bool HasConverter { get; set; } + public bool IsGenericConverter { get; set; } // used so we can call directly to the generic version if it is + public Type? Converter; + public Type? ConverterBase; + public Type? ConverterTarget; + public FieldInfo? ConverterField; // invalid for objects with IsNullable false - public Type NullableWrappedType => Nullable.GetUnderlyingType(Type); + public Type? NullableWrappedType => Nullable.GetUnderlyingType(Type); // invalid for objects with IsNullable false public PropertyInfo Nullable_HasValue => Type.GetProperty(nameof(Nullable.HasValue)); // invalid for objects with IsNullable false @@ -73,6 +77,8 @@ namespace IPA.Config.Stores { if (converterAttr.UseDefaultConverterForType) converterAttr = new UseConverterAttribute(Converter.GetDefaultConverterType(member.Type)); + if (converterAttr.UseDefaultConverterForType) + throw new InvalidOperationException("How did we get here?"); member.Converter = converterAttr.ConverterType; member.IsGenericConverter = converterAttr.IsGenericConverter; @@ -100,7 +106,7 @@ namespace IPA.Config.Stores { try { - var conv = Activator.CreateInstance(converterAttr.ConverterType) as IValueConverter; + var conv = (IValueConverter)Activator.CreateInstance(converterAttr.ConverterType); targetType = conv.Type; } catch @@ -128,8 +134,7 @@ namespace IPA.Config.Stores return true; } - private static readonly SingleCreationValueCache objectStructureCache - = new SingleCreationValueCache(); + private static readonly SingleCreationValueCache objectStructureCache = new(); private static IEnumerable ReadObjectMembers(Type type) => objectStructureCache.GetOrAdd(type, t => ReadObjectMembersInternal(type).ToArray()); diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs index cea03460..b1cd6138 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Logging; using System; using System.Collections.Generic; diff --git a/IPA.Loader/Config/Stores/GeneratedStorePublicInterface.cs b/IPA.Loader/Config/Stores/GeneratedStorePublicInterface.cs index 7ece8174..c768c1be 100644 --- a/IPA.Loader/Config/Stores/GeneratedStorePublicInterface.cs +++ b/IPA.Loader/Config/Stores/GeneratedStorePublicInterface.cs @@ -1,4 +1,5 @@ -using IPA.Config.Stores.Attributes; +#nullable enable +using IPA.Config.Stores.Attributes; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/IPA.Loader/Config/Stores/ValueConverter.cs b/IPA.Loader/Config/Stores/ValueConverter.cs index 8be1f300..68cec79e 100644 --- a/IPA.Loader/Config/Stores/ValueConverter.cs +++ b/IPA.Loader/Config/Stores/ValueConverter.cs @@ -1,4 +1,5 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using System; namespace IPA.Config.Stores @@ -31,14 +32,14 @@ namespace IPA.Config.Stores /// the object to convert /// the owning object of /// a representation of as a structure - Value ToValue(object obj, object parent); + Value? ToValue(object? obj, object parent); /// /// Converts the given to the object type handled by this converter. /// /// the to deserialize /// the object that will own the result /// the deserialized object - object FromValue(Value value, object parent); + object? FromValue(Value? value, object parent); /// /// Gets the type that this handles. /// @@ -59,7 +60,7 @@ namespace IPA.Config.Stores /// the owning object of /// a representation of as a structure /// - public abstract Value ToValue(T obj, object parent); + public abstract Value? ToValue(T? obj, object parent); /// /// Converts the given to the object type handled by this converter. /// @@ -67,10 +68,10 @@ namespace IPA.Config.Stores /// the object that will own the result /// the deserialized object /// - public abstract T FromValue(Value value, object parent); + public abstract T? FromValue(Value? value, object parent); - Value IValueConverter.ToValue(object obj, object parent) => ToValue((T)obj, parent); - object IValueConverter.FromValue(Value value, object parent) => FromValue(value, parent); + Value? IValueConverter.ToValue(object? obj, object parent) => ToValue((T?)obj, parent); + object? IValueConverter.FromValue(Value? value, object parent) => FromValue(value, parent); Type IValueConverter.Type => typeof(T); } } diff --git a/IPA.Loader/Utilities/AlmostVersion.cs b/IPA.Loader/Utilities/AlmostVersion.cs index 03ff5736..6b9c8111 100644 --- a/IPA.Loader/Utilities/AlmostVersion.cs +++ b/IPA.Loader/Utilities/AlmostVersion.cs @@ -305,15 +305,19 @@ namespace IPA.Utilities /// the node to convert /// the owner of the new object /// - public override AlmostVersion FromValue(Value value, object parent) - => new(Converter.Default.FromValue(value, parent)); + public override AlmostVersion? FromValue(Value? value, object parent) + => Converter.Default.FromValue(value, parent) switch + { + { } v => new(v), + _ => null + }; /// /// Converts an to a node. /// /// the to convert /// the parent of /// a node representing - public override Value ToValue(AlmostVersion obj, object parent) - => Value.From(obj.ToString()); + public override Value? ToValue(AlmostVersion? obj, object parent) + => Value.From(obj?.ToString()); } } diff --git a/IPA/_Attributes.cs b/IPA/_Attributes.cs deleted file mode 100644 index b56d00af..00000000 --- a/IPA/_Attributes.cs +++ /dev/null @@ -1,10 +0,0 @@ -#if NET472 -namespace System.Diagnostics.CodeAnalysis -{ - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - public sealed class DoesNotReturnAttribute : Attribute - { - public DoesNotReturnAttribute() { } - } -} -#endif \ No newline at end of file